wood-fired-tasks 2.0.5 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/AGENTS.md +4 -2
  2. package/CHANGELOG.md +90 -0
  3. package/README.md +102 -89
  4. package/SECURITY.md +3 -3
  5. package/dist/api/api-response.d.ts +76 -0
  6. package/dist/api/routes/models/index.d.ts +14 -0
  7. package/dist/api/routes/models/index.js +48 -0
  8. package/dist/api/routes/projects/index.js +6 -0
  9. package/dist/api/routes/projects/resolve-model.d.ts +3 -0
  10. package/dist/api/routes/projects/resolve-model.js +65 -0
  11. package/dist/api/routes/projects/schemas.d.ts +114 -0
  12. package/dist/api/routes/projects/schemas.js +8 -0
  13. package/dist/api/routes/settings/model-policy.d.ts +18 -0
  14. package/dist/api/routes/settings/model-policy.js +58 -0
  15. package/dist/api/server.d.ts +9 -0
  16. package/dist/api/server.js +29 -0
  17. package/dist/cli/api/client.d.ts +27 -0
  18. package/dist/cli/api/client.js +23 -0
  19. package/dist/cli/api/types.d.ts +9 -0
  20. package/dist/cli/auth/browser-open.js +11 -0
  21. package/dist/cli/auth/manual-pat.d.ts +99 -0
  22. package/dist/cli/auth/manual-pat.js +163 -0
  23. package/dist/cli/bin/tasks-client.js +6 -0
  24. package/dist/cli/bin/tasks.js +7 -0
  25. package/dist/cli/commands/login.d.ts +39 -0
  26. package/dist/cli/commands/login.js +99 -0
  27. package/dist/cli/commands/models.d.ts +44 -0
  28. package/dist/cli/commands/models.js +142 -0
  29. package/dist/cli/commands/project-set-models.d.ts +12 -0
  30. package/dist/cli/commands/project-set-models.js +66 -0
  31. package/dist/cli/commands/settings-set-models.d.ts +12 -0
  32. package/dist/cli/commands/settings-set-models.js +53 -0
  33. package/dist/cli/commands/setup.d.ts +24 -98
  34. package/dist/cli/commands/setup.js +89 -243
  35. package/dist/cli/output/formatters.js +13 -0
  36. package/dist/cli/util/prompt.d.ts +2 -2
  37. package/dist/cli/util/prompt.js +30 -11
  38. package/dist/db/migrations/016-model-policy.d.ts +33 -0
  39. package/dist/db/migrations/016-model-policy.js +47 -0
  40. package/dist/index.d.ts +16 -0
  41. package/dist/index.js +15 -0
  42. package/dist/lib/loop-run/schema.d.ts +3 -0
  43. package/dist/lib/loop-run/schema.js +16 -0
  44. package/dist/mcp/index.js +25 -1
  45. package/dist/mcp/remote/register-tools.d.ts +7 -1
  46. package/dist/mcp/remote/register-tools.js +126 -1
  47. package/dist/mcp/remote/rest-client.d.ts +46 -0
  48. package/dist/mcp/remote/rest-client.js +46 -0
  49. package/dist/mcp/server.d.ts +9 -3
  50. package/dist/mcp/server.js +27 -3
  51. package/dist/mcp/tools/model-tools.d.ts +52 -0
  52. package/dist/mcp/tools/model-tools.js +147 -0
  53. package/dist/mcp/tools/project-tools.js +6 -2
  54. package/dist/repositories/project.repository.js +44 -7
  55. package/dist/repositories/settings.repository.d.ts +26 -0
  56. package/dist/repositories/settings.repository.js +50 -0
  57. package/dist/schemas/model-policy.schema.d.ts +120 -0
  58. package/dist/schemas/model-policy.schema.js +60 -0
  59. package/dist/schemas/project.schema.d.ts +76 -0
  60. package/dist/schemas/project.schema.js +3 -0
  61. package/dist/schemas/task.schema.d.ts +20 -20
  62. package/dist/schemas/wsjf.schema.d.ts +4 -4
  63. package/dist/services/model-catalog.service.d.ts +62 -0
  64. package/dist/services/model-catalog.service.js +111 -0
  65. package/dist/services/model-policy.service.d.ts +58 -0
  66. package/dist/services/model-policy.service.js +76 -0
  67. package/dist/services/project.service.js +7 -3
  68. package/dist/services/settings.service.d.ts +50 -0
  69. package/dist/services/settings.service.js +39 -0
  70. package/dist/skills/agents/integration-auditor.md +11 -0
  71. package/dist/skills/tasks/audit.md +29 -3
  72. package/dist/skills/tasks/decompose.md +38 -8
  73. package/dist/skills/tasks/loop-dag.md +8 -5
  74. package/dist/skills/tasks/loop-shared.md +40 -0
  75. package/dist/skills/tasks/loop.md +5 -3
  76. package/dist/skills/tasks/set-models.md +221 -0
  77. package/dist/types/task.d.ts +35 -0
  78. package/docs/API.md +34 -2
  79. package/docs/CLI.md +114 -6
  80. package/docs/INTERFACES.md +26 -14
  81. package/docs/MCP.md +61 -3
  82. package/docs/SETUP.md +29 -12
  83. package/package.json +1 -1
package/AGENTS.md CHANGED
@@ -95,8 +95,10 @@ Treat these as off-limits unless your task explicitly requires touching them.
95
95
  ## Task-orchestration skills (`/tasks:*`)
96
96
 
97
97
  This repo ships agent skills under `skills/tasks/` that automate the
