claude-code-kanban 5.4.0 → 6.0.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 +2 -2
- package/cli.js +20 -103
- package/lib/dispatch-groups.js +9 -31
- package/lib/dispatch.js +19 -166
- package/lib/git-branch.js +16 -1
- package/lib/retention.js +5 -17
- package/lib/session-events.js +3 -2
- package/lib/terminal.js +28 -4
- package/package.json +1 -1
- package/plugin/plugins/claude-code-kanban/.claude-plugin/plugin.json +1 -1
- package/plugin/plugins/claude-code-kanban/monitors.json +0 -6
- package/plugin/plugins/claude-code-kanban/scripts/postman.js +2 -7
- package/plugin/plugins/claude-code-kanban/skills/dispatch/SKILL.md +17 -11
- package/plugin/plugins/claude-code-kanban/skills/dispatch/references/orchestration-patterns.md +50 -0
- package/public/app.js +249 -66
- package/public/index.html +12 -2
- package/public/project-match.js +6 -1
- package/public/style.css +99 -0
- package/server.js +43 -55
- package/skill-guides/dispatch.md +16 -54
package/public/style.css
CHANGED
|
@@ -7075,6 +7075,105 @@ select.form-input option:checked {
|
|
|
7075
7075
|
color: var(--text-muted);
|
|
7076
7076
|
align-self: center;
|
|
7077
7077
|
}
|
|
7078
|
+
|
|
7079
|
+
.scratchpad-header {
|
|
7080
|
+
position: relative;
|
|
7081
|
+
}
|
|
7082
|
+
|
|
7083
|
+
.scratchpad-title-row {
|
|
7084
|
+
display: flex;
|
|
7085
|
+
align-items: center;
|
|
7086
|
+
gap: 10px;
|
|
7087
|
+
}
|
|
7088
|
+
|
|
7089
|
+
.scratchpad-emoji-btn {
|
|
7090
|
+
width: 32px;
|
|
7091
|
+
height: 32px;
|
|
7092
|
+
display: flex;
|
|
7093
|
+
align-items: center;
|
|
7094
|
+
justify-content: center;
|
|
7095
|
+
font-size: 18px;
|
|
7096
|
+
line-height: 1;
|
|
7097
|
+
color: var(--text-muted);
|
|
7098
|
+
background: var(--bg-elevated);
|
|
7099
|
+
border: 1px dashed var(--border);
|
|
7100
|
+
border-radius: 8px;
|
|
7101
|
+
cursor: pointer;
|
|
7102
|
+
}
|
|
7103
|
+
|
|
7104
|
+
.scratchpad-emoji-btn.set {
|
|
7105
|
+
border-style: solid;
|
|
7106
|
+
border-color: var(--accent);
|
|
7107
|
+
}
|
|
7108
|
+
|
|
7109
|
+
.scratchpad-emoji-btn:hover {
|
|
7110
|
+
border-color: var(--accent);
|
|
7111
|
+
}
|
|
7112
|
+
|
|
7113
|
+
.scratchpad-emoji-btn[hidden] {
|
|
7114
|
+
display: none;
|
|
7115
|
+
}
|
|
7116
|
+
|
|
7117
|
+
.scratchpad-emoji-picker {
|
|
7118
|
+
display: none;
|
|
7119
|
+
position: absolute;
|
|
7120
|
+
top: 40px;
|
|
7121
|
+
left: 0;
|
|
7122
|
+
z-index: 5;
|
|
7123
|
+
width: 300px;
|
|
7124
|
+
padding: 10px;
|
|
7125
|
+
background: var(--bg-elevated);
|
|
7126
|
+
border: 1px solid var(--border);
|
|
7127
|
+
border-radius: 10px;
|
|
7128
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
|
|
7129
|
+
outline: none;
|
|
7130
|
+
}
|
|
7131
|
+
|
|
7132
|
+
.scratchpad-emoji-picker.open {
|
|
7133
|
+
display: block;
|
|
7134
|
+
}
|
|
7135
|
+
|
|
7136
|
+
.scratchpad-emoji-grid {
|
|
7137
|
+
display: grid;
|
|
7138
|
+
gap: 2px;
|
|
7139
|
+
}
|
|
7140
|
+
|
|
7141
|
+
.scratchpad-emoji-grid button {
|
|
7142
|
+
padding: 4px 0;
|
|
7143
|
+
font-size: 18px;
|
|
7144
|
+
background: none;
|
|
7145
|
+
border: 0;
|
|
7146
|
+
border-radius: 6px;
|
|
7147
|
+
cursor: pointer;
|
|
7148
|
+
}
|
|
7149
|
+
|
|
7150
|
+
.scratchpad-emoji-grid button:hover,
|
|
7151
|
+
.scratchpad-emoji-grid button.kb {
|
|
7152
|
+
background: var(--bg-hover);
|
|
7153
|
+
}
|
|
7154
|
+
|
|
7155
|
+
.scratchpad-emoji-grid button.cur {
|
|
7156
|
+
background: var(--accent-dim);
|
|
7157
|
+
}
|
|
7158
|
+
|
|
7159
|
+
.scratchpad-emoji-row {
|
|
7160
|
+
display: flex;
|
|
7161
|
+
gap: 6px;
|
|
7162
|
+
margin-top: 8px;
|
|
7163
|
+
}
|
|
7164
|
+
|
|
7165
|
+
.scratchpad-emoji-row .form-input {
|
|
7166
|
+
flex: 1;
|
|
7167
|
+
min-width: 0;
|
|
7168
|
+
padding: 5px 8px;
|
|
7169
|
+
font-size: 13px;
|
|
7170
|
+
}
|
|
7171
|
+
|
|
7172
|
+
.scratchpad-badge.has-emoji {
|
|
7173
|
+
font-size: 13px;
|
|
7174
|
+
line-height: 18px;
|
|
7175
|
+
padding: 0 5px;
|
|
7176
|
+
}
|
|
7078
7177
|
/* #endregion */
|
|
7079
7178
|
|
|
7080
7179
|
/* #region STORAGE_MANAGER */
|
package/server.js
CHANGED
|
@@ -43,11 +43,11 @@ const { buildDecision, decisionFileName, isDecisionFile, approvalsFrom, boardRef
|
|
|
43
43
|
const { getClaudeDir, getArgValue, storageNamespace, isDefaultClaudeDir, encodeProjectDirName } = require('./lib/claude-dir');
|
|
44
44
|
const { createTerminalService, readTerminalConfig } = require('./lib/terminal');
|
|
45
45
|
const { createProcStats } = require('./lib/proc-stats');
|
|
46
|
-
const { createDispatchRegistry
|
|
46
|
+
const { createDispatchRegistry } = require('./lib/dispatch');
|
|
47
47
|
const { createGroupStore, isGroupName, suggestGroupName } = require('./lib/dispatch-groups');
|
|
48
48
|
const { createDispatchedStore, scanTranscripts, pruneSessionDirs, retentionMs } = require('./lib/retention');
|
|
49
49
|
const { createWorktreeStore } = require('./lib/worktrees');
|
|
50
|
-
const { readGitBranch } = require('./lib/git-branch');
|
|
50
|
+
const { readGitBranch, sessionGitBranch } = require('./lib/git-branch');
|
|
51
51
|
const { createLinkedDocStore, linkUrl } = require('./lib/linked-docs');
|
|
52
52
|
const { pickFolder } = require('./lib/folder-dialog');
|
|
53
53
|
const { loadSessionCache, saveSessionCache } = require('./lib/session-cache');
|
|
@@ -343,15 +343,6 @@ const worktrees = createWorktreeStore({
|
|
|
343
343
|
save: (data) => writeJsonAtomic(WORKTREES_FILE, data),
|
|
344
344
|
});
|
|
345
345
|
|
|
346
|
-
// Only look up the branch when cwd has diverged from the launch project — that's
|
|
347
|
-
// the only case the JSONL value is wrong.
|
|
348
|
-
function resolveSessionGitBranch(meta) {
|
|
349
|
-
if (meta.cwd && meta.project && meta.cwd !== meta.project) {
|
|
350
|
-
return getGitBranch(meta.cwd) || meta.gitBranch || null;
|
|
351
|
-
}
|
|
352
|
-
return meta.gitBranch || null;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
346
|
function getSessionLogStat(meta) {
|
|
356
347
|
if (!meta.jsonlPath) return { mtime: null, hasMessages: false };
|
|
357
348
|
try {
|
|
@@ -1168,6 +1159,7 @@ function buildSessionObject(id, meta, overrides = {}) {
|
|
|
1168
1159
|
const logStat = overrides._logStat || getSessionLogStat(meta);
|
|
1169
1160
|
const logMtime = logStat.mtime;
|
|
1170
1161
|
const logAge = logMtime ? Date.now() - logMtime : Infinity;
|
|
1162
|
+
const worktree = worktrees.resolve(meta.project);
|
|
1171
1163
|
return {
|
|
1172
1164
|
id,
|
|
1173
1165
|
name: getSessionDisplayName(id, meta),
|
|
@@ -1176,8 +1168,8 @@ function buildSessionObject(id, meta, overrides = {}) {
|
|
|
1176
1168
|
project: meta.project || null,
|
|
1177
1169
|
cwd: meta.cwd || null,
|
|
1178
1170
|
description: meta.description || null,
|
|
1179
|
-
gitBranch:
|
|
1180
|
-
worktree
|
|
1171
|
+
gitBranch: sessionGitBranch(meta, !!worktree, getGitBranch),
|
|
1172
|
+
worktree,
|
|
1181
1173
|
customTitle: meta.customTitle || null,
|
|
1182
1174
|
taskCount: 0,
|
|
1183
1175
|
completed: 0,
|
|
@@ -1631,6 +1623,30 @@ app.get('/api/sessions/search', (req, res) => {
|
|
|
1631
1623
|
res.json(hits.slice(0, SESSION_SEARCH_MAX).map((h) => h.id));
|
|
1632
1624
|
});
|
|
1633
1625
|
|
|
1626
|
+
app.get('/api/sessions/known', (_req, res) => {
|
|
1627
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
1628
|
+
try {
|
|
1629
|
+
const known = new Map();
|
|
1630
|
+
for (const [id, meta] of Object.entries(loadSessionMetadata())) {
|
|
1631
|
+
known.set(id, { id, project: meta.project || null, name: getSessionDisplayName(id, meta) });
|
|
1632
|
+
}
|
|
1633
|
+
const add = (id, project = null) => {
|
|
1634
|
+
if (!known.has(id)) known.set(id, { id, project, name: null });
|
|
1635
|
+
};
|
|
1636
|
+
for (const dir of [TASKS_DIR, AGENT_ACTIVITY_DIR]) {
|
|
1637
|
+
if (!existsSync(dir)) continue;
|
|
1638
|
+
for (const d of readdirSync(dir, { withFileTypes: true })) if (d.isDirectory()) add(d.name);
|
|
1639
|
+
}
|
|
1640
|
+
for (const map of Object.values(loadAllTaskMaps().listToSessions)) {
|
|
1641
|
+
for (const [id, info] of Object.entries(map)) add(id, info.project || null);
|
|
1642
|
+
}
|
|
1643
|
+
res.json([...known.values()]);
|
|
1644
|
+
} catch (error) {
|
|
1645
|
+
console.error('Error listing known sessions:', error);
|
|
1646
|
+
res.status(500).json({ error: 'Failed to list known sessions' });
|
|
1647
|
+
}
|
|
1648
|
+
});
|
|
1649
|
+
|
|
1634
1650
|
function isTempPath(p) {
|
|
1635
1651
|
const rel = path.relative(TEMP_ROOT, p);
|
|
1636
1652
|
return !!rel && !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
@@ -3276,7 +3292,9 @@ const terminal = createTerminalService({
|
|
|
3276
3292
|
isAllowedFolder,
|
|
3277
3293
|
liveSessions: () => loadLiveSessions(true),
|
|
3278
3294
|
onChange: () => broadcast({ type: 'terminals-update', ids: terminal.list().map((t) => t.id) }),
|
|
3279
|
-
onExit: (id) =>
|
|
3295
|
+
onExit: (id) => {
|
|
3296
|
+
if (dispatches.remove(id)) broadcast({ type: 'dispatch-update' });
|
|
3297
|
+
},
|
|
3280
3298
|
});
|
|
3281
3299
|
|
|
3282
3300
|
let folderDialogOpen = false;
|
|
@@ -3346,13 +3364,7 @@ const dispatched = createDispatchedStore({
|
|
|
3346
3364
|
save: (data) => writeJsonAtomic(DISPATCHED_FILE, data),
|
|
3347
3365
|
});
|
|
3348
3366
|
|
|
3349
|
-
const dispatches = createDispatchRegistry(
|
|
3350
|
-
onSettle: (r) => {
|
|
3351
|
-
if (r.parent && r.report) enqueueSessionEvent(topicKey('dispatch', r.parent), formatDispatchLine(r));
|
|
3352
|
-
if (r.session) dispatched.settle(r.session, r.status);
|
|
3353
|
-
broadcast({ type: 'dispatch-update' });
|
|
3354
|
-
},
|
|
3355
|
-
});
|
|
3367
|
+
const dispatches = createDispatchRegistry();
|
|
3356
3368
|
|
|
3357
3369
|
const dispatchGroups = createGroupStore({
|
|
3358
3370
|
load: () => {
|
|
@@ -3371,8 +3383,6 @@ const linkedDocs = createLinkedDocStore({
|
|
|
3371
3383
|
});
|
|
3372
3384
|
|
|
3373
3385
|
// The board places a session from these alone, so it never has to move it later.
|
|
3374
|
-
// `startedBy` lets it follow a starter the user put in a named group, which only the
|
|
3375
|
-
// browser knows; it goes once the dispatch settles. `dispatched` stays for the card's marker.
|
|
3376
3386
|
function withDispatchPlacement(sessions) {
|
|
3377
3387
|
const groups = dispatchGroups.snapshot();
|
|
3378
3388
|
return sessions.map((s) => {
|
|
@@ -3382,56 +3392,35 @@ function withDispatchPlacement(sessions) {
|
|
|
3382
3392
|
return {
|
|
3383
3393
|
...s,
|
|
3384
3394
|
dispatchGroup,
|
|
3385
|
-
|
|
3386
|
-
dispatched: marker ? { parent: marker.parent, outcome: DISPATCH_OUTCOME[marker.status] } : undefined,
|
|
3395
|
+
dispatched: marker ? { parent: marker.parent } : undefined,
|
|
3387
3396
|
};
|
|
3388
3397
|
});
|
|
3389
3398
|
}
|
|
3390
3399
|
|
|
3391
3400
|
// Starting a session needs the terminal token, as the browser does; the CLI reads it from
|
|
3392
|
-
// TERMINAL_TOKEN_FILE. The
|
|
3401
|
+
// TERMINAL_TOKEN_FILE. The started session never holds it.
|
|
3393
3402
|
app.post('/api/dispatch', (req, res) => {
|
|
3394
3403
|
if (!terminal.authorized(req.get('x-terminal-token'))) return res.status(401).json({ error: 'invalid terminal token' });
|
|
3395
|
-
const { cwd, spec, name, model, worktree, parent, group,
|
|
3404
|
+
const { cwd, spec, name, model, worktree, parent, group, claudeArgs } = req.body || {};
|
|
3396
3405
|
if (typeof spec !== 'string' || !spec.trim()) return res.status(400).json({ error: 'spec is required' });
|
|
3397
|
-
if (peer != null && !isPeerName(peer)) return res.status(400).json({ error: 'peer must be a peer name as ListAgents prints it' });
|
|
3398
3406
|
if (parent != null && !(typeof parent === 'string' && isUUID(parent))) return res.status(400).json({ error: 'invalid parent' });
|
|
3399
3407
|
if (group != null && !isGroupName(group)) {
|
|
3400
3408
|
return res.status(400).json({ error: `group must be kebab-case, e.g. ${suggestGroupName(group) || 'my-group'}` });
|
|
3401
3409
|
}
|
|
3402
|
-
const
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
const started = terminal.startNew({ cwd, name, model, worktree, prompt: formatPreamble(r) }, env);
|
|
3406
|
-
if (started.error) {
|
|
3407
|
-
dispatches.discard(r.id);
|
|
3408
|
-
return res.status(started.status).json({ error: started.error });
|
|
3409
|
-
}
|
|
3410
|
-
dispatches.attach(r.id, { session: started.id, cwd: started.cwd });
|
|
3410
|
+
const started = terminal.startNew({ cwd, name, model, worktree, prompt: spec.trim(), extraArgs: claudeArgs });
|
|
3411
|
+
if (started.error) return res.status(started.status).json({ error: started.error });
|
|
3412
|
+
dispatches.add({ session: started.id, parent, cwd: started.cwd, name, group: group || null, worktree });
|
|
3411
3413
|
dispatched.record(started.id, parent);
|
|
3412
3414
|
// The starter stays where it is: moving it would jump it under the user.
|
|
3413
|
-
if (
|
|
3415
|
+
if (group) dispatchGroups.join(group, [started.id]);
|
|
3414
3416
|
broadcast({ type: 'dispatch-update' });
|
|
3415
|
-
res.status(201).json({
|
|
3417
|
+
res.status(201).json({ session: started.id, cwd: started.cwd, group: group || null });
|
|
3416
3418
|
});
|
|
3417
3419
|
|
|
3418
|
-
app.
|
|
3419
|
-
const { cap, outcome, summary } = req.body || {};
|
|
3420
|
-
const err = dispatches.settle(req.params.id, cap, outcome, summary);
|
|
3421
|
-
if (err) return res.status(err.status).json({ error: err.error });
|
|
3422
|
-
res.status(204).end();
|
|
3423
|
-
});
|
|
3424
|
-
|
|
3425
|
-
app.get('/api/dispatch', async (req, res) => {
|
|
3420
|
+
app.get('/api/dispatch', (req, res) => {
|
|
3426
3421
|
res.setHeader('Cache-Control', 'no-store');
|
|
3427
|
-
const ids = typeof req.query.ids === 'string' && req.query.ids ? req.query.ids.split(',') : null;
|
|
3428
3422
|
const parent = typeof req.query.parent === 'string' && req.query.parent ? req.query.parent : null;
|
|
3429
|
-
|
|
3430
|
-
const out = await dispatches.wait({ ids, parent }, req.query.wait, (stop) => {
|
|
3431
|
-
res.on('close', stop);
|
|
3432
|
-
return () => res.off('close', stop);
|
|
3433
|
-
});
|
|
3434
|
-
if (!res.writableEnded) res.json(out);
|
|
3423
|
+
res.json({ running: dispatches.list({ parent }) });
|
|
3435
3424
|
});
|
|
3436
3425
|
// #endregion
|
|
3437
3426
|
|
|
@@ -3496,7 +3485,6 @@ const {
|
|
|
3496
3485
|
formatTaskMoved,
|
|
3497
3486
|
handleSessionEvents,
|
|
3498
3487
|
hasSessionListener,
|
|
3499
|
-
topicKey,
|
|
3500
3488
|
} = require('./lib/session-events');
|
|
3501
3489
|
app.get('/api/sessions/:sessionId/events', handleSessionEvents);
|
|
3502
3490
|
|
package/skill-guides/dispatch.md
CHANGED
|
@@ -1,73 +1,35 @@
|
|
|
1
1
|
# Dispatch guide
|
|
2
2
|
|
|
3
|
-
A dispatch is
|
|
3
|
+
A dispatch is a plain Claude Code session that cck starts in its embedded terminal, with your spec as the first message. It is an ordinary session: it shows in the sidebar, its card links back to you, and the user can open its terminal at any time. cck only starts it.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Spec
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The started session sees only the spec, not this conversation, so the spec carries everything it needs.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- **Target:** the files, component, or environment in scope.
|
|
12
|
-
- **Change:** the concrete result to produce.
|
|
13
|
-
- **Constraints:** invariants and do-not-touch boundaries.
|
|
14
|
-
- **Ownership:** what it may edit. Two dispatches edit the same files only when each runs in its own `--worktree`.
|
|
15
|
-
- **Acceptance:** the test, output, or evidence that proves it is done.
|
|
16
|
-
|
|
17
|
-
Dispatch when the task can run on its own. Do the work yourself when it is small or needs context from this conversation that you cannot write down.
|
|
9
|
+
cck sends nothing back. To hear from the session, the spec tells it to `SendMessage` you and names you. Your name is auto-assigned (e.g. `claude-code-hub-06`); `ListAgents` prints it as "This session is <name>".
|
|
18
10
|
|
|
19
11
|
## Start
|
|
20
12
|
|
|
21
13
|
```bash
|
|
22
|
-
claude-code-kanban dispatch start --cwd <dir> --spec-file <spec.md> --name <name> --group <group> --
|
|
14
|
+
claude-code-kanban dispatch start --cwd <dir> --spec-file <spec.md> --name <name> --group <group> --json -- <claude args>
|
|
23
15
|
```
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- `--
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
- The result holds the `dispatch` id and the `session` id.
|
|
32
|
-
|
|
33
|
-
## Fire-and-forget
|
|
17
|
+
- `--name` is the sidebar name and the session's peer name. Kebab-case and unique: `fix-login-redirect`.
|
|
18
|
+
- `--group` names the effort in kebab-case (`auth-refactor`) and shows the session under that sidebar group. Pass it on every dispatch that belongs to the effort; a dispatch without it goes to its project.
|
|
19
|
+
- `--spec-file` keeps a long spec out of shell quoting.
|
|
20
|
+
- Everything after `--` goes to `claude` as it is: any flag in `claude --help`, e.g. `-- --permission-mode auto --add-dir ../shared`. Keep each value one shell word of plain characters (no quotes, `%` or control characters); long text belongs in the spec. cck owns the session id, name, model and worktree, so pass those with its own flags.
|
|
21
|
+
- After a cck restart the terminal comes back with `claude --resume <id>` alone, so the args after `--` apply to the first run only.
|
|
22
|
+
- The result holds the `session` id.
|
|
34
23
|
|
|
35
|
-
|
|
24
|
+
`claude-code-kanban help dispatch start` lists cck's flags (`--model`, `--worktree` and more); `claude --help` lists the ones you can pass after `--`.
|
|
36
25
|
|
|
37
|
-
##
|
|
26
|
+
## Messages and status
|
|
38
27
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
- **Inbox:** this skill armed it. Lines `[kanban board] Dispatch <id> (session <uuid>) ...` arrive on their own while you keep working.
|
|
42
|
-
- **Wait:** block until one settles.
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
claude-code-kanban dispatch wait [<id>...] --timeout 15m --json
|
|
46
|
-
```
|
|
28
|
+
A message from the session arrives here as a new turn. Reply with `SendMessage` to its name.
|
|
47
29
|
|
|
48
|
-
|
|
30
|
+
A crashed session sends nothing. To learn when one ends, `SendMessage` it with `notify_when_idle: true`, or look:
|
|
49
31
|
|
|
50
32
|
```bash
|
|
51
|
-
claude-code-kanban dispatch list --json
|
|
33
|
+
claude-code-kanban dispatch list --json # still running in cck's terminal
|
|
52
34
|
claude-code-kanban session peek <session-id> --limit 20
|
|
53
35
|
```
|
|
54
|
-
|
|
55
|
-
A dispatch still `running` is still working; retry only after a `failed` report or an `exited` one.
|
|
56
|
-
|
|
57
|
-
The summary is the started session's own claim. Verify it (run the tests, read the diff), then give the user each dispatch's outcome, the summary, and what you checked. Done when every `--report` dispatch has settled and each summary is verified.
|
|
58
|
-
|
|
59
|
-
## Peer
|
|
60
|
-
|
|
61
|
-
A dispatch is a Claude Code peer under its `--name`, so `SendMessage` reaches it and it reaches you. The peer channel carries the conversation. The report carries the record: only `dispatch done` settles a dispatch, ends `dispatch wait`, and shows in the sidebar.
|
|
62
|
-
|
|
63
|
-
- **Answer questions.** A question or a finding from the dispatch arrives as a new turn. Answer it yourself, or ask the user when the decision is theirs, then send the answer back.
|
|
64
|
-
- **Steer.** Send a short, self-contained message to the dispatch's name. It arrives between the receiver's steps, never inside a subagent or a running workflow.
|
|
65
|
-
- **Limits.** A session in another permission mode can hold a message until its user approves it, so anything the result depends on goes in the report. A dispatch that restarts ends as `exited` and cannot report, so its result comes back as a message.
|
|
66
|
-
|
|
67
|
-
## If you are the started session
|
|
68
|
-
|
|
69
|
-
Your prompt begins with `[cck dispatch <id>]` and holds your instructions: the peer to ask, and with a report the exact `dispatch done` command. Follow them. Without that line, the prompt is the task alone.
|
|
70
|
-
|
|
71
|
-
- Ask the peer with `SendMessage` when you need a decision or find something that changes the task, and keep working on what does not depend on the answer.
|
|
72
|
-
- The summary is three sentences: what changed, what you found, what remains. Use `--summary-file` if it needs quotes.
|
|
73
|
-
- After you report, a message from the peer is a new request: answer it.
|