cclaw-cli 0.51.29 → 0.55.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/README.md +22 -16
  2. package/dist/artifact-linter/brainstorm.d.ts +2 -0
  3. package/dist/artifact-linter/brainstorm.js +245 -0
  4. package/dist/artifact-linter/design.d.ts +2 -0
  5. package/dist/artifact-linter/design.js +323 -0
  6. package/dist/artifact-linter/plan.d.ts +2 -0
  7. package/dist/artifact-linter/plan.js +162 -0
  8. package/dist/artifact-linter/review-army.d.ts +24 -0
  9. package/dist/artifact-linter/review-army.js +365 -0
  10. package/dist/artifact-linter/review.d.ts +2 -0
  11. package/dist/artifact-linter/review.js +65 -0
  12. package/dist/artifact-linter/scope.d.ts +2 -0
  13. package/dist/artifact-linter/scope.js +115 -0
  14. package/dist/artifact-linter/shared.d.ts +246 -0
  15. package/dist/artifact-linter/shared.js +1488 -0
  16. package/dist/artifact-linter/ship.d.ts +2 -0
  17. package/dist/artifact-linter/ship.js +46 -0
  18. package/dist/artifact-linter/spec.d.ts +2 -0
  19. package/dist/artifact-linter/spec.js +108 -0
  20. package/dist/artifact-linter/tdd.d.ts +2 -0
  21. package/dist/artifact-linter/tdd.js +124 -0
  22. package/dist/artifact-linter.d.ts +4 -76
  23. package/dist/artifact-linter.js +56 -2949
  24. package/dist/cli.d.ts +2 -18
  25. package/dist/cli.js +8 -246
  26. package/dist/codex-feature-flag.d.ts +1 -1
  27. package/dist/codex-feature-flag.js +1 -1
  28. package/dist/config.d.ts +3 -2
  29. package/dist/config.js +67 -3
  30. package/dist/constants.d.ts +1 -7
  31. package/dist/constants.js +9 -15
  32. package/dist/content/cancel-command.js +2 -2
  33. package/dist/content/closeout-guidance.js +13 -10
  34. package/dist/content/core-agents.d.ts +18 -0
  35. package/dist/content/core-agents.js +51 -7
  36. package/dist/content/decision-protocol.d.ts +1 -1
  37. package/dist/content/decision-protocol.js +1 -1
  38. package/dist/content/examples.js +6 -6
  39. package/dist/content/harness-doc.js +20 -2
  40. package/dist/content/hook-inline-snippets.d.ts +17 -4
  41. package/dist/content/hook-inline-snippets.js +218 -5
  42. package/dist/content/hook-manifest.d.ts +2 -2
  43. package/dist/content/hook-manifest.js +2 -2
  44. package/dist/content/hooks.d.ts +1 -0
  45. package/dist/content/hooks.js +32 -137
  46. package/dist/content/idea-command.d.ts +8 -0
  47. package/dist/content/{ideate-command.js → idea-command.js} +57 -50
  48. package/dist/content/idea-frames.d.ts +31 -0
  49. package/dist/content/{ideate-frames.js → idea-frames.js} +9 -9
  50. package/dist/content/idea-ranking.d.ts +25 -0
  51. package/dist/content/{ideate-ranking.js → idea-ranking.js} +5 -5
  52. package/dist/content/iron-laws.d.ts +0 -1
  53. package/dist/content/iron-laws.js +31 -16
  54. package/dist/content/learnings.js +1 -1
  55. package/dist/content/meta-skill.js +11 -13
  56. package/dist/content/node-hooks.d.ts +10 -0
  57. package/dist/content/node-hooks.js +45 -11
  58. package/dist/content/opencode-plugin.js +3 -3
  59. package/dist/content/session-hooks.js +1 -1
  60. package/dist/content/skills.js +19 -7
  61. package/dist/content/stage-command.js +1 -1
  62. package/dist/content/stage-schema.js +44 -2
  63. package/dist/content/stages/_lint-metadata/index.js +26 -2
  64. package/dist/content/stages/brainstorm.js +13 -7
  65. package/dist/content/stages/design.js +16 -11
  66. package/dist/content/stages/plan.js +9 -6
  67. package/dist/content/stages/review.js +4 -4
  68. package/dist/content/stages/schema-types.d.ts +1 -1
  69. package/dist/content/stages/scope.js +15 -12
  70. package/dist/content/stages/ship.js +2 -2
  71. package/dist/content/stages/spec.js +9 -3
  72. package/dist/content/stages/tdd.js +14 -4
  73. package/dist/content/start-command.d.ts +2 -2
  74. package/dist/content/start-command.js +24 -21
  75. package/dist/content/status-command.js +8 -8
  76. package/dist/content/subagents.js +61 -7
  77. package/dist/content/templates.d.ts +1 -1
  78. package/dist/content/templates.js +104 -152
  79. package/dist/content/tree-command.js +2 -2
  80. package/dist/content/utility-skills.d.ts +2 -2
  81. package/dist/content/utility-skills.js +2 -2
  82. package/dist/content/view-command.js +4 -2
  83. package/dist/delegation.d.ts +2 -0
  84. package/dist/delegation.js +2 -1
  85. package/dist/early-loop.d.ts +66 -0
  86. package/dist/early-loop.js +275 -0
  87. package/dist/flow-state.d.ts +1 -1
  88. package/dist/flow-state.js +1 -1
  89. package/dist/gate-evidence.d.ts +8 -0
  90. package/dist/gate-evidence.js +141 -5
  91. package/dist/harness-adapters.d.ts +2 -2
  92. package/dist/harness-adapters.js +54 -122
  93. package/dist/harness-selection.d.ts +31 -0
  94. package/dist/harness-selection.js +214 -0
  95. package/dist/install.js +166 -38
  96. package/dist/internal/advance-stage/advance.d.ts +50 -0
  97. package/dist/internal/advance-stage/advance.js +480 -0
  98. package/dist/internal/advance-stage/cancel-run.d.ts +8 -0
  99. package/dist/internal/advance-stage/cancel-run.js +19 -0
  100. package/dist/internal/advance-stage/flow-state-coercion.d.ts +3 -0
  101. package/dist/internal/advance-stage/flow-state-coercion.js +81 -0
  102. package/dist/internal/advance-stage/helpers.d.ts +14 -0
  103. package/dist/internal/advance-stage/helpers.js +145 -0
  104. package/dist/internal/advance-stage/hook.d.ts +8 -0
  105. package/dist/internal/advance-stage/hook.js +40 -0
  106. package/dist/internal/advance-stage/parsers.d.ts +54 -0
  107. package/dist/internal/advance-stage/parsers.js +307 -0
  108. package/dist/internal/advance-stage/review-loop.d.ts +7 -0
  109. package/dist/internal/advance-stage/review-loop.js +170 -0
  110. package/dist/internal/advance-stage/rewind.d.ts +14 -0
  111. package/dist/internal/advance-stage/rewind.js +108 -0
  112. package/dist/internal/advance-stage/start-flow.d.ts +11 -0
  113. package/dist/internal/advance-stage/start-flow.js +136 -0
  114. package/dist/internal/advance-stage/verify.d.ts +29 -0
  115. package/dist/internal/advance-stage/verify.js +225 -0
  116. package/dist/internal/advance-stage.js +21 -1470
  117. package/dist/internal/compound-readiness.d.ts +1 -1
  118. package/dist/internal/compound-readiness.js +2 -2
  119. package/dist/internal/early-loop-status.d.ts +7 -0
  120. package/dist/internal/early-loop-status.js +90 -0
  121. package/dist/internal/runtime-integrity.d.ts +7 -0
  122. package/dist/internal/runtime-integrity.js +288 -0
  123. package/dist/internal/tdd-red-evidence.js +1 -1
  124. package/dist/knowledge-store.d.ts +3 -8
  125. package/dist/knowledge-store.js +16 -29
  126. package/dist/managed-resources.js +24 -2
  127. package/dist/policy.js +5 -7
  128. package/dist/run-archive.d.ts +1 -1
  129. package/dist/run-archive.js +16 -16
  130. package/dist/run-persistence.js +112 -12
  131. package/dist/tdd-cycle.d.ts +3 -3
  132. package/dist/tdd-cycle.js +1 -1
  133. package/dist/types.d.ts +18 -10
  134. package/package.json +1 -1
  135. package/dist/content/finish-command.d.ts +0 -2
  136. package/dist/content/finish-command.js +0 -26
  137. package/dist/content/ideate-command.d.ts +0 -8
  138. package/dist/content/ideate-frames.d.ts +0 -31
  139. package/dist/content/ideate-ranking.d.ts +0 -25
  140. package/dist/content/next-command.d.ts +0 -20
  141. package/dist/content/next-command.js +0 -298
  142. package/dist/content/seed-shelf.d.ts +0 -36
  143. package/dist/content/seed-shelf.js +0 -301
  144. package/dist/content/stage-common-guidance.d.ts +0 -1
  145. package/dist/content/stage-common-guidance.js +0 -106
  146. package/dist/doctor-registry.d.ts +0 -10
  147. package/dist/doctor-registry.js +0 -186
  148. package/dist/doctor.d.ts +0 -17
  149. package/dist/doctor.js +0 -2206
  150. package/dist/internal/hook-manifest.d.ts +0 -16
  151. package/dist/internal/hook-manifest.js +0 -77
