@kendoo.agentdesk/agentdesk 0.14.4 → 0.15.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
@@ -81,7 +81,7 @@ agentdesk update Update to the latest version
81
81
  |------|-------------|
82
82
  | `--description`, `-d` | Task description or requirements |
83
83
  | `--cwd` | Working directory (defaults to current) |
84
- | `--phased` | Run in phased mode (INTAKE → PLAN → EXECUTION) — each phase runs as a separate Claude process for better context handling. Also available as a toggle in the dashboard when starting a session. |
84
+ | `--phased` | Run in phased mode (INTAKE → PLAN → EXECUTION → REVIEW → SUMMARY) — each phase runs as a separate Claude process for better context handling. Also available as a toggle in the dashboard when starting a session. |
85
85
  | `--child-strategy` | How to handle child tasks: `inline` (default) or `branch` |
86
86
 
87
87
  ## Configuration
@@ -157,19 +157,23 @@ You can also toggle screenshots per session when starting a task from the Web UI
157
157
 
158
158
  ### Model per phase
159
159
 
160
- In phased mode (`INTAKE → PLAN → EXECUTION`), you can pick a different Claude model per phase from project settings. Useful when you want a stronger model for planning and implementation but a cheaper one for intake.
160
+ In phased mode (`INTAKE → PLAN → EXECUTION → REVIEW → SUMMARY`), you can pick a different Claude model per phase from project settings. Useful when you want a stronger model for planning and implementation but a cheaper one for the lighter phases.
161
161
 
162
162
  ```json
163
163
  {
164
164
  "phaseModels": {
165
165
  "INTAKE": "sonnet",
166
166
  "PLAN": "opus",
167
- "EXECUTION": "opus"
167
+ "EXECUTION": "opus",
168
+ "REVIEW": "haiku",
169
+ "SUMMARY": "haiku"
168
170
  }
169
171
  }
170
172
  ```
171
173
 
