@dalmasonto/taskflow-mcp 2.5.1 → 2.7.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.
package/README.md CHANGED
@@ -1,42 +1,174 @@
1
- # @dalmasonto/taskflow-mcp — TaskFlow MCP server + Claude Code hooks
1
+ # @dalmasonto/taskflow-mcp — TaskFlow for AI coding agents
2
2
 
3
- An **agent client** for the TaskFlow backend. It lets a coding agent (e.g. Claude
4
- Code) connect to your TaskFlow project using a stable, per-repo credential
5
- (`.taskflow.json`) and drive the whole agent API: identity, tasks, chat, reviews,
6
- live sessions, streamed terminal output, and real activity logging.
3
+ **TaskFlow** is a multi-agent task board with realtime chat. This package is its
4
+ **MCP server**: it connects a coding agent — **Claude Code, Codex CLI, Gemini CLI,
5
+ Cursor, opencode**, or any other MCP client that can launch a stdio server — to your
6
+ TaskFlow project, so the agent can confirm its identity, work the task board, chat
7
+ with humans and other agents, request and record reviews, stream its terminal, log
8
+ activity, and edit the project's design pages.
7
9
 
8
- > **2.0.0** is a ground-up rewrite. Where the 1.x server kept its own local SQLite
9
- > and inferred an agent's identity from `cwd + ppid`, this package holds **no state**
10
- > and **no local DB**: it talks to a TaskFlow backend over HTTP, and its identity is
11
- > exactly the `key` in `.taskflow.json`, so an agent linked yesterday keeps the same
12
- > identity today. Multi-agent collaboration — channels, reviews, terminal streaming,
13
- > `.taskflow.json` profiles — is new in 2.x.
10
+ It holds **no state and no local DB**: it talks to a TaskFlow backend over HTTP,
11
+ and its identity is exactly the `key` in `.taskflow.json`, so an agent linked
12
+ yesterday keeps the same identity today.
13
+
14
+ > **2.x** is a ground-up rewrite. The 1.x server kept its own local SQLite and
15
+ > inferred an agent's identity from `cwd + ppid`. Multi-agent collaboration —
16
+ > channels, reviews, terminal streaming, `.taskflow.json` profiles — is new in 2.x.
17
+
18
+ ## Quick start
14
19
 
15
20
  ```bash
16
21
  npm install -g @dalmasonto/taskflow-mcp
22
+ taskflow init
23
+ ```
24
+
25
+ Or, on Linux/macOS, the install script does both (checks Node ≥ 20, installs the
26
+ package, then starts `taskflow init`; arguments are passed through to it):
27
+
28
+ ```bash
29
+ curl -fsSL https://raw.githubusercontent.com/dalmasonto/task_flow/main/scripts/install.sh | sh
30
+ ```
31
+
32
+ On **Windows** (PowerShell):
33
+
34
+ ```powershell
35
+ npm i -g @dalmasonto/taskflow-mcp; taskflow init
17
36
  ```
18
37
 
19
- This puts two commands on your PATH:
38
+ The install puts three commands on your PATH:
20
39
 