package/dist/cli.d.ts CHANGED
@@ -1,18 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import type { FlowTrack, HarnessId } from "./types.js";
3
3
  import type { ArchiveDisposition } from "./runs.js";
4
- type CommandName = "init" | "sync" | "doctor" | "upgrade" | "uninstall" | "archive" | "internal";
4
+ export { parseHarnessSelectionAnswer } from "./harness-selection.js";
5
+ type CommandName = "init" | "sync" | "upgrade" | "uninstall" | "archive" | "internal";
5
6
  interface ParsedArgs {
6
7
  command?: CommandName;
7
8
  harnesses?: HarnessId[];
8
9
  track?: FlowTrack;
9
10
  dryRun?: boolean;
10
11
  interactive?: boolean;
11
- reconcileGates?: boolean;
12
- doctorJson?: boolean;
13
- doctorExplain?: boolean;
14
- doctorQuiet?: boolean;
15
- doctorOnly?: string[];
16
12
  archiveName?: string;
17
13
  archiveSkipRetro?: boolean;
18
14
  archiveSkipRetroReason?: string;
@@ -27,17 +23,5 @@ export declare function usage(): string;
27
23
  declare function parseHarnesses(raw: string): HarnessId[];
28
24
  declare function parseTrack(raw: string): FlowTrack;
29
25
  declare function parseArchiveDisposition(raw: string): ArchiveDisposition;
30
- export type HarnessSelectionAnswer = {
31
- kind: "accept";
32
- } | {
33
- kind: "all";
34
- } | {
35
- kind: "toggle";
36
- indexes: number[];
37
- } | {
38
- kind: "invalid";
39
- message: string;
40
- };
41
- export declare function parseHarnessSelectionAnswer(raw: string): HarnessSelectionAnswer;
42
26
  declare function parseArgs(argv: string[]): ParsedArgs;
43
27
  export { parseArgs, parseArchiveDisposition, parseHarnesses, parseTrack };
package/dist/cli.js CHANGED
@@ -4,7 +4,6 @@ import path from "node:path";
4
4
  import { existsSync, realpathSync } from "node:fs";
5
5
  import { createInterface } from "node:readline/promises";
6
6
  import { fileURLToPath } from "node:url";
7
- import { doctorChecks, doctorSucceeded } from "./doctor.js";
8
7
  import { initCclaw, syncCclaw, uninstallCclaw, upgradeCclaw } from "./install.js";
9
8
  import { error, info } from "./logger.js";
10
9
  import { FLOW_TRACKS, HARNESS_IDS } from "./types.js";
@@ -13,12 +12,13 @@ import { CCLAW_VERSION, RUNTIME_ROOT } from "./constants.js";
13
12
  import { createDefaultConfig, readConfig } from "./config.js";
14
13
  import { detectHarnesses } from "./init-detect.js";
15
14
  import { HARNESS_ADAPTERS } from "./harness-adapters.js";
15
+ import { promptHarnessSelectionChecklist } from "./harness-selection.js";
16
+ export { parseHarnessSelectionAnswer } from "./harness-selection.js";
16
17
  import { classifyCodexHooksFlag, codexConfigPath, patchCodexHooksFlag, readCodexConfig, writeCodexConfig } from "./codex-feature-flag.js";
17
18
  import { runInternalCommand } from "./internal/advance-stage.js";
18
19
  const INSTALLER_COMMANDS = [
19
20
  "init",
20
21
  "sync",
21
- "doctor",
22
22
  "upgrade",
23
23
  "uninstall",
24
24
  "archive",
@@ -40,12 +40,6 @@ Commands:
40
40
  sync Reconcile generated runtime files with the current config.
41
41
  Flags: --harnesses=<list> Update configured harnesses before syncing.
42
42
  --interactive Pick harnesses from a numbered TTY menu.
43
- doctor Check install/runtime wiring and print concrete fixes for failures.
44
- Flags: --explain Include docs pointers for every check.
45
- --json Emit machine-readable check results.
46
- --quiet Show only failing checks.
47
- --only=<filter> Limit displayed checks (error,warning,hook:,state:,...).
48
- --reconcile-gates Refresh derived gate status before checking; does not repair missing artifacts/tests.
49
43
  upgrade Refresh generated files in .cclaw. Preserves your config.yaml.
50
44
  archive Archive the active run and reset flow state for the next run.
51
45
  Flags: --name=<slug> Override archive folder suffix.
@@ -67,10 +61,9 @@ Examples:
67
61
  npx cclaw-cli archive --disposition=cancelled --reason="deprioritized"
68
62
  npx cclaw-cli upgrade
69
63
 
70
- Happy-path work happens inside your harness via /cc, /cc-next,
71
- /cc-ideate, /cc-view, /cc-finish, and /cc-cancel. Doctor is an operator/support surface:
72
- it verifies install/runtime wiring, but a real harness smoke test is
73
- still needed to prove provider auth and model execution.
64
+ Happy-path work happens inside your harness via /cc, /cc-idea,
65
+ and /cc-cancel. Installer/support operations are init/sync/upgrade/uninstall
66
+ plus explicit archive actions.
74
67
 
75
68
  Docs: https://github.com/zuevrs/cclaw
76
69
  Local: README.md and generated .cclaw/skills/*.md
@@ -137,7 +130,7 @@ function buildInitSurfacePreview(harnesses) {
137
130
  ".cclaw/agents/*.md",
138
131
  ".cclaw/hooks/*",
139
132
  ".cclaw/rules/**",
140
- ".cclaw/runs/**",
133
+ ".cclaw/archive/**",
141
134
  ".cclaw/artifacts/**",
142
135
  ".cclaw/knowledge.jsonl",
143
136
  ".cclaw/state/*.json|*.jsonl",
@@ -171,93 +164,8 @@ function buildInitSurfacePreview(harnesses) {
171
164
  }
172
165
  return lines;
173
166
  }
174
- function harnessLabel(harness) {
175
- const adapter = HARNESS_ADAPTERS[harness];
176
- const tier = adapter ? `${adapter.reality.declaredSupport}, ${adapter.capabilities.hookSurface} hooks` : "supported";
177
- return `${harness} (${tier})`;
178
- }
179
- function selectedHarnessPreview(harnesses) {
180
- return harnesses.length > 0 ? harnesses.join(", ") : "none";
181
- }
182
- export function parseHarnessSelectionAnswer(raw) {
183
- const answer = raw.trim().toLowerCase();
184
- if (answer.length === 0)
185
- return { kind: "accept" };
186
- if (answer === "all")
187
- return { kind: "all" };
188
- if (answer === "none") {
189
- return { kind: "invalid", message: "Zero harnesses is not supported. Select at least one harness." };
190
- }
191
- const parts = answer.split(",").map((part) => part.trim()).filter(Boolean);
192
- const indexes = parts.map((part) => Number.parseInt(part, 10));
193
- if (indexes.some((value) => !Number.isInteger(value) || value < 1 || value > HARNESS_IDS.length)) {
194
- return { kind: "invalid", message: `Invalid selection. Use numbers 1-${HARNESS_IDS.length}, comma-separated.` };
195
- }
196
- return { kind: "toggle", indexes };
197
- }
198
- async function promptHarnessSelection(defaults, ctx, label = "Harness selection") {
199
- const rl = createInterface({
200
- input: process.stdin,
201
- output: ctx.stdout
202
- });
203
- const defaultSet = new Set(defaults.harnesses);
204
- const selected = new Set(defaults.harnesses.length > 0 ? defaults.harnesses : HARNESS_IDS);
205
- const detected = new Set(defaults.detectedHarnesses ?? []);
206
- const current = new Set(defaults.currentHarnesses ?? []);
207
- const printMenu = () => {
208
- ctx.stdout.write(`\n${label}\n`);
209
- ctx.stdout.write(`Detected: ${selectedHarnessPreview(defaults.detectedHarnesses ?? [])}\n`);
210
- ctx.stdout.write(`Current: ${selectedHarnessPreview(defaults.currentHarnesses ?? [])}\n`);
211
- ctx.stdout.write(`Supported harnesses and target paths:\n`);
212
- HARNESS_IDS.forEach((harness, index) => {
213
- const adapter = HARNESS_ADAPTERS[harness];
214
- const markers = [
215
- detected.has(harness) ? "detected" : "",
216
- current.has(harness) ? "current" : "",
217
- defaultSet.has(harness) ? "default" : ""
218
- ].filter(Boolean).join(", ");
219
- const checked = selected.has(harness) ? "x" : " ";
220
- ctx.stdout.write(` ${index + 1}. [${checked}] ${harnessLabel(harness)} -> ${adapter.commandDir}${markers ? ` (${markers})` : ""}\n`);
221
- });
222
- ctx.stdout.write("Enter numbers to toggle (for example 1,3), 'all', or press Enter to accept.\n");
223
- };
224
- try {
225
- while (true) {
226
- printMenu();
227
- const answer = await rl.question(`Selected [${[...selected].join(",") || "select at least one"}]: `);
228
- const parsedAnswer = parseHarnessSelectionAnswer(answer);
229
- if (parsedAnswer.kind === "accept") {
230
- if (selected.size === 0) {
231
- ctx.stdout.write("Select at least one harness.\n");
232
- continue;
233
- }
234
- return HARNESS_IDS.filter((harness) => selected.has(harness));
235
- }
236
- if (parsedAnswer.kind === "all") {
237
- HARNESS_IDS.forEach((harness) => selected.add(harness));
238
- continue;
239
- }
240
- if (parsedAnswer.kind === "invalid") {
241
- ctx.stdout.write(`${parsedAnswer.message}\n`);
242
- continue;
243
- }
244
- for (const index of parsedAnswer.indexes) {
245
- const harness = HARNESS_IDS[index - 1];
246
- if (!harness)
247
- continue;
248
- if (selected.has(harness))
249
- selected.delete(harness);
250
- else
251
- selected.add(harness);
252
- }
253
- }
254
- }
255
- finally {
256
- rl.close();
257
- }
258
- }
259
167
  async function promptInitConfig(defaults, ctx) {
260
- const harnesses = await promptHarnessSelection(defaults, ctx, "Initial cclaw harnesses");
168
+ const harnesses = await promptHarnessSelectionChecklist(defaults, ctx, "Initial cclaw harnesses");
261
169
  return { harnesses };
262
170
  }
263
171
  /**
@@ -392,105 +300,13 @@ async function resolveSyncInputs(parsed, ctx) {
392
300
  const detectedHarnesses = await detectHarnesses(ctx.cwd);
393
301
  const defaults = detectedHarnesses.length > 0 ? detectedHarnesses : currentHarnesses.length > 0 ? currentHarnesses : HARNESS_IDS.slice();
394
302
  return {
395
- harnesses: await promptHarnessSelection({
303
+ harnesses: await promptHarnessSelectionChecklist({
396
304
  harnesses: defaults,
397
305
  detectedHarnesses,
398
306
  currentHarnesses
399
307
  }, ctx, "Sync harness reconfiguration")
400
308
  };
401
309
  }
402
- function parseDoctorOnly(raw) {
403
- return raw
404
- .split(",")
405
- .map((item) => item.trim().toLowerCase())
406
- .filter((item) => item.length > 0);
407
- }
408
- function filterDoctorChecks(checks, filters) {
409
- if (!filters || filters.length === 0) {
410
- return checks;
411
- }
412
- return checks.filter((check) => {
413
- const name = check.name.toLowerCase();
414
- return filters.some((filter) => {
415
- if (filter === "error" || filter === "warning" || filter === "info") {
416
- return check.severity === filter;
417
- }
418
- return name.includes(filter);
419
- });
420
- });
421
- }
422
- function doctorCountsBySeverity(checks) {
423
- const result = {
424
- error: { total: 0, failing: 0 },
425
- warning: { total: 0, failing: 0 },
426
- info: { total: 0, failing: 0 }
427
- };
428
- for (const check of checks) {
429
- const bucket = result[check.severity];
430
- bucket.total += 1;
431
- if (!check.ok) {
432
- bucket.failing += 1;
433
- }
434
- }
435
- return result;
436
- }
437
- const DOCTOR_ACTION_GROUP_LABELS = {
438
- sync: "Can fix with cclaw sync",
439
- "user-action": "Requires user action",
440
- "stage-work": "Requires stage work",
441
- informational: "Informational warning"
442
- };
443
- function doctorActionGroupOrder(group) {
444
- return group === "sync" ? 0 : group === "user-action" ? 1 : group === "stage-work" ? 2 : 3;
445
- }
446
- function printDoctorText(ctx, checks, options) {
447
- const orderedSeverities = ["error", "warning", "info"];
448
- const view = options.quiet ? checks.filter((check) => !check.ok) : checks;
449
- const actionGroups = [...new Set(view.map((check) => check.actionGroup))]
450
- .sort((left, right) => doctorActionGroupOrder(left) - doctorActionGroupOrder(right));
451
- for (const actionGroup of actionGroups) {
452
- const groupChecks = view.filter((check) => check.actionGroup === actionGroup);
453
- const failingInGroup = groupChecks.filter((check) => !check.ok).length;
454
- ctx.stdout.write(`
455
- [${DOCTOR_ACTION_GROUP_LABELS[actionGroup]}] ${failingInGroup}/${groupChecks.length} failing
456
- `);
457
- for (const severity of orderedSeverities) {
458
- const inBucket = groupChecks.filter((check) => check.severity === severity);
459
- if (inBucket.length === 0)
460
- continue;
461
- ctx.stdout.write(` ${severity.toUpperCase()}
462
- `);
463
- for (const check of inBucket) {
464
- const status = check.ok ? "PASS" : "FAIL";
465
- ctx.stdout.write(` ${status} ${check.name} :: ${check.summary}
466
- `);
467
- if (!options.quiet) {
468
- ctx.stdout.write(` details: ${check.details}
469
- `);
470
- }
471
- if (!check.ok || options.explain) {
472
- ctx.stdout.write(` next action: ${check.fix}
473
- `);
474
- if (check.docRef) {
475
- ctx.stdout.write(` reference: ${check.docRef}
476
- `);
477
- }
478
- }
479
- }
480
- }
481
- }
482
- const counts = doctorCountsBySeverity(checks);
483
- const failingErrors = checks.filter((check) => check.severity === "error" && !check.ok).length;
484
- ctx.stdout.write(`\nTotals: error ${counts.error.failing}/${counts.error.total} failing, ` +
485
- `warning ${counts.warning.failing}/${counts.warning.total} failing, ` +
486
- `info ${counts.info.failing}/${counts.info.total} failing\n`);
487
- if (failingErrors > 0) {
488
- ctx.stdout.write(`Doctor status: BLOCKED (${failingErrors} failing error checks)\n`);
489
- }
490
- else {
491
- ctx.stdout.write("Doctor status: HEALTHY (no failing error checks)\n");
492
- }
493
- }
494
310
  function parseArgs(argv) {
495
311
  const parsed = {};
496
312
  const helpFlag = argv.find((arg) => arg === "--help" || arg === "-h");
@@ -522,13 +338,6 @@ function parseArgs(argv) {
522
338
  flag === "--no-interactive" ||
523
339
  (parsed.command === "init" && flag === "--dry-run");
524
340
  }
525
- if (parsed.command === "doctor") {
526
- return flag === "--reconcile-gates" ||
527
- flag === "--json" ||
528
- flag === "--explain" ||
529
- flag === "--quiet" ||
530
- flag.startsWith("--only=");
531
- }
532
341
  if (parsed.command === "archive") {
533
342
  return flag.startsWith("--name=") ||
534
343
  flag === "--skip-retro" ||
@@ -565,26 +374,6 @@ function parseArgs(argv) {
565
374
  parsed.dryRun = true;
566
375
  continue;
567
376
  }
568
- if (flag === "--reconcile-gates") {
569
- parsed.reconcileGates = true;
570
- continue;
571
- }
572
- if (flag === "--json") {
573
- parsed.doctorJson = true;
574
- continue;
575
- }
576
- if (flag === "--explain") {
577
- parsed.doctorExplain = true;
578
- continue;
579
- }
580
- if (flag === "--quiet") {
581
- parsed.doctorQuiet = true;
582
- continue;
583
- }
584
- if (flag.startsWith("--only=")) {
585
- parsed.doctorOnly = parseDoctorOnly(flag.replace("--only=", ""));
586
- continue;
587
- }
588
377
  if (flag.startsWith("--name=")) {
589
378
  parsed.archiveName = flag.replace("--name=", "").trim();
590
379
  continue;
@@ -670,33 +459,6 @@ async function runCommand(parsed, ctx) {
670
459
  info(ctx, `Synchronized harness shims from current .cclaw config${harnessNote}`);
671
460
  return 0;
672
461
  }
673
- if (command === "doctor") {
674
- const checks = await doctorChecks(ctx.cwd, {
675
- reconcileCurrentStageGates: parsed.reconcileGates === true
676
- });
677
- const filteredChecks = filterDoctorChecks(checks, parsed.doctorOnly);
678
- const explain = parsed.doctorExplain === true;
679
- const quiet = parsed.doctorQuiet === true;
680
- if (parsed.doctorJson === true) {
681
- const counts = doctorCountsBySeverity(filteredChecks);
682
- ctx.stdout.write(`${JSON.stringify({
683
- ok: doctorSucceeded(filteredChecks),
684
- globalOk: doctorSucceeded(checks),
685
- filters: parsed.doctorOnly ?? [],
686
- counts,
687
- checks: filteredChecks
688
- }, null, 2)}\n`);
689
- }
690
- else {
691
- if (filteredChecks.length === 0) {
692
- ctx.stdout.write("No checks matched the --only filter.\n");
693
- }
694
- else {
695
- printDoctorText(ctx, filteredChecks, { explain, quiet });
696
- }
697
- }
698
- return doctorSucceeded(filteredChecks) ? 0 : 2;
699
- }
700
462
  if (command === "upgrade") {
701
463
  await upgradeCclaw(ctx.cwd);
702
464
  info(ctx, "Upgraded .cclaw runtime and regenerated generated files");
@@ -11,7 +11,7 @@
11
11
  * ```
12
12
  *
13
13
  * in `$CODEX_HOME/config.toml` (default: `~/.codex/config.toml`).
14
- * cclaw init/sync can prompt the user to flip this flag for them; `cclaw doctor --explain` reports the concrete repair when it is missing;
14
+ * cclaw init/sync can prompt the user to flip this flag for them; sync/runtime diagnostics report the concrete repair when it is missing;
15
15
  * this module owns the detection / mutation code so the prompt logic in
16
16
  * `cli.ts` stays small and testable.
17
17
  *
@@ -11,7 +11,7 @@
11
11
  * ```
12
12
  *
13
13
  * in `$CODEX_HOME/config.toml` (default: `~/.codex/config.toml`).
14
- * cclaw init/sync can prompt the user to flip this flag for them; `cclaw doctor --explain` reports the concrete repair when it is missing;
14
+ * cclaw init/sync can prompt the user to flip this flag for them; sync/runtime diagnostics report the concrete repair when it is missing;
15
15
  * this module owns the detection / mutation code so the prompt logic in
16
16
  * `cli.ts` stays small and testable.
17
17
  *
package/dist/config.d.ts CHANGED
@@ -24,9 +24,10 @@ export declare const DEFAULT_TDD_TEST_PATH_PATTERNS: readonly string[];
24
24
  export declare const DEFAULT_TDD_TEST_GLOBS: readonly string[];
25
25
  export declare const DEFAULT_TDD_PRODUCTION_PATH_PATTERNS: readonly string[];
26
26
  export declare const DEFAULT_COMPOUND_RECURRENCE_THRESHOLD = 3;
27
+ export declare const DEFAULT_EARLY_LOOP_MAX_ITERATIONS = 3;
27
28
  /**
28
29
  * Populated runtime view of config values that downstream callers (install,
29
- * observe, doctor) consume. Always has the derived guard modes populated,
30
+ * observe, sync/runtime checks) consume. Always has the derived guard modes populated,
30
31
  * regardless of whether the user wrote `strictness`, the legacy keys, both,
31
32
  * or neither.
32
33
  */
@@ -49,7 +50,7 @@ export declare function readConfig(projectRoot: string, options?: ReadConfigOpti
49
50
  * the user set them explicitly. Keeps the default template small and honest:
50
51
  * only knobs a new user would meaningfully flip show up.
51
52
  */
52
- type AdvancedConfigKey = "vcs" | "tddTestGlobs" | "tdd" | "compound" | "defaultTrack" | "languageRulePacks" | "trackHeuristics" | "sliceReview" | "ironLaws" | "optInAudits" | "reviewLoop";
53
+ type AdvancedConfigKey = "vcs" | "tddTestGlobs" | "tdd" | "compound" | "earlyLoop" | "defaultTrack" | "languageRulePacks" | "trackHeuristics" | "sliceReview" | "ironLaws" | "optInAudits" | "reviewLoop";
53
54
  /**
54
55
  * Options controlling the serialisation shape of `config.yaml`.
55
56
  *
package/dist/config.js CHANGED
@@ -21,6 +21,8 @@ const ALLOWED_CONFIG_KEYS = new Set([
21
21
  "tddTestGlobs",
22
22
  "tdd",
23
23
  "compound",
24
+ "earlyLoop",
25
+ "early_loop",
24
26
  "gitHookGuards",
25
27
  "defaultTrack",
26
28
  "languageRulePacks",
@@ -131,9 +133,10 @@ export const DEFAULT_TDD_TEST_PATH_PATTERNS = [
131
133
  export const DEFAULT_TDD_TEST_GLOBS = [...DEFAULT_TDD_TEST_PATH_PATTERNS];
132
134
  export const DEFAULT_TDD_PRODUCTION_PATH_PATTERNS = [];
133
135
  export const DEFAULT_COMPOUND_RECURRENCE_THRESHOLD = 3;
136
+ export const DEFAULT_EARLY_LOOP_MAX_ITERATIONS = 3;
134
137
  /**
135
138
  * Populated runtime view of config values that downstream callers (install,
136
- * observe, doctor) consume. Always has the derived guard modes populated,
139
+ * observe, sync/runtime checks) consume. Always has the derived guard modes populated,
137
140
  * regardless of whether the user wrote `strictness`, the legacy keys, both,
138
141
  * or neither.
139
142
  */
@@ -155,11 +158,19 @@ export function createDefaultConfig(harnesses = DEFAULT_HARNESSES, defaultTrack
155
158
  compound: {
156
159
  recurrenceThreshold: DEFAULT_COMPOUND_RECURRENCE_THRESHOLD
157
160
  },
161
+ earlyLoop: {
162
+ enabled: true,
163
+ maxIterations: DEFAULT_EARLY_LOOP_MAX_ITERATIONS
164
+ },
158
165
  gitHookGuards: false,
159
166
  defaultTrack,
160
167
  languageRulePacks: [],
161
168
  ironLaws: {
162
169
  strictLaws: []
170
+ },
171
+ optInAudits: {
172
+ scopePreAudit: false,
173
+ staleDiagramAudit: true
163
174
  }
164
175
  };
165
176
  }
@@ -327,6 +338,46 @@ export async function readConfig(projectRoot, options = {}) {
327
338
  compoundRecurrenceThreshold = compoundRaw.recurrenceThreshold;
328
339
  }
329
340
  }
341
+ const hasEarlyLoopField = Object.prototype.hasOwnProperty.call(parsed, "earlyLoop");
342
+ const hasLegacyEarlyLoopField = Object.prototype.hasOwnProperty.call(parsed, "early_loop");
343
+ if (hasEarlyLoopField && hasLegacyEarlyLoopField) {
344
+ emitConfigWarningOnce(warningState, "CCLAW_CONFIG_EARLY_LOOP_ALIAS", `[cclaw] Both "earlyLoop" and legacy "early_loop" are set in ${fullPath}. Using "earlyLoop".`);
345
+ }
346
+ const earlyLoopRaw = hasEarlyLoopField
347
+ ? parsed.earlyLoop
348
+ : parsed.early_loop;
349
+ let earlyLoopEnabled = true;
350
+ let earlyLoopMaxIterations = DEFAULT_EARLY_LOOP_MAX_ITERATIONS;
351
+ if (hasEarlyLoopField || hasLegacyEarlyLoopField) {
352
+ if (!isRecord(earlyLoopRaw)) {
353
+ throw configValidationError(fullPath, `"${hasEarlyLoopField ? "earlyLoop" : "early_loop"}" must be an object`);
354
+ }
355
+ const unknownEarlyLoopKeys = Object.keys(earlyLoopRaw).filter((key) => key !== "enabled" && key !== "maxIterations" && key !== "max_iterations");
356
+ if (unknownEarlyLoopKeys.length > 0) {
357
+ throw configValidationError(fullPath, `"${hasEarlyLoopField ? "earlyLoop" : "early_loop"}" has unknown key(s): ${unknownEarlyLoopKeys.join(", ")}`);
358
+ }
359
+ if (earlyLoopRaw.enabled !== undefined && typeof earlyLoopRaw.enabled !== "boolean") {
360
+ throw configValidationError(fullPath, `"${hasEarlyLoopField ? "earlyLoop" : "early_loop"}.enabled" must be a boolean`);
361
+ }
362
+ if (earlyLoopRaw.maxIterations !== undefined &&
363
+ earlyLoopRaw.max_iterations !== undefined &&
364
+ earlyLoopRaw.maxIterations !== earlyLoopRaw.max_iterations) {
365
+ emitConfigWarningOnce(warningState, "CCLAW_CONFIG_EARLY_LOOP_MAX_ITERATIONS_ALIAS", `[cclaw] Both "${hasEarlyLoopField ? "earlyLoop.maxIterations" : "early_loop.maxIterations"}" and "${hasEarlyLoopField ? "earlyLoop.max_iterations" : "early_loop.max_iterations"}" are set in ${fullPath}. Using "maxIterations".`);
366
+ }
367
+ const rawMaxIterations = earlyLoopRaw.maxIterations ?? earlyLoopRaw.max_iterations;
368
+ if (rawMaxIterations !== undefined &&
369
+ (typeof rawMaxIterations !== "number" ||
370
+ !Number.isInteger(rawMaxIterations) ||
371
+ rawMaxIterations < 1)) {
372
+ throw configValidationError(fullPath, `"${hasEarlyLoopField ? "earlyLoop" : "early_loop"}.maxIterations" must be a positive integer`);
373
+ }
374
+ if (typeof earlyLoopRaw.enabled === "boolean") {
375
+ earlyLoopEnabled = earlyLoopRaw.enabled;
376
+ }
377
+ if (typeof rawMaxIterations === "number") {
378
+ earlyLoopMaxIterations = rawMaxIterations;
379
+ }
380
+ }
330
381
  const gitHookGuardsRaw = parsed.gitHookGuards;
331
382
  if (Object.prototype.hasOwnProperty.call(parsed, "gitHookGuards") &&
332
383
  typeof gitHookGuardsRaw !== "boolean") {
@@ -483,7 +534,13 @@ export async function readConfig(projectRoot, options = {}) {
483
534
  : false,
484
535
  staleDiagramAudit: typeof optInAuditsRaw.staleDiagramAudit === "boolean"
485
536
  ? optInAuditsRaw.staleDiagramAudit
486
- : false
537
+ : true
538
+ };
539
+ }
540
+ if (!optInAudits) {
541
+ optInAudits = {
542
+ scopePreAudit: false,
543
+ staleDiagramAudit: true
487
544
  };
488
545
  }
489
546
  const reviewLoopRaw = parsed.reviewLoop;
@@ -544,6 +601,10 @@ export async function readConfig(projectRoot, options = {}) {
544
601
  compound: {
545
602
  recurrenceThreshold: compoundRecurrenceThreshold
546
603
  },
604
+ earlyLoop: {
605
+ enabled: earlyLoopEnabled,
606
+ maxIterations: earlyLoopMaxIterations
607
+ },
547
608
  gitHookGuards,
548
609
  defaultTrack,
549
610
  languageRulePacks,
@@ -570,6 +631,7 @@ function buildSerializableConfig(config, options = {}) {
570
631
  "tddTestGlobs",
571
632
  "tdd",
572
633
  "compound",
634
+ "earlyLoop",
573
635
  "gitHookGuards",
574
636
  "defaultTrack",
575
637
  "languageRulePacks",
@@ -626,6 +688,7 @@ export async function detectAdvancedKeys(projectRoot) {
626
688
  "tddTestGlobs",
627
689
  "tdd",
628
690
  "compound",
691
+ "earlyLoop",
629
692
  "defaultTrack",
630
693
  "languageRulePacks",
631
694
  "trackHeuristics",
@@ -636,7 +699,8 @@ export async function detectAdvancedKeys(projectRoot) {
636
699
  ];
637
700
  const present = new Set();
638
701
  for (const key of advancedCandidates) {
639
- if (Object.prototype.hasOwnProperty.call(parsedUnknown, key)) {
702
+ if (Object.prototype.hasOwnProperty.call(parsedUnknown, key) ||
703
+ (key === "earlyLoop" && Object.prototype.hasOwnProperty.call(parsedUnknown, "early_loop"))) {
640
704
  present.add(key);
641
705
  }
642
706
  }
@@ -10,16 +10,10 @@ export declare const FLOW_VERSION = "1.0.0";
10
10
  export declare const SHIP_FINALIZATION_MODES: readonly ["FINALIZE_MERGE_LOCAL", "FINALIZE_OPEN_PR", "FINALIZE_KEEP_BRANCH", "FINALIZE_DISCARD_BRANCH", "FINALIZE_NO_VCS"];
11
11
  export type ShipFinalizationMode = (typeof SHIP_FINALIZATION_MODES)[number];
12
12
  export declare const DEFAULT_HARNESSES: HarnessId[];
13
- export declare const REQUIRED_DIRS: readonly [".cclaw", ".cclaw/commands", ".cclaw/skills", ".cclaw/templates", ".cclaw/templates/state-contracts", ".cclaw/artifacts", ".cclaw/state", ".cclaw/runs", ".cclaw/rules", ".cclaw/agents", ".cclaw/hooks", ".cclaw/skills/review-prompts"];
13
+ export declare const REQUIRED_DIRS: readonly [".cclaw", ".cclaw/commands", ".cclaw/skills", ".cclaw/templates", ".cclaw/templates/state-contracts", ".cclaw/artifacts", ".cclaw/archive", ".cclaw/state", ".cclaw/rules", ".cclaw/agents", ".cclaw/hooks", ".cclaw/skills/review-prompts"];
14
14
  export declare const REQUIRED_GITIGNORE_PATTERNS: readonly ["# cclaw generated artifacts", ".cclaw/", ".claude/commands/cc-*.md", ".claude/commands/cc.md", ".cursor/commands/cc-*.md", ".cursor/commands/cc.md", ".opencode/commands/cc-*.md", ".opencode/commands/cc.md", ".agents/skills/cc/SKILL.md", ".agents/skills/cc-*/SKILL.md", ".claude/hooks/hooks.json", ".cursor/hooks.json", ".codex/hooks.json", ".opencode/plugins/cclaw-plugin.mjs", ".cursor/rules/cclaw-workflow.mdc"];
15
15
  /**
16
16
  * Canonical stage -> skill folder mapping.
17
- *
18
- * Intentional divergence from stage ids:
19
- * - stage ids stay short and flow-oriented (`spec`, `tdd`, `ship`)
20
- * - skill folders stay descriptive and user-facing for `.cclaw/skills/*`.
21
- *
22
- * Keep this map as the single source of truth for generated skill paths.
23
17
  */
24
18
  export declare const STAGE_TO_SKILL_FOLDER: Record<FlowStage, string>;
25
19
  export declare const SUBAGENT_SKILL_FOLDERS: readonly ["subagent-dev", "parallel-dispatch"];
package/dist/constants.js CHANGED
@@ -59,8 +59,8 @@ export const REQUIRED_DIRS = [
59
59
  `${RUNTIME_ROOT}/templates`,
60
60
  `${RUNTIME_ROOT}/templates/state-contracts`,
61
61
  `${RUNTIME_ROOT}/artifacts`,
62
+ `${RUNTIME_ROOT}/archive`,
62
63
  `${RUNTIME_ROOT}/state`,
63
- `${RUNTIME_ROOT}/runs`,
64
64
  `${RUNTIME_ROOT}/rules`,
65
65
  `${RUNTIME_ROOT}/agents`,
66
66
  `${RUNTIME_ROOT}/hooks`,
@@ -89,22 +89,16 @@ export const REQUIRED_GITIGNORE_PATTERNS = [
89
89
  ];
90
90
  /**
91
91
  * Canonical stage -> skill folder mapping.
92
- *
93
- * Intentional divergence from stage ids:
94
- * - stage ids stay short and flow-oriented (`spec`, `tdd`, `ship`)
95
- * - skill folders stay descriptive and user-facing for `.cclaw/skills/*`.
96
- *
97
- * Keep this map as the single source of truth for generated skill paths.
98
92
  */
99
93
  export const STAGE_TO_SKILL_FOLDER = {
100
- brainstorm: "brainstorming",
101
- scope: "scope-shaping",
102
- design: "engineering-design-lock",
103
- spec: "specification-authoring",
104
- plan: "planning-and-task-breakdown",
105
- tdd: "test-driven-development",
106
- review: "two-layer-review",
107
- ship: "shipping-and-handoff"
94
+ brainstorm: "brainstorm",
95
+ scope: "scope",
96
+ design: "design",
97
+ spec: "spec",
98
+ plan: "plan",
99
+ tdd: "tdd",
100
+ review: "review",
101
+ ship: "ship"
108
102
  };
109
103
  export const SUBAGENT_SKILL_FOLDERS = [
110
104
  "subagent-dev",
@@ -6,7 +6,7 @@ Use this command when the user wants to stop the active run without claiming com
6
6
  ## Protocol
7
7
 
8
8
  1. Ask for a concise cancellation reason if the user has not already provided one.
9
- 2. Run \`cclaw archive --disposition=cancelled --reason=<reason>\` from the project root. Use \`--disposition=abandoned\` only when the user explicitly frames the run as abandoned rather than cancelled.
9
+ 2. Run \`node .cclaw/hooks/cancel-run.mjs --reason="<reason>"\` from the project root. Use \`--disposition=abandoned\` only when the user explicitly frames the run as abandoned rather than cancelled.
10
10
  3. Report the archive path and reset run id. Make clear that the archived run is not a completed ship.
11
11
 
12
12
  Cancelled and abandoned archives are allowed from any stage, but they require a required reason so future readers know why the run ended.
@@ -20,6 +20,6 @@ description: Cancel or abandon the active cclaw run with a required reason. Use
20
20
 
21
21
  # Cancel cclaw Run
22
22
 
23
- Load and follow \`.cclaw/commands/cancel.md\`. This is a non-completion path: require a reason and archive with cancelled or abandoned disposition.
23
+ Load and follow \`.cclaw/commands/cancel.md\`. This is a non-completion path: require a reason, run the generated cancel helper, and archive with cancelled or abandoned disposition.
24
24
  `;
25
25
  }