98
- plan→execute→audit loop over a wood-fired-tasks project. They install to
99
- `~/.claude/commands/tasks/` via `install.sh`. The orchestration set:
98
+ plan→execute→audit loop over a wood-fired-tasks project. They are copied to
99
+ `~/.claude/commands/tasks/` by `wood-fired-tasks setup` (the published-npm
100
+ install path; the old root `install.sh` is a deprecated shim that just delegates
101
+ to `setup`). The orchestration set:
100
102
 
101
103
  | Skill | Status | One-line purpose |
102
104
  |---|---|---|
package/CHANGELOG.md CHANGED
@@ -13,6 +13,96 @@ vulnerabilities, supply-chain pinning) are always called out under `Security`.
13
13
 
14
14
  _No changes yet._
15
15
 
16
+ ## [v2.1.0] - 2026-06-09
17
+
18
+ ### Added
19
+ - **Configurable Task Models** (PR #55, project 39 — 17 tasks). Route the
20
+ `/tasks:loop` worker (`execution`), verifier (`validation`), and planning
21
+ agents — decompose / audit / integration-auditor (`planning`) — to different
22
+ Anthropic models via a per-project + database-default `model_policy`.
23
+ - **Policy model:** six power categories (`minimal…maximum`, a 1:1 relabel of
24
+ the WSJF jobSize Fibonacci tiers) plus an `auto` sentinel; per-role
25
+ `byCategory` routing with a uniform `byCategory → constant → default →
26
+ null` slot walk, two-layer per-slot merge (`project ?? global`). Migration
27
+ 016 adds `projects.model_policy` and the `app_settings` singleton.
28
+ - **Runtime model catalog:** live discovery via the Anthropic Models API
29
+ (set the optional `ANTHROPIC_API_KEY`) with a static fallback marked
30
+ `stale: true` — the read path never throws.
31
+ - **MCP:** four new tools — `list_models`, `resolve_model`,
32
+ `get_model_defaults`, `set_model_defaults` — on **both** the stdio and
33
+ remote servers (tool count 27 → 31).
34
+ - **REST:** `GET /models`, `GET|PUT /settings/model-policy`,
35
+ `GET /projects/:id/resolve-model`, and `model_policy` on project
36
+ create/update/get.
37
+ - **CLI:** `tasks models list`, `tasks project-set-models <id>`, and
38
+ `tasks settings-set-models` — the set-models commands fetch-merge-write
39
+ client-side, so incremental invocations never destroy earlier role config.
40
+ - **Skills:** a `/tasks:set-models` interview (one four-stage checklist
41
+ question per role, fixed option order, catalog-only options) and a
42
+ canonical, telemetry-grounded **Default Model Map** in `loop-shared.md` §R
43
+ that makes `auto` resolution deterministic (execution
44
+ sonnet/sonnet/opus/fable across the category ladder, validation
45
+ haiku/sonnet/opus/opus, planning opus). Optional
46
+ `--execution-model` / `--validation-model` / `--planning-model` run-arg
47
+ overrides ride LOOP-RUN frontmatter for replay provenance.
48
+
49
+ ### Fixed
50
+ - **Test suite no longer launches the developer's real browser.**
51
+ `setup.remote.test.ts` drove the device flow with `openBrowser: true`, so
52
+ every `npm test` on a DISPLAY-set desktop spawned `xdg-open` at a mock
53
+ server. `openBrowser()` now honors `WFT_NO_BROWSER`, set for every test via
54
+ `vitest.setup.ts`.
55
+ - **Documentation counts drift.** README / `docs/INTERFACES.md` / `docs/MCP.md`
56
+ had stale tool (27 vs 31), route (52/45 vs 59/52), and CLI command (42 vs 45)
57
+ counts, plus a stale "model tools are stdio-only" claim from before remote
58
+ parity landed; the `interfaces-counts` drift guard now covers the model-tools
59
+ file so the next addition cannot dodge it.
60
+
61
+ ### Security
62
+ - No security-relevant changes. The new `/models`, `/settings/model-policy`,
63
+ and `resolve-model` routes inherit the standard `/api/v1` auth chain; the
64
+ optional `ANTHROPIC_API_KEY` is only ever sent to the Anthropic Models API
65
+ and is never echoed in responses or logs.
66
+
67
+ ## [v2.0.6] - 2026-06-08
68
+
69
+ ### Added
70
+ - **`tasks login --token <pat>` — a manual-PAT login path.** `tasks login` was
71
+ device-flow only and **dead-ended on a remote non-`https` server**: the OAuth
72
+ device flow can't complete where Google rejects the non-`https` redirect URI,
73
+ and there was no way to supply a PAT instead. `login` now accepts `--token`
74
+ (validated against `GET /api/v1/me`, then persisted to the credentials file)
75
+ and, like `tasks setup`, applies the `canUseBrowserSso` gate — on a plain-`http`
76
+ non-localhost server it prints the `https`-required / how-to-mint-a-PAT
77
+ guidance and (on a TTY) prompts for one instead of launching a flow that can't
78
+ finish. The shared manual-PAT logic now lives in one module so `login` and
79
+ `setup` can't drift. (#857)
80
+
81
+ ### Fixed
82
+ - **`tasks setup --remote --token <pat>` now actually authenticates you.** The
83
+ non-interactive `--token` path wrote the PAT to an **orphaned cache file that
84
+ no code reads** and never to the credentials file, so it reported success while
85
+ leaving *both* the CLI ("Not authenticated. Run: tasks login") and the remote
86
+ MCP bridge unauthenticated — the likely root cause of "remote MCP shows 0
87
+ projects" reports. The path now validates the PAT against `GET /api/v1/me` and
88
+ persists it to the credentials file (the same writer the device flow uses); the
89
+ bridge resolves its bearer token from there at runtime. The dead `remote-token`
90
+ cache (`cachePat`/`patCachePath`) was removed — the credentials file is the
91
+ single source of truth. (#858)
92
+ - **The interactive "Paste a personal access token:" prompt no longer hangs on a
93
+ real terminal.** `promptSecret` enables TTY raw mode to suppress echo, which
94
+ disables CR→LF translation, so the Enter key delivers a bare `\r` (0x0D) — but
95
+ the line reader only terminated on `\n`, so the read blocked forever (pasted
96
+ PAT buffered, Enter never recognized). Hit on Windows PowerShell during
97
+ `tasks setup --remote` manual-PAT entry; platform-independent. The reader now
98
+ treats `\r`, `\n`, or `\r\n` as the line terminator. (#856)
99
+
100
+ ### Security
101
+ - The `--remote --token` PAT is now stored only in the `0600` credentials file
102
+ and is still **never** embedded in `~/.claude.json` (the remote MCP entry stays
103
+ URL-only, #810). The removed `remote-token` cache eliminates a second on-disk
104
+ copy of the secret.
105
+
16
106
  ## [v2.0.5] - 2026-06-08
17
107
 
18
108
  ### Changed
package/README.md CHANGED
@@ -15,8 +15,8 @@ Wood Fired Tasks is open-source coordination infrastructure for fleets of AI cod
15
15
  **Key capabilities:**
16
16
 
17
17
  - `/tasks:*` skill files implementing the plan→decompose→loop→audit lifecycle (ship as Claude Code slash commands; the recipes are vendor-neutral)
18
- - MCP server with 27 tools for native agent integration (local SQLite or remote HTTP modes) + cross-platform installers (Linux/macOS and Windows)
19
- - REST API with 52 route handlers across `src/api/routes/` (1 public `/health`; the rest authenticated; a single instance serves up to 45 — OIDC-disabled stubs are mutually exclusive with the live OIDC routes) and a `tasks` CLI with 42 commands
18
+ - MCP server with 31 tools for native agent integration (local SQLite or remote HTTP modes) + a single cross-platform npm install (Linux/macOS/Windows)
19
+ - REST API with 59 route handlers across `src/api/routes/` (1 public `/health`; the rest authenticated; a single instance serves up to 52 — OIDC-disabled stubs are mutually exclusive with the live OIDC routes) and a `tasks` CLI with 45 commands
20
20
  - Atomic task claiming with optimistic locking + workflow automation (parent auto-complete, dependency auto-unblock) for multi-agent coordination
21
21
  - Real-time Server-Sent Events (SSE) for task/project change notifications
22
22
  - SQLite database with WAL mode, FTS5 full-text search, and automatic migrations
@@ -62,25 +62,58 @@ Coding agents (Claude Code, Cursor, Gemini, Codex, and others) should start with
62
62
  2. [docs/AGENT_CONTEXT.md](docs/AGENT_CONTEXT.md) — the vendor-neutral context contract.
63
63
  3. [.agent-context.json](.agent-context.json) — machine-readable manifest of canonical files and their budgets.
64
64
 
65
- Vendor-specific files (`CLAUDE.md`, `.cursor/`, `.gemini/`, `.codex/`) are adapters and MUST NOT carry unique facts — see `docs/AGENT_CONTEXT.md` §6.
65
+ A root [`llms.txt`](llms.txt) (the emerging agent-discovery convention) also
66
+ points here, and both it and the curated agent docs ship inside the npm tarball.
67
+ Vendor-specific files (`CLAUDE.md`, `.cursor/`, `.gemini/`, `.codex/`) are
68
+ adapters and MUST NOT carry unique facts — see `docs/AGENT_CONTEXT.md` §6.
66
69
 
67
- ## Quick Start
70
+ ## Install & run modes
68
71
 
69
- Install from npm — **no git clone, no build, no admin rights.** The global
70
- install ships the server, the `tasks` CLI, the MCP bridge, and the `/tasks:*`
71
- skills together; `setup` wires them into Claude Code and `serve` runs the API.
72
+ **One install, no git clone, no build, no admin rights.** Installing from npm
73
+ ships the API server, the `tasks` CLI, the MCP bridge, the `wft-router`
74
+ automation daemon, and the `/tasks:*` skills together the *same* install backs
75
+ every way you might run it.
72
76
 
73
77
  ```bash
74
- # 1. Install the CLI globally (never needs sudo — see the admin-free note below)
75
- npm i -g wood-fired-tasks
78
+ npm i -g wood-fired-tasks # never needs sudo — see the admin-free note below
79
+ ```
80
+
81
+ This puts three identical CLI entry points on your PATH — `wood-fired-tasks`,
82
+ `tasks`, and the short alias `wft` — plus `wft-router`. Requires **Node ≥ 22**;
83
+ works on Linux, macOS, and Windows. (There is no `curl | bash` bootstrap and the
84
+ old `install.sh` / `install.ps1` git-clone scripts are retired — npm is the one
85
+ supported install path.)
86
+
87
+ ### Pick how to run it
88
+
89
+ The install is the same everywhere; what differs is *where the database and API
90
+ server live*. Pick the row that matches your environment:
91
+
92
+ | Run mode | Use it when | After installing, run | Guide |
93
+ |----------|-------------|-----------------------|-------|
94
+ | **Solo / local** | You want a tracker on your own machine, backed by a local SQLite DB. | `wood-fired-tasks setup` → `wood-fired-tasks serve` | [Local setup](docs/SETUP.md#frictionless-install-npm--no-clone) |
95
+ | **Background service** | Keep the local API running across logout/reboot (user-scoped, admin-free). | `wood-fired-tasks service install` | [Background service](docs/SETUP.md#background-service-keep-the-server-running) |
96
+ | **Remote client** | A shared server already exists — point this machine at it. | `wood-fired-tasks setup --remote <url> --token wft_pat_…` *(or `tasks login`)* | [Setup modes](docs/SETUP.md#setup-modes) |
97
+ | **Self-hosted server** | Stand up the shared API for a team to point at. | `deploy/install.sh` on the host | [Self-hosting & upgrades](docs/SETUP.md#self-hosting-and-upgrades) |
98
+ | **Multi-OS fleet** | Many Windows/Linux/macOS machines on one shared backlog. | self-host once, then `setup --remote` per client | [Multi-OS fleet](docs/SETUP.md#multi-os-client-fleet-one-shared-on-prem-server) |
99
+
100
+ > **New here? Start with Solo / local** — three commands and the `/tasks:*`
101
+ > workflow is live in Claude Code. Nothing is wasted if you move to a shared
102
+ > server later: the modes coexist (a Local stdio entry and a Remote bridge entry
103
+ > can both live in `~/.claude.json` at once).
76
104
 
77
- # 2. Wire it into Claude Code: merges the local stdio MCP server into
78
- # ~/.claude.json and copies the /tasks:* skills + subagents — idempotent,
79
- # no manual JSON editing.
105
+ ### Solo / local fastest start
106
+
107
+ After `npm i -g wood-fired-tasks` (above), two commands wire it into Claude Code
108
+ and run the server:
109
+
110
+ ```bash
111
+ # Merge the local stdio MCP server into ~/.claude.json and copy the /tasks:*
112
+ # skills + subagents — idempotent, no manual JSON editing.
80
113
  wood-fired-tasks setup
81
114
 
82
- # 3. Run the API server. Migrates the OS app-data DB on start and listens on
83
- # 127.0.0.1:3000 (set HOST=0.0.0.0 to expose on the LAN).
115
+ # Run the API. Migrates the OS app-data DB on start; listens on 127.0.0.1:3000
116
+ # (set HOST=0.0.0.0 to expose on the LAN).
84
117
  wood-fired-tasks serve
85
118
  ```
86
119
 
@@ -113,15 +146,13 @@ wood-fired-tasks service install # Linux: user-scoped systemd unit (admin-free
113
146
  wood-fired-tasks self-update # npm i -g wood-fired-tasks@latest (no sudo)
114
147
  ```
115
148
 
116
- **Point at a shared remote server** instead of running it locally:
117
-
118
- ```bash
119
- wood-fired-tasks setup --remote https://tasks.example.com --token wft_pat_…
120
- ```
121
-
122
- This writes a `wood-fired-tasks-remote` MCP entry (proxying every tool to the
123
- REST API) and caches the PAT under your OS config dir. For a full
124
- Windows/Linux/macOS fleet on one on-prem server, see
149
+ **Point at a shared remote server** (the *Remote client* mode above) with
150
+ `wood-fired-tasks setup --remote https://tasks.example.com --token wft_pat_…`: it
151
+ writes a URL-only `wood-fired-tasks-remote` MCP entry (proxying every tool to the
152
+ REST API) and persists the validated PAT to the CLI credentials file — the same
153
+ file `tasks login` writes; the bridge reads its bearer token from there at
154
+ runtime, never from `~/.claude.json`. Omit `--token` for the interactive
155
+ device-flow / manual-PAT onboarding. Full fleet recipe:
125
156
  [Multi-OS client fleet](docs/SETUP.md#multi-os-client-fleet-one-shared-on-prem-server).
126
157
 
127
158
  Browse the bundled guides from anywhere with `wood-fired-tasks docs list` /
@@ -281,7 +312,7 @@ flowchart TB
281
312
  | Interface | Access Method | Transport | Auth |
282
313
  |-----------|--------------|-----------|------|
283
314
  | REST API | HTTP endpoints | Port 3000 (configurable) | PAT (`Authorization: Bearer`) or OIDC session cookie |
284
- | CLI | `tasks` command | HTTP to API server (most cmds); direct SQLite for offline ops (`backup`, `doctor`, `stats`, `db-check`, `completed`) | `API_KEY` env var (holds a PAT, sent as `Authorization: Bearer`) |
315
+ | CLI | `tasks` command | HTTP to API server (most cmds); direct SQLite for offline ops (`backup`, `doctor`, `stats`, `db-check`, `completed`) | Credentials file from `tasks login` / `setup --remote --token` (preferred); else `API_KEY` env or `--token` flag — each a PAT sent as `Authorization: Bearer` |
285
316
  | MCP Server | stdio JSON-RPC (local) or HTTP (remote variant) | MCP client integration | None for stdio (local access); Bearer PAT for remote |
286
317
  | Slack subprocess | Slack Socket Mode | WebSocket to Slack | Slack signing secret + bot token |
287
318
 
@@ -423,7 +454,7 @@ The OIDC/session/PAT surface backing the auth model lives partly outside the tas
423
454
 
424
455
  A device-authorization flow under `/auth/device*` (`GET /auth/device`, `POST /auth/device/code`, `POST /auth/device/token`, `POST /auth/device/verify`) supports headless PAT minting. When OIDC is **not** configured, the `/auth/*` and `/auth/device/*` routes are replaced by disabled-stub handlers (HTTP 501), so they exist in both modes but only one set is live per instance. When `SESSION_COOKIE_SECRET` is set, top-level HTML web routes (`GET /login`, `GET /me`, `GET /me/tokens`, `POST /me/tokens/:id/revoke`) are also served for the browser sign-in UI.
425
456
 
426
- This brings the full registered surface to **52 route handlers** under `src/api/routes/` — derived by counting `fastify.<verb>(` / `server.<verb>(` registrations across the route files (excluding tests). A single running instance serves up to **45** of them: the 7 OIDC-disabled stub handlers are mutually exclusive with the live OIDC `/auth/*` routes.
457
+ This brings the full registered surface to **59 route handlers** under `src/api/routes/` — derived by counting `fastify.<verb>(` / `server.<verb>(` registrations across the route files (excluding tests). A single running instance serves up to **52** of them: the 7 OIDC-disabled stub handlers are mutually exclusive with the live OIDC `/auth/*` routes.
427
458
 
428
459
  For detailed API documentation including request/response schemas, see [docs/API.md](docs/API.md).
429
460
 
@@ -431,11 +462,12 @@ For detailed API documentation including request/response schemas, see [docs/API
431
462
 
432
463
  The `tasks` command provides terminal access to all task operations.
433
464
 
434
- Every `tasks <command>` example below is shorthand for `npm run cli -- <command>`
435
- (everything after `--` is forwarded verbatim). Running `npm link` once from the
436
- project root is **optional** it installs a global `tasks` command so the
437
- examples work verbatim from any directory. See [docs/CLI.md](docs/CLI.md) for the
438
- full reference.
465
+ If you installed from npm (`npm i -g wood-fired-tasks`), the `tasks`, `wft`, and
466
+ `wood-fired-tasks` commands are already on your PATH and every `tasks <command>`
467
+ example below works verbatim. Working from a clone instead? Use
468
+ `npm run cli -- <command>` (everything after `--` is forwarded verbatim), or run
469
+ `npm link` once from the project root to install a global `tasks`. See
470
+ [docs/CLI.md](docs/CLI.md) for the full reference.
439
471
 
440
472
  **Global Flags:**
441
473
  - `--json` - Output in machine-readable JSON format
@@ -505,7 +537,7 @@ full reference.
505
537
 
506
538
  | Command | Description |
507
539
  |---------|-------------|
508
- | tasks login | OIDC sign-in; caches a PAT to the local credentials file |
540
+ | tasks login | OIDC device-flow sign-in, or `--token <pat>` for a manual PAT (required for remote non-`https` servers); writes a PAT to the local credentials file |
509
541
  | tasks logout | Revoke the active PAT and clear the local credentials |
510
542
  | tasks whoami | Show the currently authenticated identity |
511
543
 
@@ -528,7 +560,7 @@ For detailed CLI documentation including all options and examples, see [docs/CLI
528
560
 
529
561
  ## MCP Tools Summary
530
562
 
531
- The MCP server exposes 27 tools and 1 resource for Claude Code integration — Task (9), Project (5), Comment (3), Dependency (3), Health (1), Topology (1), Wait (1), and WSJF (4). A second entry point (`npm run mcp:remote`) exposes the REST-backed tool surface (also 27 tools at full parity; `wait_for_unblock` resolves over the SSE event stream rather than the in-process EventBus) for clients running on a different host than the bugs API — see [docs/MCP.md#remote-mcp-server](docs/MCP.md#remote-mcp-server).
563
+ The MCP server exposes 31 tools and 1 resource for Claude Code integration — Task (9), Project (5), Comment (3), Dependency (3), Health (1), Topology (1), Wait (1), WSJF (4), and Model (4). A second entry point (`npm run mcp:remote`) exposes the same 31 tools REST-backed (the four Model tools proxy `GET /models`, `GET /projects/:id/resolve-model`, and `GET|PUT /settings/model-policy`; `wait_for_unblock` resolves over the SSE event stream rather than the in-process EventBus) for clients running on a different host than the bugs API — see [docs/MCP.md#remote-mcp-server](docs/MCP.md#remote-mcp-server).
532
564
 
533
565
  ### Task Tools (9)
534
566
 
@@ -597,6 +629,15 @@ The MCP server exposes 27 tools and 1 resource for Claude Code integration — T
597
629
  | rescore_project | (Mutation) Deterministically rescore a project's scored tasks against the current value charter; skips locked components; returns evaluated/changed/skipped-locked counts |
598
630
  | wsjf_health | Non-blocking degeneracy/pitfall linter for a project's WSJF state (empty findings ⇔ healthy) |
599
631
 
632
+ ### Model Tools (4)
633
+
634
+ | Tool | Description |
635
+ |------|-------------|
636
+ | list_models | Runtime-discovered Claude model catalog (Anthropic Models API, TTL-cached); `stale: true` when serving the static fallback |
637
+ | resolve_model | Resolve the model for a pipeline role (`execution` \| `validation` \| `planning`) for a project, optionally task-scoped for jobSize→category routing; returns `{ model }` or `null` (inherit the session model) |
638
+ | get_model_defaults | Read the database-wide default `ModelPolicy` (`null` when unset) |
639
+ | set_model_defaults | (Mutation) Set or clear the database-wide default `ModelPolicy` |
640
+
600
641
  ### Resources (1)
601
642
 
602
643
  | URI | Description |
@@ -729,66 +770,39 @@ variables) lives in [docs/SETUP.md → Environment Variables](docs/SETUP.md#envi
729
770
  ### Key Commands
730
771
 
731
772
  ```bash
732
- # Development mode with hot reload
733
- npm run dev
734
-
735
- # Run tests (~2600+ tests)
736
- npm test
737
-
738
- # Watch mode for tests
739
- npm run test:watch
740
-
741
- # Build TypeScript
742
- npm run build
743
-
744
- # Run CLI in development (without building)
745
- npm run cli -- <command>
746
-
747
- # Run MCP server in development
748
- npm run mcp:dev
773
+ npm run dev # REST API, hot reload npm run build # typecheck + compile
774
+ npm test # full suite (~3,700+) npm run mcp:dev # MCP server (stdio)
775
+ npm run cli -- <command> # run the CLI in-tree npm run lint # biome check
749
776
  ```
750
777
 
778
+ The canonical command table (focused tests, migrations, quality gate) lives in
779
+ [AGENTS.md → Essential commands](AGENTS.md#essential-commands).
780
+
751
781
  ### Database
752
782
 
753
- SQLite with better-sqlite3 driver, WAL mode, and automatic migrations via Umzug. Fifteen migration files in `src/db/migrations/`:
754
-
755
- 1. `001-initial-schema.ts` projects, tasks, task_tags (plus the tasks_fts FTS5 virtual table and sync triggers)
756
- 2. `002-task-hierarchy-and-dependencies.ts` adds `parent_task_id` to tasks and creates the `task_dependencies` table
757
- 3. `003-comments-and-estimates.ts` creates the `task_comments` table and adds `estimated_minutes` to tasks
758
- 4. `004-claim-protocol.ts` version field, claimed_at, idempotency_keys table
759
- 5. `005-backlogged-status.ts` — adds `backlogged` to the status CHECK constraint (rebuilds tasks table; preserves FTS triggers)
760
- 6. `006-slack-channel-subscriptions.ts` — `slack_channel_subscriptions` table for the Slack notifier
761
- 7. `007-completed-at.ts` — `completed_at` column on tasks (set on transition into `done`, backfilled from `updated_at`)
762
- 8. `008-identity-tables.ts` — `users` and `api_tokens` tables (OIDC/PAT identity)
763
- 9. `009-parallel-fk-columns.ts` — parallel `*_user_id` FK columns alongside the legacy TEXT identity columns
764
- 10. `010-identity-uniqueness-indexes.ts` — uniqueness indexes on user identity (oidc_sub, email)
765
- 11. `011-acceptance-criteria.ts` — `acceptance_criteria` column on tasks
766
- 12. `012-verification-evidence.ts` — `verification_evidence` column on tasks (verifier verdict + checks)
767
- 13. `013-wsjf-fields.ts` — adds the per-task WSJF columns: four Fibonacci-constrained component columns (`wsjf_value`, `wsjf_time_criticality`, `wsjf_risk_opportunity`, `wsjf_job_size`) plus five JSON metadata columns (`wsjf_evidence`, `wsjf_locked`, `wsjf_source`, `wsjf_classifications`, `wsjf_features`); all-four-or-none invariant enforced at the DTO boundary
768
- 14. `014-value-charter.ts` — adds the nullable `value_charter` JSON column on `projects` (the per-project Business-Value reference frame)
769
- 15. `015-wsjf-audit.ts` — creates the three append-only audit tables: `wsjf_rescore_run`, `wsjf_score_history` (one immutable row per score write), and `project_charter_history` (full charter snapshot per interview version)
783
+ SQLite with the better-sqlite3 driver, WAL mode, and automatic forward
784
+ migrations via Umzug. The 15 migration files in `src/db/migrations/` are the
785
+ canonical, self-documenting schema history — from `001-initial-schema` (projects,
786
+ tasks, FTS5) through the identity tables (`008`–`010`, OIDC/PAT), acceptance
787
+ criteria + verification evidence (`011`–`012`), and the WSJF columns, value
788
+ charter, and append-only audit tables (`013`–`015`). Read the files directly for
789
+ exact DDL; [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) covers how they fit
790
+ together.
770
791
 
771
792
  ### Testing
772
793
 
773
- ~2600+ tests covering:
774
- - Service layer unit tests
775
- - API route integration tests (all endpoints)
776
- - MCP tool tests (all tools)
777
- - CLI command tests
778
- - Event system tests (EventBus, SSEManager, events API)
779
- - Claim protocol tests (including 20-agent concurrency)
780
- - Workflow engine tests (auto-complete, auto-unblock, cascade depth)
781
- - Skill file validation tests
794
+ ~3,700+ vitest tests span the service layer, every API route, every MCP tool,
795
+ the CLI, the event system (EventBus/SSEManager), the claim protocol (including
796
+ 20-agent concurrency), the workflow engine (auto-complete/auto-unblock/cascade
797
+ depth), and skill-file validation. Run `npm test`; see
798
+ [docs/WORKFLOWS.md](docs/WORKFLOWS.md) for focused-run recipes.
782
799
 
783
800
  ### Code Quality Roadmap
784
801
 
785
- The current TypeScript service quality baseline and the prioritized
786
- uplift roadmap (lint/format gate, stricter compiler flags, architecture
787
- guardrails, migration safety, CI/release automation) live in
788
- [docs/CODE_QUALITY_ROADMAP.md](docs/CODE_QUALITY_ROADMAP.md). It is the
789
- source of truth for the `Code Quality Uplift Roadmap` project tracked
790
- in the bugs database and should be linked from future PR/release
791
- quality checklist work.
802
+ The TypeScript quality baseline and the prioritized uplift roadmap (lint/format
803
+ gate, stricter compiler flags, architecture guardrails, migration safety,
804
+ CI/release automation) live in
805
+ [docs/CODE_QUALITY_ROADMAP.md](docs/CODE_QUALITY_ROADMAP.md).
792
806
 
793
807
  ## Integrations
794
808
 
@@ -810,7 +824,7 @@ slash-command reference, channel subscription model, error handling.
810
824
  ### Claude Code (MCP)
811
825
 
812
826
  The shipped MCP server registers as a stdio MCP target in `~/.claude.json`
813
- and exposes 27 tools plus the `/tasks:*` skill files. See
827
+ and exposes 31 tools plus the `/tasks:*` skill files. See
814
828
  [docs/MCP.md](docs/MCP.md) and the "Claude Code Integration" section in
815
829
  [docs/SETUP.md](docs/SETUP.md#claude-code-integration).
816
830
 
@@ -834,12 +848,11 @@ guarantee.
834
848
 
835
849
  ## Release Verification
836
850
 
837
- Before publishing to npm, run `npm run pack:check` (alias for
838
- `npm pack --dry-run`) and inspect the printed file list. Confirm that
839
- `dist/` JS + `.d.ts` files, `LICENSE`, `README.md`, `CHANGELOG.md`, and
840
- `SECURITY.md` are present, and that `src/`, `.env*`, `data/*.db`,
841
- `.planning/`, and test files are **absent**. The package uses an explicit
842
- `files` allowlist in `package.json` — adjust it there if the output drifts.
851
+ Before publishing, `npm run pack:check` (`npm pack --dry-run`) prints the tarball
852
+ file list confirm `dist/` + `.d.ts`, `LICENSE`, `README.md`, `CHANGELOG.md`,
853
+ `SECURITY.md` are present and `src/`, `.env*`, `data/*.db`, `.planning/`, and
854
+ tests are absent. The `files` allowlist in `package.json` controls this. Full
855
+ release procedure: [docs/RELEASE.md](docs/RELEASE.md).
843
856
 
844
857
  ## License
845
858
 
package/SECURITY.md CHANGED
@@ -14,11 +14,11 @@ security updates. Older tags are provided as-is.
14
14
  | Version | Supported |
15
15
  | ----------------- | ------------------ |
16
16
  | `main` (HEAD) | :white_check_mark: |
17
- | `v1.18` (latest) | :white_check_mark: |
18
- | `v1.0` – `v1.17` | :x: |
17
+ | `v2.1.0` (latest) | :white_check_mark: |
18
+ | `v1.0` – `v2.0.6` | :x: |
19
19
 
20
20
  "Latest" tracks whichever tag is most recent on GitHub; at the time of
21
- writing that is `v1.18`. If you are reading this on an older checkout,
21
+ writing that is `v2.1.0`. If you are reading this on an older checkout,
22
22
  verify the current latest release via
23
23
  `git tag --sort=-creatordate | head -1` or the GitHub Releases page.
24
24
 
@@ -115,6 +115,44 @@ export declare function parseProjectResponse(payload: unknown, endpoint: string)
115
115
  interview_version: number;
116
116
  updated_at: string;
117
117
  } | null | undefined;
118
+ model_policy?: {
119
+ execution?: {
120
+ byCategory?: {
121
+ minimal?: string | undefined;
122
+ light?: string | undefined;
123
+ moderate?: string | undefined;
124
+ strong?: string | undefined;
125
+ heavy?: string | undefined;
126
+ maximum?: string | undefined;
127
+ } | undefined;
128
+ constant?: string | undefined;
129
+ default?: string | undefined;
130
+ } | undefined;
131
+ validation?: {
132
+ byCategory?: {
133
+ minimal?: string | undefined;
134
+ light?: string | undefined;
135
+ moderate?: string | undefined;
136
+ strong?: string | undefined;
137
+ heavy?: string | undefined;
138
+ maximum?: string | undefined;
139
+ } | undefined;
140
+ constant?: string | undefined;
141
+ default?: string | undefined;
142
+ } | undefined;
143
+ planning?: {
144
+ byCategory?: {
145
+ minimal?: string | undefined;
146
+ light?: string | undefined;
147
+ moderate?: string | undefined;
148
+ strong?: string | undefined;
149
+ heavy?: string | undefined;
150
+ maximum?: string | undefined;
151
+ } | undefined;
152
+ constant?: string | undefined;
153
+ default?: string | undefined;
154
+ } | undefined;
155
+ } | null | undefined;
118
156
  };
119
157
  /** Convenience: validate a task list (envelope or bare array). */
120
158
  export declare function parseTaskListResponse(payload: unknown, endpoint: string): {
@@ -174,6 +212,44 @@ export declare function parseProjectListResponse(payload: unknown, endpoint: str
174
212
  interview_version: number;
175
213
  updated_at: string;
176
214
  } | null | undefined;
215
+ model_policy?: {
216
+ execution?: {
217
+ byCategory?: {
218
+ minimal?: string | undefined;
219
+ light?: string | undefined;
220
+ moderate?: string | undefined;
221
+ strong?: string | undefined;
222
+ heavy?: string | undefined;
223
+ maximum?: string | undefined;
224
+ } | undefined;
225
+ constant?: string | undefined;
226
+ default?: string | undefined;
227
+ } | undefined;
228
+ validation?: {
229
+ byCategory?: {
230
+ minimal?: string | undefined;
231
+ light?: string | undefined;
232
+ moderate?: string | undefined;
233
+ strong?: string | undefined;
234
+ heavy?: string | undefined;
235
+ maximum?: string | undefined;
236
+ } | undefined;
237
+ constant?: string | undefined;
238
+ default?: string | undefined;
239
+ } | undefined;
240
+ planning?: {
241
+ byCategory?: {
242
+ minimal?: string | undefined;
243
+ light?: string | undefined;
244
+ moderate?: string | undefined;
245
+ strong?: string | undefined;
246
+ heavy?: string | undefined;
247
+ maximum?: string | undefined;
248
+ } | undefined;
249
+ constant?: string | undefined;
250
+ default?: string | undefined;
251
+ } | undefined;
252
+ } | null | undefined;
177
253
  }[];
178
254
  total: number;
179
255
  limit: number;
@@ -0,0 +1,14 @@
1
+ import { FastifyPluginAsyncZod } from 'fastify-type-provider-zod';
2
+ import { z } from 'zod';
3
+ /** The `{ models, stale }` envelope returned by GET /models. */
4
+ export declare const ModelCatalogResponseSchema: z.ZodObject<{
5
+ models: z.ZodArray<z.ZodObject<{
6
+ id: z.ZodString;
7
+ display_name: z.ZodString;
8
+ family: z.ZodString;
9
+ created_at: z.ZodString;
10
+ }, z.core.$strip>>;
11
+ stale: z.ZodBoolean;
12
+ }, z.core.$strip>;
13
+ declare const modelsRoutes: FastifyPluginAsyncZod;
14
+ export default modelsRoutes;
@@ -0,0 +1,48 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Configurable Task Models (Task 13) — GET /api/v1/models
4
+ *
5
+ * Exposes the runtime-discovered Claude model catalog (task #917's
6
+ * `model-catalog.service`) over REST so the remote MCP proxy / dashboard /
7
+ * `set-models` interview can enumerate the available models. The 200 body is
8
+ * `{ models, stale }` — identical to the service's `ModelCatalog` and the
9
+ * stdio MCP `list_models` tool's structured output.
10
+ *
11
+ * `stale: true` signals the catalog was served from the static fallback (no
12
+ * ANTHROPIC_API_KEY, non-OK Models API response, or a network error). The
13
+ * service NEVER throws from `list()`, so this route always returns 200.
14
+ *
15
+ * Auth: inherits the standard `/api/v1` auth chain (the parent plugin is
16
+ * mounted inside the `/api/v1` scope that wires `authPlugin`). No custom guard.
17
+ */
18
+ /** One discovered model, mirroring `ModelCatalogEntry` from the catalog service. */
19
+ const ModelCatalogEntrySchema = z.object({
20
+ id: z.string(),
21
+ display_name: z.string(),
22
+ family: z.string(),
23
+ created_at: z.string(),
24
+ });
25
+ /** The `{ models, stale }` envelope returned by GET /models. */
26
+ export const ModelCatalogResponseSchema = z.object({
27
+ models: z.array(ModelCatalogEntrySchema),
28
+ stale: z.boolean(),
29
+ });
30
+ const modelsRoutes = async (fastify) => {
31
+ fastify.get('/', {
32
+ schema: {
33
+ tags: ['models'],
34
+ description: 'List the available Claude model catalog (runtime-discovered via the ' +
35
+ 'Anthropic Models API, TTL-cached). Returns `{ models, stale }`; ' +
36
+ '`stale: true` means the static fallback was served (no API key / ' +
37
+ 'unreachable Models API).',
38
+ response: {
39
+ 200: ModelCatalogResponseSchema,
40
+ },
41
+ },
42
+ }, async (_request, reply) => {
43
+ const catalog = await fastify.modelCatalogService.list();
44
+ return reply.send(catalog);
45
+ });
46
+ };
47
+ export default modelsRoutes;
48
+ //# sourceMappingURL=index.js.map
@@ -3,6 +3,7 @@ import { CreateProjectSchema, UpdateProjectSchema } from '../../../schemas/task.
3
3
  import { ProjectResponseSchema, ProjectListPaginatedResponseSchema } from './schemas.js';
4
4
  import dependencyGraphRoutes from './dependency-graph.js';
5
5
  import topologyRoutes from './topology.js';
6
+ import resolveModelRoutes from './resolve-model.js';
6
7
  import projectWsjfRoutes from './wsjf.js';
7
8
  // Pagination query schema for GET /projects. Mirrors task list bounds.
8
9
  const QueryProjectListSchema = z.object({
@@ -78,6 +79,11 @@ const projectRoutes = async (fastify) => {
78
79
  // MCP `topology_check` proxy tool. Registered as a child plugin alongside
79
80
  // dependency-graph so its colocated `schema:` block lives in its own file.
80
81
  await fastify.register(topologyRoutes);
82
+ // GET /:id/resolve-model — model-policy resolver over REST (task #926),
83
+ // backing the remote MCP `resolve_model` proxy tool. Registered as a child
84
+ // plugin alongside topology so its colocated `schema:` block lives in its
85
+ // own file.
86
+ await fastify.register(resolveModelRoutes);
81
87
  // WSJF 4.5 (#645): GET /:id/charter-history + GET /:id/rescore-runs.
82
88
  // Registered as a child plugin alongside topology so its colocated `schema:`
83
89
  // blocks live in their own file.
@@ -0,0 +1,3 @@
1
+ import { FastifyPluginAsyncZod } from 'fastify-type-provider-zod';
2
+ declare const resolveModelRoutes: FastifyPluginAsyncZod;
3
+ export default resolveModelRoutes;