21
- - **`taskflow-mcp`** — the MCP server an MCP client (Claude Code, Cursor, …) runs.
22
- - **`taskflow-hook`** — the Claude Code lifecycle hook (see [Hooks](#hooks)).
40
+ | Command | What it is |
41
+ | --- | --- |
42
+ | `taskflow` | The setup CLI you run: `taskflow init`, `taskflow doctor`, `taskflow mint`. |
43
+ | `taskflow-mcp` | The MCP server your coding agent launches over stdio. You don't run it by hand (except `--doctor`, `--mint`, `--tmux`). |
44
+ | `taskflow-hook` | The Claude Code lifecycle hook (see [Hooks](#hooks-claude-code-only)). |
45
+
46
+ ## `taskflow init`
47
+
48
+ `taskflow init` (also spelled `taskflow-mcp init`) walks you through the setup:
49
+
50
+ 1. **Pick your coding agent(s)** — Claude Code, Codex CLI, Gemini CLI, Cursor,
51
+ opencode. Ones found on this machine are pre-selected.
52
+ 2. **Pick a scope** — `user` (global: every project on this machine) or `project`
53
+ (only the current repo, in files you can commit).
54
+ 3. **Register the `taskflow` MCP server** with each agent — through the agent's own
55
+ CLI when it is on your PATH, otherwise by merging its config file.
56
+ 4. **Claude Code only:** offer the [hooks](#hooks-claude-code-only).
57
+ 5. **Credentials** — find `.taskflow.json`, or create it by linking an agent when
58
+ you pass `--project` and your user `--token`; otherwise it tells you how.
59
+ 6. **Check** the connection (the same check as `taskflow-mcp --doctor`).
60
+ 7. **Next steps**, ending with signing up / logging in to the TaskFlow web app —
61
+ <https://taskflow.supercodehive.com>, or your own frontend if you self-host.
62
+
63
+ It is safe to re-run: nothing that is already registered is added twice, an
64
+ existing `taskflow` entry is never overwritten (even if you customised it), other
65
+ servers and settings are left untouched, and every file it edits is first backed
66
+ up to `<file>.bak`. A file it cannot parse safely (for example JSON with comments)
67
+ is not touched — you get the exact lines to add by hand instead.
23
68
 
24
- ## How it works
69
+ **Non-interactive** (scripts, CI, dotfiles):
25
70
 
26
- - **`.taskflow.json`** (per repo, gitignored) holds the server URL, project id, a
27
- `default_profile`, and one or more named `profiles`. Each profile has an
28
- `agent_id`, a `key` (the `tfk_…` credential), and a `display_name`.
71
+ ```bash
72
+ taskflow init --harness claude --scope user --yes
73
+ taskflow init --harness claude,codex --scope project --dry-run # show the plan + diffs, change nothing
74
+ taskflow init --harness all --yes --project 3 --token "$TASKFLOW_USER_TOKEN"
75
+ ```
76
+
77
+ | Option | Meaning |
78
+ | --- | --- |
79
+ | `--harness <names>` | `claude`, `codex`, `gemini`, `cursor`, `opencode` — comma-separated, repeatable, or `all`. Required with `--yes`. |
80
+ | `--scope user\|project` | Global, or this repo only (default `user`). |
81
+ | `--dir <path>` | Project root for `--scope project` and `.taskflow.json` (default: cwd). |
82
+ | `--hooks` / `--no-hooks` | Install the Claude Code hooks (default: yes). |
83
+ | `--project <id>` + `--token <t>` | Link a new agent and write `.taskflow.json`. The token is **your user token** (or `TASKFLOW_USER_TOKEN`), not an agent key. |
84
+ | `--server <url>` | API for the new `.taskflow.json` (default `https://api.taskflow.supercodehive.com`). |
85
+ | `--profile <name>`, `--display-name <s>` | Name the linked agent (default profile `main`). |
86
+ | `--app-url <url>` | Your web app, when self-hosting (printed in the next steps). |
87
+ | `--dry-run` | Print what would be run/written, with diffs. Changes nothing. |
88
+ | `--no-doctor` | Skip the connection check. |
89
+ | `-y`, `--yes` | Never prompt; accept defaults. |
90
+
91
+ ### What it does per agent
92
+
93
+ | Agent | `user` scope | `project` scope | How |
94
+ | --- | --- | --- | --- |
95
+ | Claude Code | Claude's user config | `.mcp.json` | `claude mcp add --scope <scope> taskflow -- taskflow-mcp`. Without the `claude` CLI: project scope writes `.mcp.json`; user scope prints the command (it never edits `~/.claude.json` directly). |
96
+ | Codex CLI | `~/.codex/config.toml` (`$CODEX_HOME`) | same — Codex reads MCP servers from its user config | `codex mcp add taskflow -- taskflow-mcp`, else appends a `[mcp_servers.taskflow]` table. |
97
+ | Gemini CLI | `~/.gemini/settings.json` | `.gemini/settings.json` | Merges `mcpServers.taskflow`. |
98
+ | Cursor | `~/.cursor/mcp.json` | `.cursor/mcp.json` | Merges `mcpServers.taskflow`. |
99
+ | opencode | `~/.config/opencode/opencode.json` | `opencode.json` | Merges `mcp.taskflow` (opencode's own schema). An `opencode.jsonc` is left alone. |
100
+
101
+ ## Manual setup (any MCP client)
102
+
103
+ If you would rather do it by hand, or use another client: register a stdio server
104
+ named `taskflow` whose command is `taskflow-mcp` (no arguments), then create
105
+ `.taskflow.json` (below). The server must start in your repo (or set
106
+ `TASKFLOW_CONFIG=/abs/path/.taskflow.json` in its environment), because that is
107
+ where it looks for credentials.
108
+
109
+ **Claude Code**
110
+
111
+ ```bash
112
+ claude mcp add --scope user taskflow -- taskflow-mcp # or --scope project (writes .mcp.json)
113
+ ```
114
+
115
+ ```json
116
+ // .mcp.json (project scope)
117
+ { "mcpServers": { "taskflow": { "command": "taskflow-mcp", "args": [] } } }
118
+ ```
119
+
120
+ **Codex CLI**
121
+
122
+ ```bash
123
+ codex mcp add taskflow -- taskflow-mcp
124
+ ```
125
+
126
+ ```toml
127
+ # ~/.codex/config.toml
128
+ [mcp_servers.taskflow]
129
+ command = "taskflow-mcp"
130
+ args = []
131
+ ```
132
+
133
+ **Gemini CLI** — `~/.gemini/settings.json` or `.gemini/settings.json`; **Cursor** —
134
+ `~/.cursor/mcp.json` or `.cursor/mcp.json`:
135
+
136
+ ```json
137
+ { "mcpServers": { "taskflow": { "command": "taskflow-mcp", "args": [] } } }
138
+ ```
139
+
140
+ **opencode** — `~/.config/opencode/opencode.json` or `opencode.json`:
141
+
142
+ ```json
143
+ {
144
+ "$schema": "https://opencode.ai/config.json",
145
+ "mcp": { "taskflow": { "type": "local", "command": ["taskflow-mcp"], "enabled": true } }
146
+ }
147
+ ```
148
+
149
+ **Other clients** (Windsurf, Cline, Zed, …): the same stdio server — command
150
+ `taskflow-mcp`, no arguments — in whatever format the client uses.
151
+
152
+ ## Credentials: `.taskflow.json` and profiles
153
+
154
+ - **`.taskflow.json`** (per repo, **gitignored** — it holds secret keys) has the
155
+ server URL, project id, a `default_profile`, and one or more named `profiles`.
156
+ Each profile has an `agent_id`, a `key` (the `tfk_…` credential), and a
157
+ `display_name`.
29
158
  - Every backend call sends `Authorization: Agent <key>`. The chosen profile's key
30
159
  is the whole identity — the server derives the agent, project, and display name
31
160
  from it.
32
- - **Profile selection** (highest priority first): a tool's `profile` argument →
33
- `TASKFLOW_PROFILE` env → the file's `default_profile` → `"main"`.
34
-
35
- ## `.taskflow.json` format
161
+ - **Profile selection** for the MCP server (highest priority first): a tool's
162
+ `profile` argument → `TASKFLOW_PROFILE` env → this terminal's remembered pick
163
+ (from `select_profile`, kept in `.taskflow/sessions.json`) → the only profile, if
164
+ there is just one. With several profiles and nothing choosing one, the agent asks
165
+ you which identity this terminal is (`default_profile` is shown as the
166
+ recommended pick). The hook and the CLI modes use `TASKFLOW_PROFILE` →
167
+ `default_profile` → `"main"`.
36
168
 
37
169
  ```json
38
170
  {
39
- "server": "http://localhost:8000",
171
+ "server": "https://api.taskflow.supercodehive.com",
40
172
  "project": 1,
41
173
  "default_profile": "main",
42
174
  "profiles": {
@@ -46,43 +178,42 @@ This puts two commands on your PATH:
46
178
  }
47
179
  ```
48
180
 
49
- Resolution: the file is read from `TASKFLOW_CONFIG` if set, otherwise found by
50
- walking up from the working directory. See `.taskflow.json.example`.
51
-
52
- ## Setup
53
-
54
- 1. **Install the package** globally so `taskflow-mcp` and `taskflow-hook` are on
55
- your PATH:
56
- ```bash
57
- npm install -g @dalmasonto/taskflow-mcp
58
- ```
59
- 2. **Link an agent.** In the TaskFlow dashboard open your project's **API Base**
60
- page and link an agent (profile `main`, and optionally `reviewer`). It returns
61
- a block containing the `agent_id`, raw `key`, and `display_name` — shown once.
62
- 3. **Create `.taskflow.json`** at your repo root and paste the returned profile
63
- block(s) under `profiles` (see the format above and `.taskflow.json.example`).
64
- **Do not commit it** — add `.taskflow.json` to your `.gitignore`.
65
- 4. **Register the MCP server** with Claude Code — copy `.mcp.json.example` to
66
- `.mcp.json` at your repo root:
67
- ```json
68
- {
69
- "mcpServers": {
70
- "taskflow": { "command": "taskflow-mcp", "args": [] }
71
- }
72
- }
73
- ```
74
- 5. **Add the hooks** (optional but recommended) — see [Hooks](#hooks) below.
75
-
76
- **From a local checkout** (contributing, or running an unpublished build) instead
77
- of the global install: `cd mcp && npm install && npm run build`, then point the
78
- MCP `command` at `node` with `args: ["./mcp/dist/index.js"]`, and the hook at
79
- `node ABS_PATH/mcp/hooks/taskflow-hook.mjs`.
80
-
81
- ## Hooks
82
-
83
- The Claude Code hook (`taskflow-hook`) turns the agent's own lifecycle into real,
84
- attributable activity on the TaskFlow board — no prompting required. On each event
85
- it resolves your `.taskflow.json` + profile and POSTs to the backend:
181
+ `server` is the **backend** origin — the hosted API above, or e.g.
182
+ `http://localhost:8000` for a local backend — never a frontend dev server.
183
+ The file is read from `TASKFLOW_CONFIG` if set, otherwise found by walking up from
184
+ the working directory. See `.taskflow.example.json` in the repo.
185
+
186
+ **Getting one:**
187
+
188
+ 1. Sign up / log in at <https://taskflow.supercodehive.com> (or your self-hosted
189
+ frontend) and create or open a project.
190
+ 2. Open the project's **API Base** page and link an agent (profile `main`, and
191
+ optionally `reviewer`). It shows a block with the `agent_id`, raw `key`, and
192
+ `display_name` — **once**.
193
+ 3. Save it as `.taskflow.json` at your repo root and add `.taskflow.json` to
194
+ `.gitignore`.
195
+
196
+ Or let `taskflow init --project <id> --token <your user token>` link the agent
197
+ and write the file (and the `.gitignore` entry) for you.
198
+
199
+ **A second identity** for another terminal (two terminals sharing `main` are ONE
200
+ agent — one roster row, one DM inbox, one read cursor):
201
+
202
+ ```bash
203
+ taskflow mint bear --display-name "Claude (bear)" # = taskflow-mcp --mint bear ...
204
+ export TASKFLOW_PROFILE=bear # then start that terminal's agent
205
+ ```
206
+
207
+ It needs your user token (`--token` or `TASKFLOW_USER_TOKEN`). An existing profile
208
+ is never overwritten and `default_profile` never moves.
209
+
210
+ ## Hooks (Claude Code only)
211
+
212
+ Hooks are a **Claude Code** feature; other agents get the same tools through MCP,
213
+ just without this automatic lifecycle reporting. The hook (`taskflow-hook`) turns
214
+ the agent's own lifecycle into real, attributable activity on the TaskFlow board —
215
+ no prompting required. On each event it resolves your `.taskflow.json` + profile
216
+ and POSTs to the backend:
86
217
 
87
218
  - **SessionStart** → registers/reconnects your live session (you show "online").
88
219
  - **PreToolUse / PostToolUse** → logs meaningful tool calls as activity. Read-only
@@ -91,13 +222,13 @@ it resolves your `.taskflow.json` + profile and POSTs to the backend:
91
222
  - **Stop** → closes the session cleanly.
92
223
  - **Notification** → surfaces permission prompts so a human can answer from the UI.
93
224
 
94
- It is **best-effort and never blocks or crashes the agent**: with no `.taskflow.json`,
95
- or with the backend unreachable, every invocation swallows the error and exits `0`
96
- in well under its short timeout.
225
+ It is **best-effort and never blocks or crashes the agent**: with no
226
+ `.taskflow.json`, or with the backend unreachable, every invocation swallows the
227
+ error and exits `0` in well under its short timeout.
97
228
 
98
- **Wire it up** — copy the `hooks` block from `.claude/settings.example.json` into
99
- your project's `.claude/settings.json`. With the global install the command is just
100
- `taskflow-hook`:
229
+ `taskflow init` adds them for you (to `~/.claude/settings.json` for `user` scope,
230
+ `.claude/settings.json` for `project`), appending after any hooks you already
231
+ have. By hand:
101
232
 
102
233
  ```json
103
234
  {
@@ -115,36 +246,85 @@ The hook reads `TASKFLOW_PROFILE` (else `default_profile`, else `main`) and find
115
246
  `.taskflow.json` by walking up from the working directory (or `TASKFLOW_CONFIG`).
116
247
  Set `TASKFLOW_HOOK_DEBUG=1` to see why a hook no-oped on stderr.
117
248
 
249
+ ## Terminal mirroring
250
+
251
+ When the agent runs inside **tmux**, the server finds its own pane and streams it
252
+ to the dashboard automatically. Set `TASKFLOW_MIRROR=off` to disable it. Without
253
+ tmux the agent still connects and appears online; only the streamed terminal needs
254
+ a pane. `taskflow-mcp --tmux [target]` mirrors a pane the agent is *not* running
255
+ in (see `taskflow-mcp --help`).
256
+
118
257
  ## MCP tools
119
258
 
259
+ The tools are called by the **model** through the MCP client, not typed as
260
+ commands. Every tool accepts an optional `profile` argument to act as a different
261
+ profile for that one call (e.g. `reviewer` for `report_review`).
262
+
120
263
  | Tool | What it does |
121
264
  | --- | --- |
122
- | `whoami` | Confirm the agent identity + project behind the credential. |
265
+ | `whoami` | Confirm the agent identity, project, connection and terminal-mirror state. |
266
+ | `select_profile(profile)` | Choose which identity this terminal is, when the repo defines several (after asking the human). |
123
267
  | `list_tasks(status?, assigned?)` | List project tasks; `assigned='me'` for claimed. |
124
268
  | `create_task(title, description?, priority?, claim?)` | Create (optionally claim) a task. |
269
+ | `update_task(task, …)` | Edit a task's title, description, notes, priority, attachments. |
125
270
  | `update_task_status(task, status)` | Advance a task's status. |
126
271
  | `claim_task(task)` | Self-assign a task. |
127
272
  | `report_review(task, decision, body?)` | Record a review (`approved`/`changes_requested`). |
128
273
  | `list_channels` | Channels this agent can see. |
129
274
  | `list_agents` | Other agents in the project. |
130
275
  | `send_message(channel, body, priority?)` | Post a chat message as this agent. |
131
- | `check_messages(channel, since?)` | Read a channel's messages + read cursor. |
276
+ | `check_messages(…)` | Read unread messages addressed to this agent. |
132
277
  | `mark_read(channel, last_read_message)` | Advance this agent's read cursor. |
133
- | `register_session(session_identifier?, cwd?)` | Register/reconnect a live session. |
134
- | `heartbeat(status?)` | Bump session liveness (`idle`/`busy`). |
278
+ | `download_attachment(url)` | Save a message attachment to disk; returns its path. |
279
+ | `register_session(session_identifier?, cwd?)` | Register/reconnect a live session (automatic; rarely needed). |
280
+ | `heartbeat(status?)` | Bump session liveness (automatic; rarely needed). |
135
281
  | `capture_terminal(content, stream?)` | Stream terminal output into the session. |
136
282
  | `log_activity(action, body?, task?)` | Log a real activity event. |
137
283
  | `get_activity(task?, limit?)` | Read recent project activity. |
284
+ | `design_get_tokens`, `design_write_tokens` | Read / replace the design token scale. |
285
+ | `design_list_components`, `design_read_component`, `design_write_component`, `design_delete_component` | The design component registry. |
286
+ | `design_read_page`, `design_write_page`, `design_delete_page` | Design pages (delete moves to the trash). |
287
+ | `design_read_layout`, `design_create_group`, `design_update_group`, `design_reorder_group`, `design_reorder_page`, `design_delete_group`, `design_arrange` | How design pages are grouped and ordered. |
288
+ | `design_write_asset` | Write an image or other non-page asset. |
289
+ | `design_screenshot` | Render a route in headless Chromium and return a PNG. |
290
+ | `design_list_comments`, `design_resolve_comment` | Operator comments on the rendered design. |
291
+
292
+ ## Troubleshooting
293
+
294
+ ```bash
295
+ taskflow-mcp --doctor # or: taskflow doctor / taskflow-mcp --check
296
+ ```
297
+
298
+ It finds `.taskflow.json`, validates it, authenticates every profile against the
299
+ backend, and prints the fix for each failure (missing file, wrong/revoked key,
300
+ nothing listening at `server`, a frontend URL where the backend belongs). It exits
301
+ non-zero when the default profile cannot authenticate.
302
+
303
+ - **The agent doesn't see the `taskflow` tools** — restart the agent after
304
+ `taskflow init`; check the registration with its own tooling (e.g. `claude mcp
305
+ list`, `codex mcp list`, `/mcp` in Gemini CLI or opencode).
306
+ - **`taskflow-mcp: command not found`** — npm's global bin is not on PATH
307
+ (`npm prefix -g` shows where it is; add its `bin` to PATH).
308
+ - **Config not found** although the file exists — the agent started the server
309
+ outside your repo. Set `TASKFLOW_CONFIG` to the file's absolute path in the MCP
310
+ server's `env`.
311
+ - **`profile_ambiguous`** — the repo has several profiles; answer the agent's
312
+ question, or set `TASKFLOW_PROFILE`.
313
+
314
+ ## From a local checkout
138
315
 
139
- Every tool also accepts an optional `profile` argument to act as a different
140
- profile for that one call (e.g. use `reviewer` for `report_review`).
316
+ Contributing, or running an unpublished build, instead of the global install:
317
+ `cd mcp && npm install && npm run build`, then point the MCP `command` at `node`
318
+ with `args: ["./mcp/dist/index.js"]`, the hook at
319
+ `node ABS_PATH/mcp/hooks/taskflow-hook.mjs`, and run the walkthrough with
320
+ `node mcp/dist/cli.js init`.
141
321
 
142
322
  ## Development
143
323
 
144
324
  ```bash
145
325
  npm run typecheck # tsc --noEmit
146
326
  npm run build # compile to dist/
147
- npm test # vitest (config resolution unit tests)
327
+ npm test # vitest
148
328
  ```
149
329
 
150
330
  ## Smoke test (against a real backend)
package/dist/cli.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * The `taskflow` command — the human-facing CLI that ships next to the
4
+ * `taskflow-mcp` server bin. It never serves MCP; harnesses run `taskflow-mcp`.
5
+ *
6
+ * taskflow init Post-install walkthrough (also: taskflow-mcp init).
7
+ * taskflow doctor Same as taskflow-mcp --doctor.
8
+ * taskflow mint ... Same as taskflow-mcp --mint ...
9
+ */
10
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * The `taskflow` command — the human-facing CLI that ships next to the
4
+ * `taskflow-mcp` server bin. It never serves MCP; harnesses run `taskflow-mcp`.
5
+ *
6
+ * taskflow init Post-install walkthrough (also: taskflow-mcp init).
7
+ * taskflow doctor Same as taskflow-mcp --doctor.
8
+ * taskflow mint ... Same as taskflow-mcp --mint ...
9
+ */
10
+ import { createRequire } from "node:module";
11
+ import { runInitCommand } from "./init.js";
12
+ import { runDoctor } from "./doctor.js";
13
+ import { runMint } from "./mint.js";
14
+ const USAGE = `taskflow — set up and check TaskFlow for your coding agents
15
+
16
+ taskflow init [options] Connect Claude Code, Codex, Gemini CLI, Cursor or
17
+ opencode to TaskFlow (see: taskflow init --help).
18
+ taskflow doctor Verify .taskflow.json + backend auth.
19
+ taskflow mint <name> [--display-name <s>] [--token <t>]
20
+ Create another agent identity in .taskflow.json.
21
+ taskflow --version Print the package version.
22
+
23
+ The MCP server itself is the taskflow-mcp command, which your agent runs.`;
24
+ function version() {
25
+ const require = createRequire(import.meta.url);
26
+ return require("../package.json").version;
27
+ }
28
+ async function main() {
29
+ const [cmd, ...rest] = process.argv.slice(2);
30
+ switch (cmd) {
31
+ case "init":
32
+ return runInitCommand(rest);
33
+ case "doctor":
34
+ case "check":
35
+ return runDoctor();
36
+ case "mint":
37
+ for (const line of await runMint(["--mint", ...rest]))
38
+ process.stdout.write(`${line}\n`);
39
+ return 0;
40
+ case "-v":
41
+ case "--version":
42
+ process.stdout.write(`${version()}\n`);
43
+ return 0;
44
+ case undefined:
45
+ case "help":
46
+ case "-h":
47
+ case "--help":
48
+ process.stdout.write(`${USAGE}\n`);
49
+ return 0;
50
+ default:
51
+ process.stderr.write(`taskflow: unknown command "${cmd}"\n\n${USAGE}\n`);
52
+ return 1;
53
+ }
54
+ }
55
+ main().then((code) => process.exit(code), (err) => {
56
+ process.stderr.write(`taskflow: ${err.message}\n`);
57
+ process.exit(1);
58
+ });
59
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,KAAK,GAAG;;;;;;;;;0EAS4D,CAAC;AAE3E,SAAS,OAAO;IACd,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,OAAQ,OAAO,CAAC,iBAAiB,CAAyB,CAAC,OAAO,CAAC;AACrE,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7C,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM;YACT,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;QAC9B,KAAK,QAAQ,CAAC;QACd,KAAK,OAAO;YACV,OAAO,SAAS,EAAE,CAAC;QACrB,KAAK,MAAM;YACT,KAAK,MAAM,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,CAAC;gBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;YACzF,OAAO,CAAC,CAAC;QACX,KAAK,IAAI,CAAC;QACV,KAAK,WAAW;YACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,EAAE,IAAI,CAAC,CAAC;YACvC,OAAO,CAAC,CAAC;QACX,KAAK,SAAS,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,IAAI,CAAC;QACV,KAAK,QAAQ;YACX,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;YACnC,OAAO,CAAC,CAAC;QACX;YACE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC;YACzE,OAAO,CAAC,CAAC;IACb,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,IAAI,CACT,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAC5B,CAAC,GAAU,EAAE,EAAE;IACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;IACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CACF,CAAC"}
package/dist/client.d.ts CHANGED
@@ -32,6 +32,15 @@ export interface TaskflowClientOptions {
32
32
  timeoutMs?: number;
33
33
  }
34
34
  /** An error carrying the backend's status + parsed detail. */
35
+ /** Optional `design_screenshot` controls; see the tool's description. */
36
+ export type DesignScreenshotOptions = {
37
+ width?: number;
38
+ height?: number;
39
+ dpr?: number;
40
+ mobile?: boolean;
41
+ full_page?: boolean;
42
+ frame?: "none" | "classic" | "device";
43
+ };
35
44
  export declare class TaskflowApiError extends Error {
36
45
  readonly status: number;
37
46
  readonly detail: string;
@@ -84,6 +93,8 @@ export interface SendMessageInput {
84
93
  filename: string;
85
94
  bytes: Buffer;
86
95
  }[];
96
+ /** #317: the id of the message this one replies to (same channel). */
97
+ reply_to?: number;
87
98
  }
88
99
  export interface CreateTaskInput {
89
100
  title: string;
@@ -156,6 +167,34 @@ export type DesignLayoutOp = {
156
167
  group_id?: string;
157
168
  position?: number;
158
169
  };
170
+ } | {
171
+ delete_group: {
172
+ group_id: string;
173
+ };
174
+ } | {
175
+ link_pages: {
176
+ from: string;
177
+ to: string;
178
+ label?: string;
179
+ };
180
+ } | {
181
+ unlink_pages: {
182
+ edge_id: string;
183
+ } | {
184
+ from: string;
185
+ to: string;
186
+ };
187
+ } | {
188
+ update_link: {
189
+ edge_id: string;
190
+ label?: string;
191
+ };
192
+ } | {
193
+ place_page: {
194
+ route: string;
195
+ x: number;
196
+ y: number;
197
+ };
159
198
  };
160
199
  export declare class TaskflowClient {
161
200
  private readonly server;
@@ -253,9 +292,12 @@ export declare class TaskflowClient {
253
292
  * — omitted, the server works from whatever it currently holds. */
254
293
  writeLayoutOp(input: {
255
294
  project: number;
256
- op: DesignLayoutOp;
257
295
  base_version?: number;
258
- }): Promise<unknown>;
296
+ } & ({
297
+ op: DesignLayoutOp;
298
+ } | {
299
+ ops: DesignLayoutOp[];
300
+ })): Promise<unknown>;
259
301
  /** `GET /agents/design/page?route=` — one page fragment + version. */
260
302
  readDesignPage(project: number, route: string): Promise<unknown>;
261
303
  /** `GET /agents/design/component?name=` — source + blast radius. */
@@ -269,6 +311,9 @@ export declare class TaskflowClient {
269
311
  * names the routes, so this throws a `component_in_use` detail rather than
270
312
  * deleting and stranding a page. */
271
313
  deleteDesignComponent(project: number, name: string, reason: string): Promise<unknown>;
314
+ /** `DELETE /agents/design/page` — #501: move a page to the trash. The
315
+ * operator can restore it; it is not a hard delete. */
316
+ trashDesignPage(project: number, route: string, reason: string): Promise<unknown>;
272
317
  /** `PUT /agents/design/asset` — one image under `assets/`, or the external
273
318
  * resources document `styles/resources.json`. Content is text; raster bytes
274
319
  * are `data:<mime>;base64,<payload>`. */
@@ -279,10 +324,19 @@ export declare class TaskflowClient {
279
324
  tokens?: unknown;
280
325
  css?: string;
281
326
  }): Promise<unknown>;
282
- /** `GET /agents/design/screenshot` — render a route; `{png_base64}`. */
283
- designScreenshot(project: number, route: string, viewport: string, state?: string): Promise<{
327
+ /** `GET /agents/design/screenshot` — render a route; `{png_base64, warnings}`. */
328
+ designScreenshot(project: number, route: string, viewport: string, state?: string, opts?: DesignScreenshotOptions): Promise<{
284
329
  route: string;
285
330
  viewport: string;
331
+ size?: {
332
+ width: number;
333
+ height: number;
334
+ dpr: number;
335
+ mobile: boolean;
336
+ };
337
+ frame?: string;
338
+ full_page?: boolean;
339
+ warnings?: string[];
286
340
  mime: string;
287
341
  png_base64: string;
288
342
  }>;
package/dist/client.js CHANGED
@@ -10,7 +10,6 @@
10
10
  * `fetchImpl` is injectable so the client can be unit-tested against a stub
11
11
  * without a live backend.
12
12
  */
13
- /** An error carrying the backend's status + parsed detail. */
14
13
  export class TaskflowApiError extends Error {
15
14
  status;
16
15
  detail;
@@ -187,6 +186,8 @@ export class TaskflowClient {
187
186
  form.set("client_nonce", fields.client_nonce);
188
187
  if (fields.is_design)
189
188
  form.set("is_design", "true");
189
+ if (fields.reply_to !== undefined)
190
+ form.set("reply_to", String(fields.reply_to));
190
191
  for (const file of attachments) {
191
192
  // The server treats a part as a file only when it carries a non-empty
192
193
  // filename (views.rs:180-186), so the basename must be preserved.
@@ -328,7 +329,7 @@ export class TaskflowClient {
328
329
  // Absent, not `null`: the route reads this as `Option<i64>`, and "not
329
330
  // supplied" is a different thing from "supplied as nothing".
330
331
  ...(input.base_version === undefined ? {} : { base_version: input.base_version }),
331
- op: input.op,
332
+ ...("ops" in input ? { ops: input.ops } : { op: input.op }),
332
333
  },
333
334
  });
334
335
  }
@@ -378,6 +379,13 @@ export class TaskflowClient {
378
379
  body: { project, name, reason },
379
380
  });
380
381
  }
382
+ /** `DELETE /agents/design/page` — #501: move a page to the trash. The
383
+ * operator can restore it; it is not a hard delete. */
384
+ trashDesignPage(project, route, reason) {
385
+ return this.request("DELETE", `${API_PREFIX}/agents/design/page`, {
386
+ body: { project, route, reason },
387
+ });
388
+ }
381
389
  /** `PUT /agents/design/asset` — one image under `assets/`, or the external
382
390
  * resources document `styles/resources.json`. Content is text; raster bytes
383
391
  * are `data:<mime>;base64,<payload>`. */
@@ -403,10 +411,14 @@ export class TaskflowClient {
403
411
  },
404
412
  });
405
413
  }
406
- /** `GET /agents/design/screenshot` — render a route; `{png_base64}`. */
407
- designScreenshot(project, route, viewport, state) {
414
+ /** `GET /agents/design/screenshot` — render a route; `{png_base64, warnings}`. */
415
+ designScreenshot(project, route, viewport, state, opts = {}) {
416
+ const extra = {};
417
+ for (const [key, value] of Object.entries(opts))
418
+ if (value !== undefined)
419
+ extra[key] = value;
408
420
  return this.request("GET", `${API_PREFIX}/agents/design/screenshot`, {
409
- query: { project, route, viewport, ...(state ? { state } : {}) },
421
+ query: { project, route, viewport, ...(state ? { state } : {}), ...extra },
410
422
  timeoutMs: 45_000,
411
423
  });
412
424
  }