@cemoody/pi-crust-ext-todoist

Interactive, mobile-friendly Todoist widget for pi-crust — the LLM drives your task list via tools while you edit/complete tasks inline in the sidebar.

Packages

Package details

extension

Install @cemoody/pi-crust-ext-todoist from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@cemoody/pi-crust-ext-todoist
Package
@cemoody/pi-crust-ext-todoist
Version
0.1.1
Published
Jun 19, 2026
Downloads
not available
Author
chrisemoodynpm
License
MIT
Types
extension
Size
70.4 KB
Dependencies
0 dependencies · 1 peer
Pi manifest JSON
{
  "extensions": [
    "./pi.mjs"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

@cemoody/pi-crust-ext-todoist

An interactive, mobile-friendly Todoist widget for pi-crust. The LLM drives your task list with tools; you edit, complete, and reschedule tasks inline in the sidebar. Both surfaces operate on the same Todoist account, so changes the agent makes show up immediately in the widget and vice-versa.

Without a TODOIST_API_TOKEN the extension runs against a fully-interactive in-memory mock (create / edit / complete / reopen / delete all work) so you can try it offline. Set the token to talk to your real account.

Three surfaces, one client

This package wires three pi-crust/pi entry points to a single shared Todoist REST client (todoist-client.mjs):

File Manifest key Role
pi.mjs pi.extensions LLM-facing tools — the agent driving surface
server.mjs piCrust.extension Registers the Todoist sidebar activity + /api/ext/todoist/* REST routes
web.mjs piCrust.web The interactive React widget mounted in the sidebar

The widget calls the server routes; the agent tools call Todoist directly. Both go through the same client, so there's no state to keep in sync — Todoist (or the mock store) is the single source of truth.

The UI paradigm

A deliberately focused subset of Todoist rather than a full clone — what you actually want inline in a session:

  • Project chips across the top scope the list (All + each project, with live open-task counts). Horizontally scrollable on phones.
  • A sticky quick-add bar creates tasks into the active project (Inbox by default). Enter or the Add button.
  • Each task row is a large tap target:
    • a round complete checkbox colored by priority (P1 red → P4 grey) with a satisfying strike-and-remove animation,
    • the title is tap-to-edit inline (Enter saves, Esc cancels),
    • a due-date chip (overdue = red, today = green, otherwise a friendly date),
    • project dot (in the All view) and @label pills,
    • row actions to cycle priority and delete (hover-revealed on desktop, always visible on phones).
  • Every mutation is optimistic and reconciles against the server response.

It collapses to a single comfortable column with 44px+ tap targets under 640px, and reuses the host design tokens so it matches the rest of pi-crust.

Agent tools

Tool Description
todoist_list List active tasks (by projectId or a Todoist filter query like today, overdue, @work & p1)
todoist_projects List projects with ids
todoist_add Create a task (content, dueString natural language, priority 1–4, labels, projectId)
todoist_update Edit a task (content, dueString, clearDue, priority, labels, projectId)
todoist_complete Complete (close) a task
todoist_reopen Reopen a completed task
todoist_delete Delete a task

Priority follows the Todoist REST convention: 4 = P1 (urgent)1 = P4.

Inline tool-call cards

Every agent tool call renders a compact Todoist summary inline in the conversation timeline (not just a line of text). The tools attach a details.piRemoteControlArtifact of kind markdown to their result; pi-crust renders it natively in the timeline. We use markdown (not HTML) on purpose: HTML artifacts are wrapped in a bordered figure and a bordered, 320px-min sandboxed iframe, which double-boxes a small task list, leaves dead whitespace, and can't run scripts anyway. Markdown renders organically in a single box with the host's own typography.

When the agent runs todoist_list, todoist_add, todoist_complete, or todoist_update, you see the affected list right in the chat — a just-added task is bolded, completed tasks are struck through, and a status line (Added · Work, Completed · 3 still open) heads the card.

The inline card is a read-only snapshot. pi-crust sandboxes artifact iframes (sandbox="", no scripts), so inline items can't be tapped to mutate. The interactive surface is the sidebar panel.

Editing in the sidebar (interactive)

The sidebar Todoist panel is where you work with tasks directly:

  • Tap the round check → completes the task (with strike animation).
  • Tap the title → inline text editor; Enter saves, Esc cancels.
  • Tap the priority flag → cycles P1→P4 (todoist_update priority).
  • Tap the due chip (or the faint 📅 Due on undated tasks) → a natural-language due editor (“tomorrow 9am”, “next monday”); clear it to remove the date.
  • Quick-add at the top; trash to delete.

All mutations are optimistic and proxied to the /api/ext/todoist/* routes.

Tests

Built test-first (vitest + jsdom + Testing Library):

npm test

Covers the inline-artifact builder (test/artifact.test.mjs — markdown shape, highlight, status, due labels), the agent tools' artifact wiring (test/pi-tools.test.mjs), and the sidebar widget interactions (test/widget.test.mjs — complete, title edit, priority cycle, due-date editing, quick-add).

Connecting your account

Two ways to provide a Todoist personal API token (Todoist → Settings → Integrations → Developer → API token):

  1. In-app (recommended) — open the Todoist panel and use the Connect your Todoist account banner. The token is saved via prc.storage to …/todoist.json and survives restarts. A Disconnect link clears it.
  2. Env var — set TODOIST_API_TOKEN (handy for headless/server/CI).

Token resolution precedence: an in-app (stored) token wins; otherwise the env var; otherwise the interactive in-memory demo store. The connection status endpoint returns only a masked hint (…cdef) — the raw token is never sent to the browser.

Env var Purpose
TODOIST_API_TOKEN Optional fallback token. In-app token takes precedence; when neither is set the in-memory mock is used.

Config routes: GET /api/ext/todoist/config, POST /api/ext/todoist/token {token}, POST /api/ext/todoist/token/delete. Full surface + edge cases: docs/SPEC-token-config.md.

Publishing

Ships an allow-listed tarball (9 files; verify with npm pack --dry-run). prepublishOnly runs the test suite. To publish:

npm version patch        # or minor/major
npm publish --access public
# or: push a vX.Y.Z tag and let .github/workflows/publish.yml do it (needs NPM_TOKEN)

Until published, install from a path or git URL:

PI_CRUST_EXTENSIONS=/path/to/pi-crust-ext-todoist npx pi-crust
# or add the path/git url to packages[] in your pi-crust settings.json

Install

Pi-crust discovers any installed package whose package.json carries a piRemoteControl / piCrust field; pi discovers the pi.extensions entry.

TODOIST_API_TOKEN=xxxx PI_CRUST_EXTENSIONS=/path/to/pi-crust-ext-todoist npx pi-crust

REST surface (used by the widget)

GET  /api/ext/todoist/state?projectId=        -> { projects, tasks, live }
POST /api/ext/todoist/tasks                    { content, projectId? } -> { task }
POST /api/ext/todoist/tasks/:id/update         { content?, priority?, dueString?, ... } -> { task }
POST /api/ext/todoist/tasks/:id/close          -> { ok }
POST /api/ext/todoist/tasks/:id/reopen         -> { ok }
POST /api/ext/todoist/tasks/:id/delete         -> { ok }

Preview

node preview/shoot.mjs renders the widget against an in-memory mock and writes preview/desktop.png + preview/mobile.png (requires playwright-core).

License

MIT.