172
- Valid values: `"default"` (Claude Code's default), `"opus"`, `"sonnet"`, `"haiku"`. Non-phased runs use the `EXECUTION` model.
174
+ Valid values: `"default"`, `"opus"`, `"sonnet"`, `"haiku"`. `"default"` resolves to Claude Code's default for `INTAKE`/`PLAN`/`EXECUTION` and to `haiku` for `REVIEW`/`SUMMARY`. Non-phased runs use the `EXECUTION` model.
175
+
176
+ The `REVIEW` phase is a read-only completeness check (no code changes) — it verifies the implementation meets requirements, flags missed documentation updates or silently-deferred scope. If gaps are found, the orchestrator loops back to `EXECUTION` once before moving on. The `SUMMARY` phase writes the final tracker comments and session protocol.
173
177
 
174
178
  ## How It Works
175
179
 
@@ -177,7 +181,7 @@ All agents collaborate in a single Claude process — each with distinct roles,
177
181
 
178
182
  1. You run `agentdesk team TASK-ID` in your project directory
179
183
  2. AgentDesk detects your project type, reads `CLAUDE.md`, and discovers existing agents
180
- 3. The orchestrator manages 5 phases: **Intake** > **Brainstorm** > **Planning** > **Execution** > **Review**
184
+ 3. The orchestrator manages 5 phases: **Intake** > **Plan** > **Execution** > **Review** > **Summary**
181
185
  4. Agents discuss, disagree, and build on each other's ideas within a shared context
182
186
  5. The session streams live to [agentdesk.live](https://agentdesk.live) where you can watch and send messages to the team
183
187
  6. Token usage is tracked and displayed per session
package/bin/agentdesk.mjs CHANGED
@@ -71,7 +71,7 @@ if (!command || command === "help" || command === "--help") {
71
71
  Options:
72
72
  --description, -d Task description or requirements
73
73
  --cwd Working directory (defaults to current)
74
- --phased Run in phased mode (INTAKE → PLAN → EXECUTION)
74
+ --phased Run in phased mode (INTAKE → PLAN → EXECUTION → REVIEW → SUMMARY)
75
75
  --child-strategy How to handle child tasks: "inline" (default) or "branch"
76
76
 
77
77
 
package/cli/config.mjs CHANGED
@@ -42,10 +42,11 @@ const DEFAULTS = {
42
42
  // Capture screenshots for UI tasks (default: on)
43
43
  screenshots: true,
44
44
 
45
- // Model per phase — override Claude Code's default per phase.
46
- // Keys: INTAKE, PLAN, EXECUTION. Values: "default" | "opus" | "sonnet" | "haiku".
47
- // Missing entry or "default" = use Claude Code's default model.
48
- // Example: { "PLAN": "opus", "EXECUTION": "opus" }
45
+ // Model per phase — override the phase default.
46
+ // Keys: INTAKE, PLAN, EXECUTION, REVIEW, SUMMARY. Values: "default" | "opus" | "sonnet" | "haiku".
47
+ // Missing entry or "default" falls back to the phase default
48
+ // (sonnet for INTAKE/PLAN/EXECUTION, haiku for REVIEW/SUMMARY).
49
+ // Example: { "PLAN": "opus", "EXECUTION": "opus", "REVIEW": "sonnet" }
49
50
  phaseModels: {},
50
51
 
51
52
  // Extra prompt instructions appended to the team prompt
package/cli/daemon.mjs CHANGED
@@ -191,7 +191,7 @@ export async function runDaemon() {
191
191
  }
192
192
 
193
193
  console.log("");
194
- console.log(` ${green}AgentDesk Daemon${reset}`);
194
+ console.log(` ${green}AgentDesk Daemon${reset} ${dim}v${DAEMON_VERSION}${reset}`);
195
195
  console.log(" ━━━━━━━━━━━━━━━━━━━━━━━━");
196
196
  console.log("");
197
197
  console.log(` Projects:`);
@@ -44,19 +44,29 @@ function timestamp() {
44
44
  .map(n => String(n).padStart(2, "0")).join(":");
45
45
  }
46
46
 
47
+ // Phase-specific fallback when the user hasn't set a model override.
48
+ // REVIEW and SUMMARY default to haiku (fast + cheap — no heavy reasoning needed).
49
+ // INTAKE/PLAN/EXECUTION default to Claude Code's default (sonnet).
50
+ function defaultForPhase(phase) {
51
+ if (phase === "REVIEW" || phase === "SUMMARY") return "haiku";
52
+ return null;
53
+ }
54
+
47
55
  // Resolve --model args for a given phase based on project settings.
48
- // phaseModels is { INTAKE?, PLAN?, EXECUTION? } with values "opus"|"sonnet"|"haiku"|"default".
56
+ // phaseModels is { INTAKE?, PLAN?, EXECUTION?, REVIEW?, SUMMARY? } with values "opus"|"sonnet"|"haiku"|"default".
49
57
  // Returns [] when no override (so Claude Code picks its default).
50
58
  function modelArgsForPhase(phase, phaseModels) {
51
- const choice = phaseModels?.[phase];
52
- if (!choice || choice === "default") return [];
53
- // Claude Code accepts short aliases: opus, sonnet, haiku
59
+ let choice = phaseModels?.[phase];
60
+ if (!choice || choice === "default") choice = defaultForPhase(phase);
61
+ if (!choice) return [];
54
62
  return ["--model", choice];
55
63
  }
56
64
 
57
- // Display label for a phase's model ("opus"|"sonnet"|"haiku"|"default").
65
+ // Display label for a phase's model.
58
66
  function modelLabelForPhase(phase, phaseModels) {
59
- return phaseModels?.[phase] || "default";
67
+ const override = phaseModels?.[phase];
68
+ if (override && override !== "default") return override;
69
+ return defaultForPhase(phase) || "default";
60
70
  }
61
71
 
62
72
  export async function runOrchestrator({
@@ -262,18 +272,29 @@ export async function runPhasedOrchestrator({
262
272
  cliVersion: CLI_VERSION,
263
273
  });
264
274
 
265
- const phases = ["INTAKE", "PLAN", "EXECUTION"];
266
275
  const sessionMemoryPath = join(cwd, ".agentdesk", "session-memory.md");
276
+ const reviewVerdictPath = join(cwd, ".agentdesk", "review-verdict.md");
277
+ const MAX_REVIEW_RETRIES = 1; // One execution redo after a failed review, then force SUMMARY.
267
278
  let totalInputTokens = 0, totalOutputTokens = 0, totalSteps = 0;
268
279
  let handoff = false;
280
+ let reviewRetries = 0;
281
+
282
+ // Ordered queue of phases; REVIEW may re-enqueue EXECUTION before SUMMARY.
283
+ const queue = ["INTAKE", "PLAN", "EXECUTION", "REVIEW", "SUMMARY"];
284
+
285
+ while (queue.length > 0) {
286
+ const phase = queue.shift();
269
287
 
270
- for (const phase of phases) {
271
- // Read session memory from previous phase
272
288
  let sessionMemory = "";
273
289
  try {
274
290
  if (existsSync(sessionMemoryPath)) sessionMemory = readFileSync(sessionMemoryPath, "utf-8").trim();
275
291
  } catch {}
276
292
 
293
+ // Clear any stale verdict before entering REVIEW so we don't read a previous run's result.
294
+ if (phase === "REVIEW") {
295
+ try { if (existsSync(reviewVerdictPath)) unlinkSync(reviewVerdictPath); } catch {}
296
+ }
297
+
277
298
  emit({ type: "phase:change", phase, model: modelLabelForPhase(phase, config?.phaseModels) });
278
299
 
279
300
  const prompt = buildPhasedPrompt({
@@ -285,42 +306,55 @@ export async function runPhasedOrchestrator({
285
306
  const modelArgs = modelArgsForPhase(phase, config?.phaseModels);
286
307
  const result = await runSinglePhase({ prompt, cwd, env, teamNames, emit, modelArgs });
287
308
 
288
- // Allow daemon to track the child process for cancellation
289
309
  if (onChild) onChild(result.child);
290
310
 
291
311
  totalInputTokens += result.inputTokens;
292
312
  totalOutputTokens += result.outputTokens;
293
313
  totalSteps += result.steps;
294
314
 
295
- // Check if session memory was written
296
315
  const hasMemory = existsSync(sessionMemoryPath);
297
316
 
298
- if (result.exitCode !== 0) {
299
- if (!hasMemory) {
300
- // No memory written — can't continue, handoff
301
- handoff = true;
302
-
303
- let branch = "", diffStat = "";
304
- try { branch = execSync("git branch --show-current", { cwd, encoding: "utf-8" }).trim(); } catch {}
305
- try { diffStat = execSync("git diff --stat HEAD", { cwd, encoding: "utf-8" }).trim(); } catch {}
306
-
307
- const resumePath = join(cwd, ".agentdesk-resume.md");
308
- try {
309
- writeFileSync(resumePath, [
310
- `# AgentDesk Resume — ${taskId}`,
311
- ``, `Session: ${sessionUrl}`, `Phase: ${phase}`,
312
- `Date: ${new Date().toISOString()}`,
313
- ``, `## Branch`, branch || "(none)",
314
- ``, `## Uncommitted changes`, diffStat || "(none)",
315
- ``, `## Notes`, `Phased session interrupted during ${phase}.`,
316
- ].join("\n"));
317
- } catch {}
318
- break;
317
+ if (result.exitCode !== 0 && !hasMemory) {
318
+ handoff = true;
319
+
320
+ let branch = "", diffStat = "";
321
+ try { branch = execSync("git branch --show-current", { cwd, encoding: "utf-8" }).trim(); } catch {}
322
+ try { diffStat = execSync("git diff --stat HEAD", { cwd, encoding: "utf-8" }).trim(); } catch {}
323
+
324
+ const resumePath = join(cwd, ".agentdesk-resume.md");
325
+ try {
326
+ writeFileSync(resumePath, [
327
+ `# AgentDesk Resume — ${taskId}`,
328
+ ``, `Session: ${sessionUrl}`, `Phase: ${phase}`,
329
+ `Date: ${new Date().toISOString()}`,
330
+ ``, `## Branch`, branch || "(none)",
331
+ ``, `## Uncommitted changes`, diffStat || "(none)",
332
+ ``, `## Notes`, `Phased session interrupted during ${phase}.`,
333
+ ].join("\n"));
334
+ } catch {}
335
+ break;
336
+ }
337
+
338
+ // After REVIEW, check the verdict file. NEEDS_MORE_WORK -> loop back to EXECUTION (up to MAX_REVIEW_RETRIES).
339
+ if (phase === "REVIEW") {
340
+ let verdict = "";
341
+ try {
342
+ if (existsSync(reviewVerdictPath)) verdict = readFileSync(reviewVerdictPath, "utf-8").trim();
343
+ } catch {}
344
+
345
+ const needsMoreWork = /^NEEDS_MORE_WORK\b/i.test(verdict);
346
+ if (needsMoreWork && reviewRetries < MAX_REVIEW_RETRIES) {
347
+ reviewRetries++;
348
+ emit({ type: "agent:message", agent: "Jane", tag: "SAY",
349
+ message: `Review flagged gaps — returning to EXECUTION (retry ${reviewRetries}/${MAX_REVIEW_RETRIES}).` });
350
+ queue.unshift("EXECUTION", "REVIEW"); // redo execution, then review again
319
351
  }
320
- // Memory exists — continue to next phase despite non-zero exit
321
352
  }
322
353
  }
323
354
 
355
+ // Clean up internal marker files regardless of outcome
356
+ try { if (existsSync(reviewVerdictPath)) unlinkSync(reviewVerdictPath); } catch {}
357
+
324
358
  const duration = `${((Date.now() - startTime) / 1000).toFixed(1)}s`;
325
359
 
326
360
  if (handoff) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kendoo.agentdesk/agentdesk",
3
- "version": "0.14.4",
3
+ "version": "0.15.0",
4
4
  "description": "AI team orchestrator for Claude Code — run collaborative agent sessions from your terminal",
5
5
  "type": "module",
6
6
  "bin": {
package/prompts/phased.md CHANGED
@@ -362,24 +362,7 @@ After completing work, agents post brief comments.
362
362
 
363
363
  {{EXECUTION_STEPS}}
364
364
 
365
- ---
366
-
367
- ## SUMMARY
368
-
369
- Jane dictates the summary content; Dennis executes the tracker commands:
370
- 1. Verify Bart posted the PR link. If not, Dennis posts it now.
371
- 2. Dennis transitions the task to "In Review".
372
- 3. Jane dictates the final summary text (product outcome in non-technical terms). Dennis posts the comment with:
373
- - **What was done**: Brief summary of changes
374
- - **What was omitted**: Anything skipped or deferred
375
- - **Manual steps**: Actions the developer must perform
376
- - **PR link**
377
- - **Session link**: {{SESSION_URL}}
378
-
379
- Print:
380
- echo "Team Session Complete."
381
- echo "Task: {{TASK_ID}}"
382
- echo "Status: Ready for review."
365
+ Do NOT post final tracker summaries or transition state here — the SUMMARY phase owns all final tracker writes.
383
366
 
384
367
  ## SESSION MEMORY UPDATE (MANDATORY)
385
368
 
@@ -395,8 +378,171 @@ Before finishing, update `.agentdesk/session-memory.md`:
395
378
  - <test results>
396
379
  - <issues found and fixed>
397
380
 
381
+ ## Next Phase: REVIEW
382
+ - <anything the reviewer should pay extra attention to>
383
+ ```
384
+ {{/PHASE_EXECUTION}}
385
+
386
+ {{#PHASE_REVIEW}}
387
+ You are running **Phase 4: REVIEW** of a phased team session with {{AGENT_COUNT}} agents.
388
+
389
+ Task: {{TASK_ID}}
390
+ {{TASK_LINK}}
391
+
392
+ The agents are:
393
+
394
+ {{AGENT_LIST}}
395
+
396
+ You role-play all {{AGENT_COUNT}} agents. Jane leads. Be concise.
397
+
398
+ Speaking order:
399
+ {{SPEAKING_ORDER}}
400
+
401
+ ## GROUND RULES
402
+
403
+ {{GROUND_RULES}}
404
+
405
+ ## CRITICAL: NO CODE CHANGES IN THIS PHASE
406
+
407
+ This is a hard constraint:
408
+
409
+ - Do NOT call Edit, Write, or any tool that modifies code, configuration, or documentation files.
410
+ - Read-only tools are allowed: Read, Glob, Grep, and Bash for read-only commands (`git diff`, `git log`, `gh pr view`, `ls`, etc.).
411
+ - The REVIEW phase ONLY identifies gaps and writes a verdict. Dennis does NOT fix anything here — if work is needed, it happens back in EXECUTION.
412
+ - This is NOT a QA round. Unit tests and Bart's QA already ran in EXECUTION. Focus on higher-level completeness.
413
+
414
+ ## YOUR MISSION: REVIEW
415
+
416
+ Re-read the session memory and the actual implementation (`git diff`, changed files). Then the team assesses:
417
+
418
+ 1. **Jane (product):** Does the implementation meet the acceptance criteria stated in INTAKE? Is any requirement missed or silently deferred?
419
+ 2. **Dennis (engineering):** Does the code match the PLAN? Are there obvious gaps — partially-implemented helpers, dead branches, TODOs left in place, error paths not wired up?
420
+ 3. **Sam (quality):** Are there hidden cross-cutting concerns the team missed — docs that should be updated (README, CLAUDE.md, guide/help text), changelog entries, config schema bumps, migration notes, dependent callers?
421
+ 4. **Bart (PR):** Is the PR description accurate? Does it reference the task? Are screenshots attached where expected?
422
+
423
+ After each agent contributes, Jane calls the verdict.
424
+
425
+ ## VERDICT FILE (MANDATORY)
426
+
427
+ You MUST write `.agentdesk/review-verdict.md` using the Write tool. The FIRST LINE of the file is one of:
428
+
429
+ - `APPROVED` — implementation meets the bar; proceed to SUMMARY.
430
+ - `NEEDS_MORE_WORK` — gaps identified; the orchestrator will loop back to EXECUTION.
431
+
432
+ Then, on the following lines, list the findings (empty list for APPROVED):
433
+
434
+ ```markdown
435
+ APPROVED
436
+ or
437
+ NEEDS_MORE_WORK
438
+
439
+ ## Findings
440
+ - <specific, actionable item>
441
+ - <specific, actionable item>
442
+
443
+ ## Out of scope (deferred to future work)
444
+ - <items noted but intentionally NOT addressed now>
445
+ ```
446
+
447
+ Be strict but not pedantic. Do NOT flag stylistic preferences or speculative refactors — only actual gaps against the task requirements and the PLAN.
448
+
449
+ ## SESSION MEMORY UPDATE (MANDATORY)
450
+
451
+ Also update `.agentdesk/session-memory.md`:
452
+
453
+ ```markdown
454
+ ## Review
455
+ - Verdict: APPROVED | NEEDS_MORE_WORK
456
+ - Findings: <bulleted summary of gaps, or "none">
457
+ - Out of scope: <items explicitly deferred>
458
+ ```
459
+ {{/PHASE_REVIEW}}
460
+
461
+ {{#PHASE_SUMMARY}}
462
+ You are running **Phase 5: SUMMARY** of a phased team session with {{AGENT_COUNT}} agents.
463
+
464
+ Task: {{TASK_ID}}
465
+ {{TASK_LINK}}
466
+
467
+ The agents are:
468
+
469
+ {{AGENT_LIST}}
470
+
471
+ Jane dictates the narrative; Dennis executes the tracker writes.
472
+
473
+ ## GROUND RULES
474
+
475
+ {{GROUND_RULES}}
476
+
477
+ ## RULES
478
+
479
+ - Do NOT modify code, tests, or configuration. This phase writes messages only.
480
+ - Read-only exploration (`git diff`, `git log`, `gh pr view`) is allowed to gather accurate numbers for the summary.
481
+
482
+ {{#LINEAR}}
483
+ ## LINEAR INTEGRATION
484
+
485
+ - Endpoint: https://api.linear.app/graphql
486
+ - Auth header: Authorization: $LINEAR_API_KEY
487
+
488
+ ### Tracker actions for SUMMARY
489
+
490
+ 1. Verify Bart posted the PR link in EXECUTION. If not, Dennis posts it now via `attachmentCreate`.
491
+ 2. Dennis transitions the task to "In Review":
492
+ ```
493
+ mutation { issueUpdate(id: "$ISSUE_ID", input: { stateId: "$IN_REVIEW_STATE_ID" }) { success } }
494
+ ```
495
+ 3. Dennis posts the final session-end comment with session link: {{SESSION_URL}}
496
+ {{/LINEAR}}
497
+
498
+ {{#JIRA}}
499
+ ## JIRA INTEGRATION
500
+
501
+ - Endpoint: {{JIRA_BASE_URL}}/rest/api/3/issue/{{TASK_ID}}
502
+ - Auth: Basic auth with $JIRA_EMAIL:$JIRA_API_TOKEN
503
+ - Use `inlineCard` nodes for URLs in ADF format.
504
+
505
+ ### Tracker actions for SUMMARY
506
+
507
+ 1. Verify PR link is attached; attach it now if missing.
508
+ 2. Transition the task to "In Review".
509
+ 3. Post the final session-end comment with inlineCard session link: {{SESSION_URL}}
510
+ {{/JIRA}}
511
+
512
+ {{#GITHUB}}
513
+ ## GITHUB ISSUES INTEGRATION
514
+
515
+ - Comment: `gh issue comment {{TASK_ID}} --body "..."`
516
+
517
+ ### Tracker actions for SUMMARY
518
+
519
+ 1. Ensure the PR references the issue ("Closes #{{TASK_ID}}").
520
+ 2. Post the final session-end comment with session link: {{SESSION_URL}}
521
+ {{/GITHUB}}
522
+
523
+ ## YOUR MISSION: SUMMARY
524
+
525
+ Jane dictates the final summary content (product outcome in non-technical terms). Dennis executes all tracker writes. The summary comment must include:
526
+
527
+ - **What was done**: Brief, outcome-focused summary of the changes
528
+ - **What was omitted / deferred**: Anything from REVIEW's "Out of scope" list, plus anything the team explicitly skipped
529
+ - **Manual steps**: Actions the developer must perform (migrations, config, deploys)
530
+ - **PR link**
531
+ - **Session link**: {{SESSION_URL}}
532
+
533
+ Then print:
534
+ echo "Team Session Complete."
535
+ echo "Task: {{TASK_ID}}"
536
+ echo "Status: Ready for review."
537
+
538
+ ## SESSION MEMORY UPDATE (MANDATORY)
539
+
540
+ Finalize `.agentdesk/session-memory.md`:
541
+
542
+ ```markdown
398
543
  ## Final Status
399
544
  - <task status>
400
- - <any remaining work>
545
+ - <PR link>
546
+ - <deferred items>
401
547
  ```
402
- {{/PHASE_EXECUTION}}
548
+ {{/PHASE_SUMMARY}}