{"_id":"@aronvaughan/punchlist","name":"@aronvaughan/punchlist","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@aronvaughan/punchlist","version":"1.0.0","publishConfig":{"access":"public"},"description":"Agent-first task manager: a Things-style drag-and-drop web app for humans, the same REST API + ready-made skills for AI agents, with claim/finish/report delegation and a review lane","type":"module","keywords":["tasks","todo","task-manager","agents","ai-agents","delegation","claude","self-hosted","sqlite","hono"],"repository":{"type":"git","url":"git+https://github.com/aronvaughan/punchlist.git"},"engines":{"node":">=26"},"bin":{"punchlist":"bin/punchlist"},"scripts":{"start":"node src/server.js","test":"node scripts/check-coverage.mjs"},"license":"MIT","dependencies":{"@modelcontextprotocol/sdk":"^1.30.0","hono":"^4.13.3"},"_id":"@aronvaughan/punchlist@1.0.0","gitHead":"672f6b1387825d7577d9210c18210172f403de86","bugs":{"url":"https://github.com/aronvaughan/punchlist/issues"},"homepage":"https://github.com/aronvaughan/punchlist#readme","_nodeVersion":"22.23.1","_npmVersion":"10.9.8","dist":{"integrity":"sha512-oSRiyLoGJpouDWzsDzQjFWPtaqnwwubKJx2h8pIKQOlLKku6l5ItJLNjGVe5RNlpF2pqKeagMVujxdiO8xiS3w==","shasum":"5fa71cad6184b764b4a5af23968d64573b318f5d","tarball":"https://registry.npmjs.org/@aronvaughan/punchlist/-/punchlist-1.0.0.tgz","fileCount":1267,"unpackedSize":9574918,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIF6meKk0duAGwZN3pGa4Py0SmoHh/kUmDjrzEhlee0+GAiBR141V/gGwBiUAakT8C2vo28BAyAfXKTiossjIsEUthg=="}]},"_npmUser":{"name":"aronvaughan","email":"aronvaughan@hotmail.com"},"directories":{},"maintainers":[{"name":"aronvaughan","email":"aronvaughan@hotmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/punchlist_1.0.0_1788196601547_0.3258446474219028"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-31T17:16:41.266Z","1.0.0":"2026-08-31T17:16:41.743Z","modified":"2026-08-31T17:16:42.383Z"},"maintainers":[{"name":"aronvaughan","email":"aronvaughan@hotmail.com"}],"description":"Agent-first task manager: a Things-style drag-and-drop web app for humans, the same REST API + ready-made skills for AI agents, with claim/finish/report delegation and a review lane","homepage":"https://github.com/aronvaughan/punchlist#readme","keywords":["tasks","todo","task-manager","agents","ai-agents","delegation","claude","self-hosted","sqlite","hono"],"repository":{"type":"git","url":"git+https://github.com/aronvaughan/punchlist.git"},"bugs":{"url":"https://github.com/aronvaughan/punchlist/issues"},"license":"MIT","readme":"# punchlist\n\n**An agent-first, Things-style task manager for you *and* your AI agents.**\n\nHumans get a fast drag-and-drop web app (Today / Upcoming / Inbox / projects /\ntags, quick-add, recurrence, logbook). Agents get the exact same REST API plus\nready-made skills — so \"add it to my list\", \"what's on my plate\", and \"delegate\nthis to an agent\" all work from chat.\n\nThe delegation loop is the point: assign a task to an agent, the agent\n**claims** it, works it, and **finishes with a written report**; the task lands\nin your **review lane**, where you approve it into the logbook (or mark tasks\nauto-close to skip review). Every task records `created_by` from the auth\ntoken, so you always know who put what on the list.\n\n- Single small Node service, SQLite storage, two small runtime dependencies (Hono + the official MCP SDK).\n- Per-actor bearer tokens; the server refuses to start without them.\n- Ships skills for Claude Code and Hermes, all backed by one canonical\n  `pl.sh` CLI (`skills/shared/pl.sh`).\n\n## Basic walkthrough\n\nThe three screenshots below are one continuous flow against a fresh local\ninstance: capture a task for yourself, delegate one to an agent, and see\nboth land where they belong.\n\n1. **Capture.** Press `n`, type a task with quick-add tokens, hit enter. Here\n   `Draft Q3 roadmap notes #planning ^today` lands in Today, and\n   `Summarize last week's server logs #ops >hermes !friday` is delegated to\n   `hermes` with a deadline — deadlines you delegate still surface in your\n   Today/Due Soon, they just can't clutter Upcoming or Inbox.\n\n   ![Today view with a planned task and a delegated deadline](docs/screenshots/flow-01-today.png)\n\n2. **Delegate.** The delegated task shows up on the **Agents** board under\n   its assignee's backlog — this is the queue an agent's `pl.sh queue` reads\n   from.\n\n   ![Agents board showing the delegated task in backlog](docs/screenshots/flow-02-agents.png)\n\n3. **Edit inline.** Click any row (here, the task you kept for yourself) and\n   it expands in place into title/notes/tags/dates — no drawer, no modal.\n\n   ![Task row expanded into the inline editor](docs/screenshots/flow-03-inline-editor.png)\n\nFrom here the delegated task's lifecycle is: agent claims it off the queue →\nworks it → finishes with a written report → it lands in **Review** for you\nto approve into the logbook. See \"Delegate to an AI agent\" and \"Stay in\ncontrol\" below for that loop in detail.\n\n### Walking through an agent's view\n\nThe three screenshots above are what *you* see. Here's the other side — what\nan agent (`pl.sh`, the shared CLI every skill wraps) sees and does, captured\nagainst the same throwaway instance:\n\n1. **Poll the queue.** Only vetted, open work assigned to this agent shows up.\n\n   ```\n   $ pl.sh queue\n   01M1ADXM92G8KJ6Q5T80S4EKRZ  Summarize last weeks server logs for anomalies  @claude\n   01M1AEAMJTQ5WQYQE73Y2CTG2B  Audit stale DNS records  @claude\n   ```\n\n2. **Claim** a task off the queue — active/backlog → in progress, timestamped.\n\n   ```\n   $ pl.sh claim 01M1AEAMJTQ5WQYQE73Y2CTG2B\n   01M1AEAMJTQ5WQYQE73Y2CTG2B  Audit stale DNS records  [in_progress] @claude\n   ```\n\n   On the Agents board this same task sits in the assignee's **Backlog** lane\n   until claimed:\n\n   ![Agents board backlog](docs/screenshots/agent-view-board.png)\n\n3. **Finish** with a required written report — no silent completions.\n\n   ```\n   $ pl.sh finish 01M1AEAMJTQ5WQYQE73Y2CTG2B \"Checked all A/CNAME records against active services; found 3 stale entries (old-staging.example.com, mail-v1.example.com, cdn-legacy.example.com) pointing at decommissioned hosts and removed them. No other anomalies.\"\n   01M1AEAMJTQ5WQYQE73Y2CTG2B  Audit stale DNS records  [review] @claude\n   ```\n\n   The finished task — with its report — now waits for you in **Review**:\n\n   ![Finished task waiting in Review with its report](docs/screenshots/agent-view-report.png)\n\nIf an agent can't proceed without you, it doesn't guess — see the next two\nsections for the other half of that exchange: the agent blocking with a\nquestion, and you answering it.\n\n## Use cases\n\n### Plan your day\n\nYou open Today and see two kinds of tasks: what you **planned** (tasks you\ngave a *when* date that has arrived) and what has a **deadline** (a *due*\ndate of today or earlier). When is \"I intend to start this\"; due is \"the\nworld expects this\". Deadlines you delegated to an agent still appear here —\na deadline is a deadline no matter whose plate it sits on. Drag to order the\nday; arriving items append after what you've hand-ranked. Due Soon shows\ndeadlines landing inside the next 30 days (the window is adjustable), and\nOverdue collects what lapsed.\n\n![Today view](docs/screenshots/today.png)\n\nCapture is one line — press `n`, type, hit enter. Quick-add parses tokens:\n`#tag`, `@project` (or `@\"multi word\"`), `!due`, `^when` (a date or\n`^someday`), `*recur` (`*daily`, `*weekly:mon,thu`, `*monthly:15`,\n`*every:3`, optionally `+completion`), and `>assignee`. Dates take\n`2026-09-01`, `today`, `tomorrow`, or a weekday name.\n\nAn agent does the same thing with one call:\n\n```bash\npl.sh quickadd \"renew the domain !friday #admin @chores\"\n```\n\n### Run a project like Things\n\nYou open a project and it reads top to bottom as a plan: **TODAY** (when\ndate arrived) → **UPCOMING** (when date in the future) → **ANYTIME** (no\nwhen date, your manual order) → **SOMEDAY** (parked, dimmed at the end).\nDrag a task between sections and its schedule follows; drag within ANYTIME\nto reorder by hand. Click any row and it expands into an inline editor —\ntitle, notes (markdown), tags, dates, recurrence, assignee — no page\nchange, no modal maze.\n\n![Inline editor](docs/screenshots/assign-inline.png)\n\n### Delegate to an AI agent\n\nYou have a task an agent should do. Either click the row and set the\nassignee in the inline editor, or type it that way from the start:\n\n```bash\npl.sh quickadd \"summarize last week's server logs >hermes\"\n```\n\nFrom there the loop runs without you. The agent polls its queue\n(`pl.sh queue` — only vetted, open work assigned to it), **claims** a task\n(active → in progress, timestamped), works it, and **finishes** with a\nrequired written report (`pl.sh finish <id> \"what I did and found\"`). The\nAgents board shows the whole pipeline per agent: what's claimed and since\nwhen, what's waiting in your review with the report inline, and what's\nstill queued.\n\n![Agents board](docs/screenshots/agents-view.png)\n\nAn agent that genuinely can't proceed calls `pl.sh block <id> \"<question>\"`\ninstead of guessing — the task moves to **Human** (`#/needs-input`) with its\nquestion inline, and stays out of the agent's queue until you answer.\nAnswering (`pl.sh answer <id> \"<answer>\"`, or the inline box shown here)\nflips it straight back to active so the agent picks it up on its next sweep:\n\n![Human lane: a blocked task's question, with the answer box being filled in](docs/screenshots/human-answer.png)\n\n### Stay in control\n\nDelegating doesn't mean losing the thread. A finished agent task doesn't\nsilently vanish into the logbook — it lands in **Review** with the agent's\nreport, and you approve it (one click) or reopen it with a note. Only tasks\nyou explicitly mark **auto-close** skip review. Meanwhile your planning\nlanes stay yours: Upcoming and Inbox never show delegated work, but a\ndelegated task's *deadline* still surfaces in your Today and Due Soon — so\ndelegated work can't clutter your day, and it can't go dark either.\n\n![Review lane](docs/screenshots/review.png)\n\nThe final step is yours: each reviewed task shows its report with **Approve**\n(→ logbook) or **Reopen** (→ back to the agent, optionally with a comment) —\nnothing reaches the logbook without this click.\n\n![Final review: a finished task's report with Approve / Reopen](docs/screenshots/review-approve.png)\n\n### Email becomes tasks\n\nYou (or anyone) email the agents' mail account, and a task appears: the\nsubject as a literal title, the body as notes. No quick-add token parsing\nis ever applied to email content — an email can't smuggle itself into a\nproject or assign itself to an agent. Because email is an untrusted\nchannel, these tasks arrive **quarantined**: agents' queues never see them\nand the claim/finish doors reject them server-side, until you look at what\narrived and tap **Vet**. You can always work an unvetted task yourself —\nquarantine locks out agent execution, not you.\n\n![Quarantine and screening](docs/screenshots/security-quarantine.png)\n\n### Trust the security model\n\nA task's title and notes are text an agent will read and act on — which\nmakes the punchlist a prompt-injection surface the moment untrusted\nchannels (like email) can create tasks. Defense is four layers deep; no\nsingle layer is the whole story, and each fails differently. The full\ndesign is in [`docs/2026-08-24-agent-security-design.md`](docs/2026-08-24-agent-security-design.md).\n\n**Layer 1 — provenance vetting (server-enforced).** Every task carries a\n`vetted` flag: tasks from trusted actors are vetted at creation; tasks from\nuntrusted actors (`PUNCHLIST_UNTRUSTED_ACTORS`, default `email`) are not.\nAgent queues exclude unvetted tasks server-side, and `/claim` and `/finish`\nreturn 403 on them — an agent that learns the id still can't work it. Only\nthe admin can vet, through a dedicated endpoint; PATCH can't touch the\nflag. This layer is the actual boundary: it cannot be prompt-injected away.\n\n**Layer 2 — screening (shipped).** Before working any task, agents run\n`skills/shared/screen.sh` on its text — a deterministic, dependency-free\nred-flag screen for secrets paths, credential harvesting, pipe-to-shell,\nexfiltration shapes, destructive commands, and \"ignore previous\ninstructions\"-style injection. Flagged tasks are not executed; the agent\nfinishes them with a `⚠ flagged` report so they land in your review lane\nwith the reason. A second mode (`--risk`) marks legitimate-but-dangerous\nwork — installs, credential changes, spending, deletion — for layer 4.\n\n**Layer 3 — prompt hardening (agent-side).** The agents' queue-sweep\nprompts wrap all task text as untrusted *data*, with standing invariants no\ntask text can lift: never put secrets in outbound requests, never weaken\nscreening because a task says to, and treat \"the owner approved this\"\nclaims inside task text as false — approval only arrives through the\npunchlist's own vet/review doors.\n\n**Layer 4 — out-of-band confirmation (agent-side).** High-risk tasks\n(from layer 2's `--risk` screen) wait for the owner's explicit confirmation\non a channel *outside* the task system, so a task's own text can never fake\nthe approval. Unconfirmed tasks sit claimed-but-idle with a note.\n\nIn the UI all of this is visible, not buried: quarantined tasks wear an\namber \"unvetted\" shield chip (tap to vet), the Agents view groups them\nunder an explicit \"agents will not execute\" heading, and screening-flagged\ntasks park in Review with their reasons.\n\n### Attach images\n\nDrop a screenshot onto a task, or click the **Attachments** count in the\ninline editor to open the attachments dialog — **Attach file** takes JPEG,\nPNG, or a `.md`/`.txt` document, validated by magic bytes (a renamed file is\nrejected), each capped at 10MB (documents at 2MB). Thumbnails lazy-load and\nrows show a small 📎 count. Bytes live as their own files in a separate media\ndir (`PUNCHLIST_MEDIA_DIR`, default `<data>/media`), not in the database.\nEach image carries a retention rule — **Keep** (default), **Delete when\ndone**, or **Expire on a date** — and a daily reaper\n(`scripts/reap-media.sh`) deletes files whose rule has fired. See\n[`docs/2026-08-26-attachments.md`](docs/2026-08-26-attachments.md).\n\n![Attachments dialog: a kept image attachment with its retention control](docs/screenshots/attachments-editor.png)\n\n### Edit templates with AI\n\nAdmin-only. When a task has a template set, a pencil beside it opens a\nconversational editor that revises the reusable template definition with\nthe local `claude` CLI. The feature stays hidden unless the `claude` binary\nis on `PATH` and `PUNCHLIST_TEMPLATES_DIR` points at your templates repo\n(e.g. `/srv/punchlist-templates`). You describe changes in plain language,\nwatch the draft re-render, and iterate; the spawned Claude is text-only and\ntouches nothing on disk. **Save** validates the draft with `plt` and, only\nif it passes, writes the override to the templates repo's `authored/`\ndirectory and **commits** it locally — never pushes. See\n[`docs/2026-08-28-template-editor-design.md`](docs/2026-08-28-template-editor-design.md).\n\nOpen it from a task's **Template** field → the pencil beside the chosen\ntemplate. The dialog shows the live-rendered draft beside the conversation\nthread, with **Save draft** / **Revert to saved** underneath:\n\n![Template editor: live preview of a template's markdown beside the AI conversation thread](docs/screenshots/template-editor.png)\n\n### Make it yours\n\nThirty themes (plus \"System\", which follows the OS) — 15 light and 15 dark\nfamilies, grouped and previewed in the picker (`public/tokens.css`,\n`THEME_GROUPS` in `public/app.js`). Keyboard throughout: `n` focuses\nquick-add, `Shift+N` opens the full new-task form, `/` jumps to search,\n`Esc` backs out. The same page works on a phone: the rail collapses to\ntabs and drag-and-drop still works by touch.\n\n![Theme picker](docs/screenshots/theme-picker-full.png)\n\nSee the full [theme gallery](#theme-gallery) below for a screenshot of\nevery theme.\n\n## Quickstart\n\n```bash\ngit clone https://github.com/aronvaughan/punchlist && cd punchlist\n./install.sh                    # npm ci, mints per-actor tokens, starts the service\n# open http://127.0.0.1:8600    # the web app (paste your token once)\n./bin/punchlist install-skills  # copy the agent skills into ~/.claude and $HERMES_HOME\n```\n\n`./install.sh --actors \"you,claude,hermes\"` controls which actors get tokens —\nthe **first** actor is the admin: the human who approves reviews and owns the\nToday/Inbox lanes (`PUNCHLIST_ADMIN`). Tokens live in `data/.env` (chmod 600,\nnever in git); re-running install keeps them.\n\nOther commands: `./bin/punchlist serve` (foreground server),\n`./bin/punchlist snapshot` (WAL-safe backup to `data/backup/`).\n`npm test` runs the suite with an 80% coverage floor.\n\n## MCP\n\nPunchlist also ships as an MCP stdio server (`punchlist mcp`), so **any MCP\nagent** — Claude Code, Cursor, Hermes, or your own — gets the punchlist as\nnative tools: `punchlist_add`, `punchlist_quickadd`, `punchlist_list`,\n`punchlist_show`, `punchlist_queue`, `punchlist_claim`, `punchlist_finish`,\n`punchlist_complete`, `punchlist_approve`, `punchlist_update`,\n`punchlist_projects`, `punchlist_counts`.\n\n```bash\npunchlist install -t claude     # runs `claude mcp add punchlist --scope user -- punchlist mcp`\n                                # (prints the .mcp.json snippet if the claude CLI is missing)\npunchlist install -t hermes     # prints the config.yaml snippet — nothing is edited for you\npunchlist install --print-config  # just show both snippets\n```\n\nFor Hermes, add to the `mcp_servers` block of `$HERMES_HOME/config.yaml`:\n\n```yaml\nmcp_servers:\n  punchlist:\n    command: punchlist\n    args: [mcp]\n```\n\nAuth is identical to the skills: `PUNCHLIST_TOKEN` in the agent's environment,\nor `PUNCHLIST_ENV_FILE`, or `~/.claude/secrets.local.env`, or\n`$HERMES_HOME/.env`; set `PUNCHLIST_URL` for a non-default server (default\n`http://127.0.0.1:8600`). Skills vs MCP: the skills are zero-protocol simple\n(a bash CLI any agent with a shell can run), while MCP surfaces the same API\nas native tools in every MCP-speaking client — pick per agent, they coexist.\n\n## Deployment posture\n\n- **Loopback by default.** The server binds `127.0.0.1:8600`\n  (`PUNCHLIST_HOST`/`PUNCHLIST_PORT` override). There is no TLS and no rate\n  limiting — it is designed to sit behind loopback or a private network.\n- **Expose over a tailnet/VPN, never publicly.** Tailscale\n  (`tailscale serve --tcp 8600`), WireGuard, or an SSH tunnel are the intended\n  remote paths. Do **not** put it on the open internet (no public funnel /\n  port-forward / reverse proxy without auth in front).\n- **Fail-closed tokens.** Startup refuses without well-formed\n  `PUNCHLIST_TOKENS` (min 32 chars per token); every API request needs a\n  bearer token; the server warns if `data/.env` is group/other-readable.\n  Note the plural/singular split: **`PUNCHLIST_TOKENS`** (server-side,\n  `data/.env`) is the full `name:token,name:token` roster, while\n  **`PUNCHLIST_TOKEN`** (client-side, each agent's own environment) is that\n  one agent's single token from the roster.\n- The API is the only write path; view SQL is parameter-bound only, and the\n  UI is served with a strict CSP.\n\n## Docs\n\nDesign records live in [`docs/`](docs/) — product analysis, PRD,\narchitecture, module design (the API contract), the delegation design, the\nagent security design, and the image-attachments design.\n\n## Theme gallery\n\nEvery theme, captured from the Today view. Pick one from the theme picker\n(nav rail → **Theme**) or leave it on **System** to follow the OS.\n\n### Light\n\n<table>\n  <tr>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-light.png\" width=\"260\"><br><sub>Light</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-paper.png\" width=\"260\"><br><sub>Paper</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-slate.png\" width=\"260\"><br><sub>Slate</sub></td>\n  </tr>\n  <tr>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-rose.png\" width=\"260\"><br><sub>Rose</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-solar.png\" width=\"260\"><br><sub>Solar</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-mint.png\" width=\"260\"><br><sub>Mint</sub></td>\n  </tr>\n  <tr>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-lilac.png\" width=\"260\"><br><sub>Lilac</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-latte.png\" width=\"260\"><br><sub>Latte</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-azure.png\" width=\"260\"><br><sub>Azure</sub></td>\n  </tr>\n  <tr>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-glass-light.png\" width=\"260\"><br><sub>Glass Light</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-conifer.png\" width=\"260\"><br><sub>Conifer</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-clay.png\" width=\"260\"><br><sub>Clay</sub></td>\n  </tr>\n  <tr>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-coral.png\" width=\"260\"><br><sub>Coral</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-mustard.png\" width=\"260\"><br><sub>Mustard</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-fog.png\" width=\"260\"><br><sub>Fog</sub></td>\n  </tr>\n</table>\n\n### Dark\n\n<table>\n  <tr>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-dark.png\" width=\"260\"><br><sub>Dark</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-spruce.png\" width=\"260\"><br><sub>Spruce</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-midnight.png\" width=\"260\"><br><sub>Midnight</sub></td>\n  </tr>\n  <tr>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-ember.png\" width=\"260\"><br><sub>Ember</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-nord.png\" width=\"260\"><br><sub>Nord</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-grape.png\" width=\"260\"><br><sub>Grape</sub></td>\n  </tr>\n  <tr>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-ocean.png\" width=\"260\"><br><sub>Ocean</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-terminal.png\" width=\"260\"><br><sub>Terminal</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-cobalt.png\" width=\"260\"><br><sub>Cobalt</sub></td>\n  </tr>\n  <tr>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-glass-dark.png\" width=\"260\"><br><sub>Glass Dark</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-synthwave.png\" width=\"260\"><br><sub>Synthwave</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-maroon.png\" width=\"260\"><br><sub>Maroon</sub></td>\n  </tr>\n  <tr>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-plum.png\" width=\"260\"><br><sub>Plum</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-jade.png\" width=\"260\"><br><sub>Jade</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/themes/theme-charcoal.png\" width=\"260\"><br><sub>Charcoal</sub></td>\n  </tr>\n</table>\n\n### Theme detail\n\nThe gallery above shows every theme's Today-list swatch; the three shots\nbelow go a level deeper — the same task expanded into its full inline editor\n(template, tags, attachments, assignee all visible) so you can judge a\ntheme's readability on real controls, not just its background color.\n\n<table>\n  <tr>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/theme-detail-light.png\" width=\"260\"><br><sub>Light</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/theme-detail-dark.png\" width=\"260\"><br><sub>Dark</sub></td>\n    <td align=\"center\" width=\"33%\"><img src=\"docs/screenshots/theme-detail-synthwave.png\" width=\"260\"><br><sub>Synthwave</sub></td>\n  </tr>\n</table>\n\n## Install\n\nFrom source (works today):\n\n```bash\ngit clone https://github.com/aronvaughan/punchlist && cd punchlist\nnpm install && npm link          # puts `punchlist` on PATH\npunchlist serve                  # http://127.0.0.1:8600\n```\n\nAlso published on npm as **`@aronvaughan/punchlist`**:\n\n```bash\nnpx @aronvaughan/punchlist serve\n# or install the CLI globally:\nnpm install -g @aronvaughan/punchlist\n```\n\nThe published tarball ships `src`, `bin`, `migrations`, `public`, `skills`,\n`scripts`, and the bundled `punchlist-templates/` packs — run\n`npm pack --dry-run` to see exactly what's included. Two runtime dependencies:\nHono and the official MCP SDK.\n\n## License\n\n[MIT](LICENSE) © 2026 Aron Vaughan.\n","readmeFilename":"README.md","_rev":"1-1006be26bf9a1df6c70bab4f12517c1a"}