cawdev-cli 1.0.10-beta → 1.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.
- package/README.md +11 -1
- package/lib/background-shells.mjs +45 -3
- package/lib/roadmap-format.mjs +31 -8
- package/lib/stage-tools.mjs +2 -0
- package/mcp/README.md +44 -30
- package/mcp/server.mjs +113 -49
- package/package.json +1 -1
- package/runner/README.md +29 -27
- package/runner/attach.mjs +19 -8
- package/runner/git-host.mjs +263 -0
- package/runner/runner.mjs +324 -117
- package/runner/split.mjs +46 -6
- package/runner/workspace-check.mjs +78 -0
package/README.md
CHANGED
|
@@ -10,4 +10,14 @@ https://github.com/4dali/cawdev-cli
|
|
|
10
10
|
|
|
11
11
|
`jira/import.mjs` brings a Jira Cloud project's issues, epics, sprints and
|
|
12
12
|
comments into a cawdev project, once and again without duplicates (R337). Run from a checkout; see
|
|
13
|
-
"Leaving Jira" in GUIDE.md.
|
|
13
|
+
"Leaving Jira" in GUIDE.md. `--team <team id>` (R489) names the team the sprints open in, when the project is
|
|
14
|
+
shared with several writer teams. Its tests: `node --test "tools/jira/*.test.mjs"`.
|
|
15
|
+
|
|
16
|
+
## The `Sprint:` line
|
|
17
|
+
|
|
18
|
+
A card in a sprint carries one `Sprint:` line in `ROADMAP.md` and `ISSUES.md`
|
|
19
|
+
(`lib/roadmap-format.mjs`): `Sprint: Core team · S7 Notifications` since sprints
|
|
20
|
+
became a team's (R489). Files written before that, `Sprint: S7 Notifications`,
|
|
21
|
+
still parse (with no team). `roadmap/import.mjs` sends the words
|
|
22
|
+
(`Core team · S7`) for the first form and the number for the second, so a file
|
|
23
|
+
naming a sprint the target does not have is refused with the API's own sentence.
|
|
@@ -17,6 +17,15 @@
|
|
|
17
17
|
* - `BashOutput` (or `TaskOutput`) reads one by `bash_id`/`shell_id`/`task_id`;
|
|
18
18
|
* the command is over only when a read says a status other than running;
|
|
19
19
|
* - `KillShell`/`KillBash`/`TaskStop` ends one.
|
|
20
|
+
* And a delegation started in the background: `Agent` (or `Task`) with
|
|
21
|
+
* `run_in_background: true`, or whose result says "Async agent launched" —
|
|
22
|
+
* its id is the result's `agentId: <id>`. It is over when a `<task-notification>`
|
|
23
|
+
* naming that id arrives with a status other than running, when a
|
|
24
|
+
* `TaskOutput` read says so, or when `TaskStop` ends it. R6 on saasng is why:
|
|
25
|
+
* IMPLEMENT handed the whole card to two background agents, said "both agents
|
|
26
|
+
* are still working in the background" and ended its turn; the walk marched on
|
|
27
|
+
* and the run FINISHED with nothing built.
|
|
28
|
+
*
|
|
20
29
|
* A command whose id cannot be read off its result stays unfinished: failing
|
|
21
30
|
* loudly and re-running a stage costs a turn, calling unproven work proven
|
|
22
31
|
* costs somebody's afternoon.
|
|
@@ -26,6 +35,7 @@
|
|
|
26
35
|
*/
|
|
27
36
|
|
|
28
37
|
const READS = new Set(['BashOutput', 'TaskOutput']);
|
|
38
|
+
const DELEGATES = new Set(['Agent', 'Task']);
|
|
29
39
|
const KILLS = new Set(['KillShell', 'KillBash', 'TaskStop']);
|
|
30
40
|
|
|
31
41
|
/** The text of a tool_result's content, whichever of its two shapes it came in. */
|
|
@@ -44,15 +54,19 @@ function shellIdOf(input) {
|
|
|
44
54
|
|
|
45
55
|
export class BackgroundShells {
|
|
46
56
|
constructor() {
|
|
47
|
-
/** tool_use id of each background `Bash` → { command, shellId, done }. */
|
|
57
|
+
/** tool_use id of each background `Bash` or `Agent` → { command, shellId, done }. */
|
|
48
58
|
this.started = new Map();
|
|
59
|
+
/** tool_use id of each `Agent` call not yet known to be in the background → its label and input. */
|
|
60
|
+
this.delegations = new Map();
|
|
49
61
|
/** tool_use id of each read → the shell id it read. */
|
|
50
62
|
this.reads = new Map();
|
|
51
63
|
}
|
|
52
64
|
|
|
53
65
|
/** One stream-json event. Anything that is not about a shell is ignored. */
|
|
54
66
|
observe(event) {
|
|
55
|
-
const
|
|
67
|
+
const content = event?.message?.content;
|
|
68
|
+
if (event?.type === 'user') this.notified(textOf(content));
|
|
69
|
+
const parts = content;
|
|
56
70
|
if (!Array.isArray(parts)) return;
|
|
57
71
|
if (event.type === 'assistant') {
|
|
58
72
|
for (const part of parts) {
|
|
@@ -63,6 +77,9 @@ export class BackgroundShells {
|
|
|
63
77
|
shellId: null,
|
|
64
78
|
done: false,
|
|
65
79
|
});
|
|
80
|
+
} else if (DELEGATES.has(part.name)) {
|
|
81
|
+
const label = `Agent: ${String(part.input?.description ?? part.input?.subagent_type ?? 'a delegation').trim()}`;
|
|
82
|
+
this.delegations.set(part.id, { label, background: part.input?.run_in_background === true });
|
|
66
83
|
} else if (READS.has(part.name)) {
|
|
67
84
|
const id = shellIdOf(part.input);
|
|
68
85
|
if (id) this.reads.set(part.id, id);
|
|
@@ -76,6 +93,17 @@ export class BackgroundShells {
|
|
|
76
93
|
for (const part of parts) {
|
|
77
94
|
if (part?.type !== 'tool_result') continue;
|
|
78
95
|
const text = textOf(part.content);
|
|
96
|
+
const delegation = this.delegations.get(part.tool_use_id);
|
|
97
|
+
if (delegation) {
|
|
98
|
+
this.delegations.delete(part.tool_use_id);
|
|
99
|
+
// A foreground delegation's result IS its end; only one that came
|
|
100
|
+
// back as "launched" is still out there.
|
|
101
|
+
if (delegation.background || /async agent launched/i.test(text)) {
|
|
102
|
+
const id = /\bagentId:?\s*([A-Za-z0-9_-]+)/i.exec(text);
|
|
103
|
+
this.started.set(part.tool_use_id, { command: delegation.label, shellId: id ? id[1] : null, done: false });
|
|
104
|
+
}
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
79
107
|
const launched = this.started.get(part.tool_use_id);
|
|
80
108
|
if (launched && !launched.shellId) {
|
|
81
109
|
const id = /\bID:?\s*([A-Za-z0-9_-]+)/.exec(text);
|
|
@@ -94,6 +122,20 @@ export class BackgroundShells {
|
|
|
94
122
|
}
|
|
95
123
|
}
|
|
96
124
|
|
|
125
|
+
/**
|
|
126
|
+
* The CLI tells a session that a background task ended with a
|
|
127
|
+
* `<task-notification>` in a user turn — `<task-id>` and `<status>`. Any
|
|
128
|
+
* status but running is over: a failed or killed agent is not one to wait for.
|
|
129
|
+
*/
|
|
130
|
+
notified(text) {
|
|
131
|
+
if (!text || !text.includes('<task-notification>')) return;
|
|
132
|
+
for (const block of text.split('<task-notification>').slice(1)) {
|
|
133
|
+
const id = /<task-id>\s*([^<\s]+)\s*<\/task-id>/.exec(block);
|
|
134
|
+
const status = /<status>\s*([^<\s]+)\s*<\/status>/.exec(block);
|
|
135
|
+
if (id && status && status[1].toLowerCase() !== 'running') this.finish(id[1]);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
97
139
|
finish(shellId) {
|
|
98
140
|
if (!shellId) return;
|
|
99
141
|
for (const each of this.started.values()) {
|
|
@@ -101,7 +143,7 @@ export class BackgroundShells {
|
|
|
101
143
|
}
|
|
102
144
|
}
|
|
103
145
|
|
|
104
|
-
/** The commands started in the background whose end this session never saw. */
|
|
146
|
+
/** The commands and delegations started in the background whose end this session never saw. */
|
|
105
147
|
unfinished() {
|
|
106
148
|
return [...this.started.values()].filter((each) => !each.done).map((each) => each.command);
|
|
107
149
|
}
|
package/lib/roadmap-format.mjs
CHANGED
|
@@ -253,6 +253,12 @@ export function parseRoadmap(text) {
|
|
|
253
253
|
entry.targetVersion = target[1].trim();
|
|
254
254
|
continue;
|
|
255
255
|
}
|
|
256
|
+
// R378. The versions an issue was found in, comma separated.
|
|
257
|
+
const affects = /^Affects:\s*(.+)$/.exec(line.trim());
|
|
258
|
+
if (affects) {
|
|
259
|
+
entry.affectedVersions = affects[1].split(',').map((v) => v.trim()).filter(Boolean);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
256
262
|
const estimate = /^Estimate:\s*(.+)$/.exec(line.trim());
|
|
257
263
|
if (estimate) {
|
|
258
264
|
const n = Number(estimate[1].trim());
|
|
@@ -326,14 +332,24 @@ export function parseRoadmap(text) {
|
|
|
326
332
|
entry.origin = origin[1].trim();
|
|
327
333
|
continue;
|
|
328
334
|
}
|
|
329
|
-
const sprint = /^Sprint:\s*S(\d+)\s*(.*)$/.exec(line.trim());
|
|
335
|
+
const sprint = /^Sprint:\s*(?:(?!S\d+(?:\s|$))(.+?)\s*·\s*)?S(\d+)\s*(.*)$/.exec(line.trim());
|
|
330
336
|
if (sprint) {
|
|
331
|
-
// R257: `Sprint: S1 Notifications` —
|
|
332
|
-
// as the file says it.
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
|
|
336
|
-
|
|
337
|
+
// R257, R489: `Sprint: Core team · S1 Notifications` — a team's
|
|
338
|
+
// sprint, its number and its name as the file says it. A file written
|
|
339
|
+
// before sprints were teams' says `Sprint: S1 Notifications`; that
|
|
340
|
+
// still parses, with no team. The same shape the API's `sprint` has
|
|
341
|
+
// (`label`, `ref`, `number`, `name`), so a re-render from either is
|
|
342
|
+
// byte-identical; the importer sends the team's words and the number,
|
|
343
|
+
// since a sprint's name is the database's to say.
|
|
344
|
+
const number = Number(sprint[2]);
|
|
345
|
+
const team = sprint[1]?.trim() || null;
|
|
346
|
+
entry.sprint = {
|
|
347
|
+
team,
|
|
348
|
+
number,
|
|
349
|
+
ref: `S${number}`,
|
|
350
|
+
label: team ? `${team} · S${number}` : `S${number}`,
|
|
351
|
+
name: sprint[3].trim(),
|
|
352
|
+
};
|
|
337
353
|
continue;
|
|
338
354
|
}
|
|
339
355
|
bodyLines.push(line);
|
|
@@ -534,6 +550,10 @@ function renderEntry(entry) {
|
|
|
534
550
|
if (entry.targetVersion && !entry.version) {
|
|
535
551
|
lines.push(`Target: ${entry.targetVersion}`);
|
|
536
552
|
}
|
|
553
|
+
// R378. Where an issue was found; history, so it stays once the issue ships.
|
|
554
|
+
if (entry.affectedVersions?.length) {
|
|
555
|
+
lines.push(`Affects: ${entry.affectedVersions.join(', ')}`);
|
|
556
|
+
}
|
|
537
557
|
|
|
538
558
|
// Each status writes only what it currently claims. A MERGED entry keeps its
|
|
539
559
|
// branch in the database as history, but the file must not print it: the
|
|
@@ -609,7 +629,10 @@ function renderEntry(entry) {
|
|
|
609
629
|
// API's `sprint` and the parser's are the same shape, which is what makes
|
|
610
630
|
// render → parse → render a fixed point. State is not written — the file
|
|
611
631
|
// says which sprint, the database says whether it is still open.
|
|
612
|
-
|
|
632
|
+
// R489: the label (`Core team · S7`) is the only spelling that means
|
|
633
|
+
// anything outside the team; `ref` alone is the pre-teams file's.
|
|
634
|
+
const label = entry.sprint.label ?? entry.sprint.ref ?? `S${entry.sprint.number}`;
|
|
635
|
+
lines.push(`Sprint: ${label} ${entry.sprint.name ?? ''}`.trimEnd());
|
|
613
636
|
}
|
|
614
637
|
|
|
615
638
|
if (entry.body?.trim()) {
|
package/lib/stage-tools.mjs
CHANGED
package/mcp/README.md
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
Plain Node, zero dependencies, stdio JSON-RPC — `server.mjs`, plus the shared
|
|
4
4
|
rule matcher in `tools/lib/tool-rules.mjs` that the runner uses too. It gives a coding
|
|
5
5
|
agent a project's roadmap and changelog — the same verbs a person gets in the
|
|
6
|
-
console, because the API was built to mirror these tools one-for-one.
|
|
6
|
+
console, because the API was built to mirror these tools one-for-one. This README says
|
|
7
|
+
**card** for the roadmap thing and **run** for the work on it, as the console does; the API and
|
|
8
|
+
the tool names say *entry* (`roadmap_list`, `propose_entry`, `EntryRef`) and keep doing so.
|
|
7
9
|
|
|
8
10
|
## Setting it up in a repository
|
|
9
11
|
|
|
@@ -59,23 +61,23 @@ four being different from what you assumed.
|
|
|
59
61
|
|
|
60
62
|
The sequence is fixed, and the second step is the one people skip:
|
|
61
63
|
|
|
62
|
-
1. Branch off an up-to-date `main`, named after the
|
|
63
|
-
2. **Move the
|
|
64
|
+
1. Branch off an up-to-date `main`, named after the card — `r4-roadmap-entries`.
|
|
65
|
+
2. **Move the card to `IN_DEVELOPMENT` naming the branch, before your first commit**
|
|
64
66
|
(`roadmap_set_status`). Not after. The roadmap should be able to answer
|
|
65
67
|
"what is being worked on right now" without asking anyone.
|
|
66
|
-
3. Build to the
|
|
68
|
+
3. Build to the card's "Done when" list.
|
|
67
69
|
4. Finish with a PR. Never push to `main`. As your session ends the platform
|
|
68
70
|
moves the *branch* on the development board, not the card: to `REVIEW` where
|
|
69
71
|
the project runs `require_review` and somebody is going to read it, and to
|
|
70
72
|
`DONE` where it does not — a finish nobody was asked about (R132), because
|
|
71
73
|
there is no reviewer coming.
|
|
72
|
-
5. When the PR merges, move the
|
|
73
|
-
be deleted — `
|
|
74
|
+
5. When the PR merges, move the card to `MERGED` naming it. The branch may then
|
|
75
|
+
be deleted — `IN_DEVELOPMENT` is for work somebody is doing, not work that is done.
|
|
74
76
|
|
|
75
77
|
### What a status must carry
|
|
76
78
|
|
|
77
79
|
`roadmap_statuses` will tell you, and it is worth reading rather than guessing:
|
|
78
|
-
`
|
|
80
|
+
`IN_DEVELOPMENT` needs a branch, `MERGED` needs the merge — a pull request, a merge
|
|
79
81
|
commit, or a sha — `SHIPPED` needs a version that is a real git tag, and
|
|
80
82
|
`DECLINED` needs a reason. `REVIEW` needs nothing, deliberately: the pull
|
|
81
83
|
request it would otherwise carry belongs to the run, and a project that reviews
|
|
@@ -85,18 +87,29 @@ without opening pull requests has none to give it.
|
|
|
85
87
|
when you name no status is the project's rule, per door, and the default for a
|
|
86
88
|
card or issue an agent files is the backlog: waiting until a person or a sprint
|
|
87
89
|
brings it in, in no sprint and on no board column. Its origin names your
|
|
88
|
-
session and the card your run was on.
|
|
90
|
+
session and the card your run was on. A plan run's card lands where the console's
|
|
91
|
+
do (R380). Name a status (`CONSIDERING`, `NEW`) when
|
|
89
92
|
you mean the pipeline. Giving a backlog card a sprint moves it to the first
|
|
90
93
|
status of its kind; moving a card to `BACKLOG` takes it out of its sprint.
|
|
91
94
|
|
|
92
|
-
**
|
|
95
|
+
**Sprints are a team's** (R474, R489). The `sprint` of `roadmap_create`,
|
|
96
|
+
`roadmap_update` and the filter of `roadmap_list` take an integer or a string:
|
|
97
|
+
`7` or `"S7"` when the project's teams have one S7, `"Core team · S7"` (or
|
|
98
|
+
`"Core team / S7"`) when two do — the API refuses an ambiguous `S7` naming both
|
|
99
|
+
spellings, so read the sentence and send the long one — a sprint id, and `0` or
|
|
100
|
+
`""` to take a card out. A completed sprint refuses a card that is not already
|
|
101
|
+
in it, and only a person can reopen one; do not retry. `sprint_list` says which
|
|
102
|
+
sprints there are. A card prints its sprint as `Core team · S7 Name (active)`.
|
|
103
|
+
Opening, starting, completing and renaming a sprint stay a person's.
|
|
104
|
+
|
|
105
|
+
**Any status may move to any other.** There is no transition diagram — a card
|
|
93
106
|
really can go from `CONSIDERING` straight to `SHIPPED` if that is what happened.
|
|
94
107
|
The rules are about what a status *carries*, not the path it took.
|
|
95
108
|
|
|
96
109
|
### The body is what we want; the plan is how; the comments are the argument
|
|
97
110
|
|
|
98
111
|
`roadmap_get` gives you all three, and `task_current` gives you them for the
|
|
99
|
-
|
|
112
|
+
card you are working on.
|
|
100
113
|
|
|
101
114
|
**The plan is the one to read before doing any of the work.** It was written by
|
|
102
115
|
a plan phase and agreed by somebody, and it names the files — R124. If you are
|
|
@@ -109,8 +122,8 @@ either. The first is the platform's — it stores what a plan phase reports when
|
|
|
109
122
|
the phase ends, so that a session which must not write is not handed a writer to
|
|
110
123
|
do its own bookkeeping with. The second is refused outright: *an agent cannot
|
|
111
124
|
start another agent*, which is `RunAccess.requireCanStart`'s rule and not an
|
|
112
|
-
omission here. **Read the discussion before you propose anything about
|
|
113
|
-
|
|
125
|
+
omission here. **Read the discussion before you propose anything about a
|
|
126
|
+
card.** It is where an objection was answered and where an obvious-looking
|
|
114
127
|
approach was ruled out with a reason — and re-proposing what was talked out
|
|
115
128
|
three months ago is precisely what it exists to stop.
|
|
116
129
|
|
|
@@ -122,7 +135,7 @@ that the next reader does not have to reconstruct it from the thread.
|
|
|
122
135
|
A comment you write is attributed to your run as well as to the account that
|
|
123
136
|
minted your token, so a person can tell an agent's reading from a colleague's.
|
|
124
137
|
|
|
125
|
-
### Read what the earlier
|
|
138
|
+
### Read what the earlier runs did before repeating it
|
|
126
139
|
|
|
127
140
|
`task_current` lists **every run this card has already had** — how each ended,
|
|
128
141
|
on what branch, with what model, whether the work was pushed, and the commits it
|
|
@@ -132,21 +145,21 @@ to know what the first tried.
|
|
|
132
145
|
|
|
133
146
|
No transcripts come with it, deliberately: the terminal log is on the run's own
|
|
134
147
|
page in the console, and nine of them would fill the context this call exists to
|
|
135
|
-
orient. If you need the detail, the discussion under the
|
|
136
|
-
previous
|
|
148
|
+
orient. If you need the detail, the discussion under the card is where a
|
|
149
|
+
previous run should have written down what it learned — and where yours
|
|
137
150
|
should.
|
|
138
151
|
|
|
139
152
|
### There is no delete
|
|
140
153
|
|
|
141
154
|
Not in these tools, not in the API. `roadmap_decline` with a reason is the only
|
|
142
|
-
exit
|
|
155
|
+
exit a card has, and the reason is the point: it is what stops the same idea
|
|
143
156
|
being proposed again in six months. Same for changelog entries — correct the
|
|
144
157
|
text, do not remove the record. Same for comments: nothing removes one, and the
|
|
145
158
|
most you can do to your own is correct its wording.
|
|
146
159
|
|
|
147
160
|
### Ids are permanent, and a card is named by its ref
|
|
148
161
|
|
|
149
|
-
|
|
162
|
+
A card's number never changes and is never reused. Commit messages and code
|
|
150
163
|
comments point at it. Since R221 a roadmap card and an issue each count on
|
|
151
164
|
their own sequence, so `R91` and `i91` are two cards: wherever a tool takes
|
|
152
165
|
`number`, `related`, `after` or `entryNumber`, pass the ref — `"i91"` for an
|
|
@@ -168,13 +181,14 @@ what you are allowed to do.
|
|
|
168
181
|
|---|---|
|
|
169
182
|
| `roadmap_where` | Which platform, which token, read from where, and who you are. Start here. |
|
|
170
183
|
| `roadmap_statuses` | The eight statuses, what they mean, what each requires. |
|
|
171
|
-
| `roadmap_list` |
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
175
|
-
| `
|
|
184
|
+
| `roadmap_list` | Cards, optionally by status or `sprint` (any spelling below). `brief` omits bodies — use it to survey. |
|
|
185
|
+
| `sprint_list` | The project's sprints with how far each has got (R489): `Core team · S7 Notifications — active — 4 of 9 landed`. ACTIVE and UPCOMING unless `state` says otherwise. A read: PLAN and VERIFY have it. |
|
|
186
|
+
| `roadmap_get` | One card in full: its body, **the plan agreed for it**, and the discussion on it. |
|
|
187
|
+
| `roadmap_create` | Create a card; the platform allocates its permanent number. |
|
|
188
|
+
| `roadmap_update` | Title, body, section, sprint (R489: `7`, `S7`, `"Core team · S7"`, a sprint id; `0` or `""` takes the card out — see below), related ids, the cards it starts coding after (`after` — R181), when it is due and how big it is (`due` — R351: an ISO date, `""` clears; `estimate`: a positive number, `0` clears), the version it is meant for (`targetVersion` — R358, `""` clears; since R362 it must be one the project defined under Project settings → Versions, and any other name is refused), the versions an issue was found in (`affectedVersions` — R378, issues only: names the project defined or shipped in, `[]` clears), and who it is assigned to (`assignee` — R331: a username, `""` clears; `roadmap_list`/`issue_list` take `assignee` too, and `me` means the token's owner). |
|
|
189
|
+
| `roadmap_comment` | Say something *beside* a card rather than inside it. |
|
|
176
190
|
| `done_when_tick` | Tick (or untick) one numbered item of the card's Done-when checklist as built, with one line of evidence (R368). `task_current` lists the items. Defaults to the run's own card and refuses another; the reviewer's column is a person's and no token can set it. |
|
|
177
|
-
| `roadmap_set_status` | Move
|
|
191
|
+
| `roadmap_set_status` | Move a card, carrying whatever the status requires. |
|
|
178
192
|
| `roadmap_decline` | Decline with a reason. The only exit. |
|
|
179
193
|
| `backlog_file` | File **feedback**, not a card — something a person should decide about. A WRITER accepts it as a card or an issue, or refuses it (R199). Tagged `agent` as a source, plus any `tags` you pass — `customer` when you relay what a user said (R372). |
|
|
180
194
|
| `changelog_list` | The changelog, grouped by release, newest first. |
|
|
@@ -191,8 +205,8 @@ token gets a refusal saying so.
|
|
|
191
205
|
|
|
192
206
|
| Tool | What it does |
|
|
193
207
|
|---|---|
|
|
194
|
-
| `task_current` | The
|
|
195
|
-
| `report` | `progress` as often as useful; `done` when finished, naming the branch and any PR; `blocked` when a person must resolve something. `done` and `blocked` end the run; on a coding run `done` is refused until everything is committed and pushed (R345). |
|
|
208
|
+
| `task_current` | The card you are working on, its branch, everything already said and asked on this run, **and every earlier run on the same card**. **Call it first**, and again whenever you are unsure where you are. |
|
|
209
|
+
| `report` | `progress` as often as useful; `done` when finished, naming the branch and any PR; `blocked` when a person must resolve something. `done` and `blocked` end the run; on a review run the `done` body is filed as the branch's review (R529), so write it for the person reading the thread; on a coding run `done` is refused until everything is committed and pushed (R345). |
|
|
196
210
|
| `ask_user` | Ask the person who started the run, and wait. Blocks up to ten minutes, then hands back a `question_id`. |
|
|
197
211
|
| `await_answer` | Resume waiting for a question `ask_user` handed back. |
|
|
198
212
|
| `ask_group` | Ask a **round** — up to twelve questions that belong together, under one title. Blocks until every one of them has been answered, then hands back a `group_id`. |
|
|
@@ -201,7 +215,7 @@ token gets a refusal saying so.
|
|
|
201
215
|
| `approve` | **Not yours to call.** Claude Code calls it itself, as `--permission-prompt-tool`, when no rule covers a tool call. |
|
|
202
216
|
|
|
203
217
|
**`ask_user` is for decisions that are genuinely theirs** — an architectural
|
|
204
|
-
choice, a trade-off with no right answer, something the
|
|
218
|
+
choice, a trade-off with no right answer, something the card does not settle.
|
|
205
219
|
Not for checking work you can check yourself. Every question stops the run and
|
|
206
220
|
costs somebody's attention.
|
|
207
221
|
|
|
@@ -212,7 +226,7 @@ asked because the decision was not yours. Call `await_answer`, or `report`
|
|
|
212
226
|
**`ask_group` is for when you have several questions at once** — R96. One
|
|
213
227
|
question at a time is right for a decision you hit halfway through a piece of
|
|
214
228
|
work; it is wrong when you are trying to understand something, because forty
|
|
215
|
-
separate
|
|
229
|
+
separate Waiting on you items arriving over an afternoon is not a conversation, it is an
|
|
216
230
|
interruption repeated forty times. A round arrives as one form under one title,
|
|
217
231
|
the person answers it in one sitting, and you are woken once when all of it is
|
|
218
232
|
answered.
|
|
@@ -258,9 +272,9 @@ Two smoke tests, both driving the server over real stdio.
|
|
|
258
272
|
CAWDEV_URL=http://localhost:4200 CAWDEV_TOKEN=cawd_… node tools/mcp/smoke.mjs scratch-project
|
|
259
273
|
```
|
|
260
274
|
|
|
261
|
-
**Name a scratch project.** The smoke test creates a roadmap
|
|
262
|
-
it, and
|
|
263
|
-
meant. It left two declined
|
|
275
|
+
**Name a scratch project.** The smoke test creates a roadmap card and declines
|
|
276
|
+
it, and cards cannot be deleted — so it refuses to guess which project you
|
|
277
|
+
meant. It left two declined cards in cawdev's own roadmap before it did.
|
|
264
278
|
|
|
265
279
|
It drives the server the way a client does — spawn, write lines to stdin, read
|
|
266
280
|
lines from stdout — rather than importing its functions, because the parts most
|