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
@@ -10,6 +10,8 @@
10
10
  * `src/knowledge-store.ts::computeCompoundReadiness`.
11
11
  * 2. `computeRalphLoopStatusInline` mirrors
12
12
  * `src/tdd-cycle.ts::computeRalphLoopStatus`.
13
+ * 3. `computeEarlyLoopStatusInline` mirrors
14
+ * `src/early-loop.ts::computeEarlyLoopStatus`.
13
15
  *
14
16
  * Previously those bodies lived inline in `src/content/node-hooks.ts` — a
15
17
  * ~2000-line file — next to unrelated hook-handler code. Any silent drift
@@ -25,11 +27,12 @@
25
27
  * longer owns their source code.
26
28
  *
27
29
  * Parity with the TypeScript canonical implementations is enforced by
28
- * `tests/unit/ralph-loop-parity.test.ts`. Any structural change to the
30
+ * `tests/unit/ralph-loop-parity.test.ts` and
31
+ * `tests/unit/early-loop-parity.test.ts`. Any structural change to the
29
32
  * canonical TS code MUST:
30
33
  *
31
34
  * 1. Update the matching snippet below.
32
- * 2. Re-run `npm test tests/unit/ralph-loop-parity.test.ts`.
35
+ * 2. Re-run parity tests for the touched snippet.
33
36
  *
34
37
  * DO NOT inline tests here — keep the parity check in its dedicated test
35
38
  * file.
@@ -43,7 +46,7 @@
43
46
  * timestamp so the hook-written `compound-readiness.json` is byte-equal
44
47
  * to the CLI-written version for the same input.
45
48
  * - `countArchivedRunsInline` counts immediate subdirectories of
46
- * `<root>/.cclaw/runs/` so both the hook and the CLI see the same
49
+ * `<root>/.cclaw/archive/` so both the hook and the CLI see the same
47
50
  * `archivedRunsCount` for the small-project relaxation.
48
51
  * - `formatCompoundReadinessLineInline` mirrors the one-line summary shape
49
52
  * used by `src/internal/compound-readiness.ts::formatCompoundReadinessLine`
@@ -54,11 +57,11 @@ function normalizeCompoundLastUpdatedAt(date) {
54
57
  return date.toISOString().replace(/\\.\\d{3}Z$/u, "Z");
55
58
  }
56
59
 
57
- // Count archived runs as sub-directories under \`.cclaw/runs/\`. Missing
60
+ // Count archived runs as sub-directories under \`.cclaw/archive/\`. Missing
58
61
  // dir returns 0; unexpected errors return undefined so the caller can
59
62
  // skip the small-project relaxation rather than guess.
60
63
  async function countArchivedRunsInline(root) {
61
- const dir = path.join(root, RUNTIME_ROOT, "runs");
64
+ const dir = path.join(root, RUNTIME_ROOT, "archive");
62
65
  try {
63
66
  const entries = await fs.readdir(dir, { withFileTypes: true });
64
67
  return entries.filter((entry) => entry.isDirectory()).length;
@@ -300,3 +303,213 @@ async function computeRalphLoopStatusInline(stateDir, runId) {
300
303
  };
301
304
  }
302
305
  `;
306
+ /**
307
+ * Inline mirror of `src/early-loop.ts::computeEarlyLoopStatus`.
308
+ *
309
+ * Parity enforced by
310
+ * `tests/unit/early-loop-parity.test.ts::early-loop parity`.
311
+ *
312
+ * Signature contract:
313
+ * async function computeEarlyLoopStatusInline(stateDir, stageId, runId, maxIterations) -> EarlyLoopStatus
314
+ */
315
+ export const EARLY_LOOP_INLINE_SOURCE = `
316
+ function normalizeEarlyLoopSeverityInline(value) {
317
+ if (value === "critical" || value === "important" || value === "suggestion") {
318
+ return value;
319
+ }
320
+ return "important";
321
+ }
322
+
323
+ function normalizeEarlyLoopTextInline(value, fallback) {
324
+ if (typeof value !== "string") return fallback;
325
+ const trimmed = value.trim();
326
+ return trimmed.length > 0 ? trimmed : fallback;
327
+ }
328
+
329
+ function stableConcernFallbackIdInline(locator, summary) {
330
+ const seed = (String(locator) + "::" + String(summary)).trim().toLowerCase();
331
+ let hash = 0;
332
+ for (let index = 0; index < seed.length; index += 1) {
333
+ hash = (Math.imul(31, hash) + seed.charCodeAt(index)) >>> 0;
334
+ }
335
+ return "C-" + hash.toString(16).padStart(8, "0");
336
+ }
337
+
338
+ function normalizeEarlyLoopConcernInline(row) {
339
+ if (!row || typeof row !== "object" || Array.isArray(row)) return null;
340
+ const locator = normalizeEarlyLoopTextInline(row.locator, "unknown-location");
341
+ const summary = normalizeEarlyLoopTextInline(row.summary, "missing-summary");
342
+ const id = typeof row.id === "string" && row.id.trim().length > 0
343
+ ? row.id.trim()
344
+ : stableConcernFallbackIdInline(locator, summary);
345
+ return {
346
+ id,
347
+ severity: normalizeEarlyLoopSeverityInline(row.severity),
348
+ locator,
349
+ summary
350
+ };
351
+ }
352
+
353
+ function normalizeEarlyLoopMaxIterationsInline(value) {
354
+ return Number.isInteger(value) && value >= 1 ? value : 3;
355
+ }
356
+
357
+ function earlyLoopSeverityWeightInline(value) {
358
+ if (value === "critical") return 3;
359
+ if (value === "important") return 2;
360
+ return 1;
361
+ }
362
+
363
+ function sortEarlyLoopConcernsInline(a, b) {
364
+ const severityDiff = earlyLoopSeverityWeightInline(b.severity) - earlyLoopSeverityWeightInline(a.severity);
365
+ if (severityDiff !== 0) return severityDiff;
366
+ if (a.firstSeenIteration !== b.firstSeenIteration) {
367
+ return a.firstSeenIteration - b.firstSeenIteration;
368
+ }
369
+ if (a.lastSeenIteration !== b.lastSeenIteration) {
370
+ return a.lastSeenIteration - b.lastSeenIteration;
371
+ }
372
+ return String(a.id).localeCompare(String(b.id), "en");
373
+ }
374
+
375
+ function formatEarlyLoopStatusLineInline(status) {
376
+ if (!status || typeof status !== "object") return "";
377
+ const convergence = status.convergenceTripped ? "tripped" : "clear";
378
+ return "Early Loop: stage=" + String(status.stage) +
379
+ ", iter=" + String(status.iteration) + "/" + String(status.maxIterations) +
380
+ ", open=" + String(Array.isArray(status.openConcerns) ? status.openConcerns.length : 0) +
381
+ ", convergence=" + convergence;
382
+ }
383
+
384
+ async function computeEarlyLoopStatusInline(stateDir, stageId, runId, maxIterations) {
385
+ const filePath = path.join(stateDir, "early-loop-log.jsonl");
386
+ const raw = await readTextFile(filePath, "");
387
+ const maxIters = normalizeEarlyLoopMaxIterationsInline(maxIterations);
388
+ const concernsMap = new Map();
389
+ let previousSnapshotKey = "";
390
+ let sameConcernStreak = 0;
391
+ let convergenceTripped = false;
392
+ let escalationReason = undefined;
393
+ let currentIteration = 0;
394
+ let lastSeenConcernIds = [];
395
+
396
+ for (const rawLine of raw.split(/\\r?\\n/gu)) {
397
+ const line = rawLine.trim();
398
+ if (line.length === 0) continue;
399
+ let parsed;
400
+ try {
401
+ parsed = JSON.parse(line);
402
+ } catch {
403
+ continue;
404
+ }
405
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) continue;
406
+ const rowRunId = typeof parsed.runId === "string" && parsed.runId.trim().length > 0
407
+ ? parsed.runId.trim()
408
+ : "active";
409
+ const rowStage = typeof parsed.stage === "string" && parsed.stage.trim().length > 0
410
+ ? parsed.stage.trim()
411
+ : "brainstorm";
412
+ if (rowRunId !== runId || rowStage !== stageId) continue;
413
+
414
+ currentIteration += 1;
415
+ const iteration = Number.isInteger(parsed.iteration) && parsed.iteration >= 1
416
+ ? parsed.iteration
417
+ : currentIteration;
418
+ const seenThisIteration = new Set();
419
+ const concerns = Array.isArray(parsed.concerns) ? parsed.concerns : [];
420
+ for (const rawConcern of concerns) {
421
+ const concern = normalizeEarlyLoopConcernInline(rawConcern);
422
+ if (!concern) continue;
423
+ seenThisIteration.add(concern.id);
424
+ const existing = concernsMap.get(concern.id);
425
+ if (!existing) {
426
+ concernsMap.set(concern.id, {
427
+ id: concern.id,
428
+ severity: concern.severity,
429
+ locator: concern.locator,
430
+ summary: concern.summary,
431
+ firstSeenIteration: iteration,
432
+ lastSeenIteration: iteration
433
+ });
434
+ continue;
435
+ }
436
+ existing.lastSeenIteration = iteration;
437
+ existing.locator = concern.locator;
438
+ existing.summary = concern.summary;
439
+ if (earlyLoopSeverityWeightInline(concern.severity) >= earlyLoopSeverityWeightInline(existing.severity)) {
440
+ existing.severity = concern.severity;
441
+ }
442
+ delete existing.resolvedAtIteration;
443
+ }
444
+
445
+ const resolvedConcernIds = Array.isArray(parsed.resolvedConcernIds)
446
+ ? parsed.resolvedConcernIds
447
+ .filter((entry) => typeof entry === "string" && entry.trim().length > 0)
448
+ .map((entry) => entry.trim())
449
+ : [];
450
+ for (const concernId of resolvedConcernIds) {
451
+ const existing = concernsMap.get(concernId);
452
+ if (!existing) continue;
453
+ if (seenThisIteration.has(concernId)) continue;
454
+ if (existing.resolvedAtIteration === undefined) {
455
+ existing.resolvedAtIteration = iteration;
456
+ }
457
+ }
458
+
459
+ for (const concern of concernsMap.values()) {
460
+ if (concern.resolvedAtIteration !== undefined) continue;
461
+ if (seenThisIteration.has(concern.id)) continue;
462
+ concern.resolvedAtIteration = iteration;
463
+ }
464
+
465
+ const openConcernIds = Array.from(concernsMap.values())
466
+ .filter((concern) => concern.resolvedAtIteration === undefined)
467
+ .map((concern) => concern.id)
468
+ .sort((a, b) => String(a).localeCompare(String(b), "en"));
469
+ lastSeenConcernIds = openConcernIds;
470
+ const snapshotKey = openConcernIds.join("|");
471
+ if (snapshotKey.length > 0 && snapshotKey === previousSnapshotKey) {
472
+ sameConcernStreak += 1;
473
+ if (!convergenceTripped && sameConcernStreak >= 2) {
474
+ convergenceTripped = true;
475
+ escalationReason = "same concerns " + String(sameConcernStreak) + " iterations in a row";
476
+ }
477
+ } else {
478
+ sameConcernStreak = snapshotKey.length > 0 ? 1 : 0;
479
+ }
480
+ previousSnapshotKey = snapshotKey;
481
+ }
482
+
483
+ const openConcerns = Array.from(concernsMap.values())
484
+ .filter((concern) => concern.resolvedAtIteration === undefined)
485
+ .sort(sortEarlyLoopConcernsInline);
486
+ const resolvedConcerns = Array.from(concernsMap.values())
487
+ .filter((concern) => concern.resolvedAtIteration !== undefined)
488
+ .sort((a, b) => {
489
+ if (a.resolvedAtIteration !== b.resolvedAtIteration) {
490
+ return a.resolvedAtIteration - b.resolvedAtIteration;
491
+ }
492
+ return sortEarlyLoopConcernsInline(a, b);
493
+ });
494
+
495
+ if (!convergenceTripped && openConcerns.length > 0 && currentIteration >= maxIters) {
496
+ convergenceTripped = true;
497
+ escalationReason = "max iterations " + String(maxIters) +
498
+ " reached with " + String(openConcerns.length) + " open concern(s)";
499
+ }
500
+
501
+ return {
502
+ schemaVersion: 1,
503
+ stage: stageId,
504
+ runId,
505
+ iteration: currentIteration,
506
+ maxIterations: maxIters,
507
+ openConcerns,
508
+ resolvedConcerns,
509
+ lastSeenConcernIds,
510
+ convergenceTripped,
511
+ ...(escalationReason ? { escalationReason } : {}),
512
+ lastUpdatedAt: new Date().toISOString()
513
+ };
514
+ }
515
+ `;
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * - the per-harness JSON generators in `./observe.ts`
7
7
  * (claude/cursor/codex hook documents),
8
- * - the semantic coverage map in `./hook-events.ts` (docs + doctor),
8
+ * - the semantic coverage map in `./hook-events.ts` (docs + sync/runtime checks),
9
9
  * - the `requiredEvents` list embedded in `src/hook-schemas/*.v1.json`
10
10
  * (enforced by a parity test).
11
11
  *
@@ -74,6 +74,6 @@ export declare function groupBindingsByEvent(harness: HookManifestHarness): Even
74
74
  /** Distinct harness-native event names covered by the manifest. */
75
75
  export declare function requiredEventsFor(harness: HookManifestHarness): string[];
76
76
  /**
77
- * Human-readable per-harness semantic coverage used by docs and doctor output.
77
+ * Human-readable per-harness semantic coverage used by docs and sync/runtime diagnostics.
78
78
  */
79
79
  export declare function semanticEventCoverage(harness: HookManifestHarness): Partial<Record<HookSemanticEvent, string>>;
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * - the per-harness JSON generators in `./observe.ts`
7
7
  * (claude/cursor/codex hook documents),
8
- * - the semantic coverage map in `./hook-events.ts` (docs + doctor),
8
+ * - the semantic coverage map in `./hook-events.ts` (docs + sync/runtime checks),
9
9
  * - the `requiredEvents` list embedded in `src/hook-schemas/*.v1.json`
10
10
  * (enforced by a parity test).
11
11
  *
@@ -177,7 +177,7 @@ export function requiredEventsFor(harness) {
177
177
  return ordered;
178
178
  }
179
179
  /**
180
- * Human-readable per-harness semantic coverage used by docs and doctor output.
180
+ * Human-readable per-harness semantic coverage used by docs and sync/runtime diagnostics.
181
181
  */
182
182
  export function semanticEventCoverage(harness) {
183
183
  const out = {};
@@ -1,4 +1,5 @@
1
1
  export declare function startFlowScript(): string;
2
+ export declare function cancelRunScript(): string;
2
3
  export declare function stageCompleteScript(): string;
3
4
  export declare function delegationRecordScript(): string;
4
5
  export declare function runHookCmdScript(): string;
@@ -26,7 +26,7 @@ function resolveCliRuntimeForGeneratedHook() {
26
26
  }
27
27
  return { entrypoint: null, argsPrefix: [] };
28
28
  }
29
- function internalHelperScript(helperName, internalSubcommand, usage) {
29
+ function internalHelperScript(helperName, internalSubcommand, usage, options) {
30
30
  const cliRuntime = resolveCliRuntimeForGeneratedHook();
31
31
  return `#!/usr/bin/env node
32
32
  import fs from "node:fs/promises";
@@ -40,6 +40,8 @@ const CCLAW_CLI_ARGS_PREFIX = ${JSON.stringify(cliRuntime.argsPrefix)};
40
40
  const HELPER_NAME = ${JSON.stringify(helperName)};
41
41
  const INTERNAL_SUBCOMMAND = ${JSON.stringify(internalSubcommand)};
42
42
  const USAGE = ${JSON.stringify(usage)};
43
+ const POSITIONAL_ARG_NAME = ${JSON.stringify(options?.positionalArgName ?? null)};
44
+ const POSITIONAL_ARG_REQUIRED = ${JSON.stringify(options?.positionalArgRequired === true)};
43
45
 
44
46
  async function detectRoot() {
45
47
  const candidates = [
@@ -69,11 +71,22 @@ function printUsage() {
69
71
  }
70
72
 
71
73
  async function main() {
72
- const [, , ...flags] = process.argv;
73
- if (flags.includes("--help") || flags.includes("-h")) {
74
+ const [, , ...argvTokens] = process.argv;
75
+ if (argvTokens.includes("--help") || argvTokens.includes("-h")) {
74
76
  printUsage();
75
77
  return;
76
78
  }
79
+ let positionalArg = "";
80
+ let flags = argvTokens;
81
+ if (POSITIONAL_ARG_NAME !== null) {
82
+ positionalArg = (argvTokens[0] ?? "").trim();
83
+ flags = argvTokens.slice(1);
84
+ if (POSITIONAL_ARG_REQUIRED && positionalArg.length === 0) {
85
+ printUsage();
86
+ process.exitCode = 1;
87
+ return;
88
+ }
89
+ }
77
90
 
78
91
  const root = await detectRoot();
79
92
  const runtimePath = path.join(root, RUNTIME_ROOT);
@@ -112,7 +125,12 @@ async function main() {
112
125
  return;
113
126
  }
114
127
 
115
- const child = spawn(process.execPath, [cliEntrypoint, ...cliArgsPrefix, "internal", INTERNAL_SUBCOMMAND, ...flags], {
128
+ const internalArgs =
129
+ POSITIONAL_ARG_NAME !== null
130
+ ? [INTERNAL_SUBCOMMAND, positionalArg, ...flags]
131
+ : [INTERNAL_SUBCOMMAND, ...flags];
132
+
133
+ const child = spawn(process.execPath, [cliEntrypoint, ...cliArgsPrefix, "internal", ...internalArgs], {
116
134
  cwd: root,
117
135
  env: process.env,
118
136
  stdio: "inherit"
@@ -124,7 +142,7 @@ async function main() {
124
142
  const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
125
143
  if (code === "ENOENT") {
126
144
  process.stderr.write(
127
- "[cclaw] " + HELPER_NAME + ": node executable not found while invoking local runtime. Re-run npx cclaw-cli doctor.\\n"
145
+ "[cclaw] " + HELPER_NAME + ": node executable not found while invoking local runtime. Re-run npx cclaw-cli sync.\\n"
128
146
  );
129
147
  } else {
130
148
  process.stderr.write(
@@ -155,137 +173,14 @@ void main();
155
173
  export function startFlowScript() {
156
174
  return internalHelperScript("start-flow", "start-flow", "Usage: node " + RUNTIME_ROOT + "/hooks/start-flow.mjs --track=<standard|medium|quick> [--class=...] [--prompt=...] [--stack=...] [--reason=...] [--reclassify] [--force-reset]");
157
175
  }
158
- export function stageCompleteScript() {
159
- const cliRuntime = resolveCliRuntimeForGeneratedHook();
160
- return `#!/usr/bin/env node
161
- import fs from "node:fs/promises";
162
- import path from "node:path";
163
- import process from "node:process";
164
- import { spawn } from "node:child_process";
165
-
166
- const RUNTIME_ROOT = ${JSON.stringify(RUNTIME_ROOT)};
167
- const CCLAW_CLI_ENTRYPOINT = ${JSON.stringify(cliRuntime.entrypoint)};
168
- const CCLAW_CLI_ARGS_PREFIX = ${JSON.stringify(cliRuntime.argsPrefix)};
169
-
170
- async function detectRoot() {
171
- const candidates = [
172
- process.env.CCLAW_PROJECT_ROOT,
173
- process.env.CLAUDE_PROJECT_DIR,
174
- process.env.CURSOR_PROJECT_DIR,
175
- process.env.CURSOR_PROJECT_ROOT,
176
- process.env.OPENCODE_PROJECT_DIR,
177
- process.env.OPENCODE_PROJECT_ROOT,
178
- process.cwd()
179
- ].filter((value) => typeof value === "string" && value.length > 0);
180
-
181
- for (const candidate of candidates) {
182
- try {
183
- const runtimePath = path.join(candidate, RUNTIME_ROOT);
184
- const stat = await fs.stat(runtimePath);
185
- if (stat.isDirectory()) return candidate;
186
- } catch {
187
- // continue
188
- }
189
- }
190
- return candidates[0] || process.cwd();
176
+ export function cancelRunScript() {
177
+ return internalHelperScript("cancel-run", "cancel-run", "Usage: node " + RUNTIME_ROOT + "/hooks/cancel-run.mjs --reason=<text> [--disposition=<cancelled|abandoned>] [--name=<slug>]");
191
178
  }
192
-
193
- function printUsage() {
194
- process.stderr.write(
195
- "Usage: node " +
196
- RUNTIME_ROOT +
197
- "/hooks/stage-complete.mjs <stage> [--passed=...] [--evidence-json=...] [--waive-delegation=...] [--waiver-reason=...] [--json]\\n"
198
- );
199
- }
200
-
201
- async function main() {
202
- const [, , stage, ...flags] = process.argv;
203
- if (!stage || stage.trim().length === 0) {
204
- printUsage();
205
- process.exitCode = 1;
206
- return;
207
- }
208
-
209
- const root = await detectRoot();
210
- const runtimePath = path.join(root, RUNTIME_ROOT);
211
- try {
212
- const stat = await fs.stat(runtimePath);
213
- if (!stat.isDirectory()) throw new Error("not-dir");
214
- } catch {
215
- process.stderr.write("[cclaw] stage-complete: runtime root not found at " + runtimePath + "\\n");
216
- process.exitCode = 1;
217
- return;
218
- }
219
-
220
- const cliEntrypoint = process.env.CCLAW_CLI_JS || CCLAW_CLI_ENTRYPOINT;
221
- const cliArgsPrefix = process.env.CCLAW_CLI_JS ? [] : CCLAW_CLI_ARGS_PREFIX;
222
- if (!cliEntrypoint || cliEntrypoint.trim().length === 0) {
223
- process.stderr.write(
224
- "[cclaw] stage-complete: local Node runtime entrypoint is missing. Re-run npx cclaw-cli sync, or set CCLAW_CLI_JS=/absolute/path/to/dist/cli.js for this session.\\n"
225
- );
226
- process.exitCode = 1;
227
- return;
228
- }
229
-
230
- try {
231
- const stat = await fs.stat(cliEntrypoint);
232
- if (!stat.isFile()) throw new Error("not-file");
233
- for (const argPath of cliArgsPrefix) {
234
- if (typeof argPath !== "string" || argPath.startsWith("-")) continue;
235
- const argStat = await fs.stat(argPath);
236
- if (!argStat.isFile()) throw new Error("arg-not-file");
237
- }
238
- } catch {
239
- process.stderr.write(
240
- "[cclaw] stage-complete: local Node runtime entrypoint not found at " + cliEntrypoint + ". Re-run npx cclaw-cli sync, or set CCLAW_CLI_JS=/absolute/path/to/dist/cli.js for this session.\\n"
241
- );
242
- process.exitCode = 1;
243
- return;
244
- }
245
-
246
- const child = spawn(
247
- process.execPath,
248
- [cliEntrypoint, ...cliArgsPrefix, "internal", "advance-stage", stage, ...flags],
249
- {
250
- cwd: root,
251
- env: process.env,
252
- stdio: "inherit"
253
- }
254
- );
255
- let spawnErrored = false;
256
-
257
- child.on("error", (error) => {
258
- spawnErrored = true;
259
- const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
260
- if (code === "ENOENT") {
261
- process.stderr.write(
262
- "[cclaw] stage-complete: node executable not found while invoking local runtime. Re-run npx cclaw-cli doctor.\\n"
263
- );
264
- } else {
265
- process.stderr.write(
266
- "[cclaw] stage-complete: failed to invoke local Node advance-stage runtime (" +
267
- (error instanceof Error ? error.message : String(error)) +
268
- ").\\n"
269
- );
270
- }
271
- process.exitCode = 1;
272
- });
273
-
274
- child.on("close", (code, signal) => {
275
- if (spawnErrored) {
276
- process.exitCode = 1;
277
- return;
278
- }
279
- if (signal) {
280
- process.exitCode = 1;
281
- return;
282
- }
283
- process.exitCode = typeof code === "number" && code >= 0 ? code : 1;
284
- });
285
- }
286
-
287
- void main();
288
- `;
179
+ export function stageCompleteScript() {
180
+ return internalHelperScript("stage-complete", "advance-stage", "Usage: node " + RUNTIME_ROOT + "/hooks/stage-complete.mjs <stage> [--passed=...] [--evidence-json=...] [--waive-delegation=...] [--waiver-reason=...] [--accept-proactive-waiver] [--accept-proactive-waiver-reason=...] [--json]", {
181
+ positionalArgName: "stage",
182
+ positionalArgRequired: true
183
+ });
289
184
  }
290
185
  export function delegationRecordScript() {
291
186
  return `#!/usr/bin/env node
@@ -669,7 +564,7 @@ set "RUNTIME=%HOOK_DIR%run-hook.mjs"
669
564
  where node >nul 2>nul
670
565
  if %ERRORLEVEL% neq 0 (
671
566
  REM Best-effort: missing node should not block harness execution loops.
672
- echo [cclaw] run-hook.cmd: node not found; cclaw hook skipped. Run npx cclaw-cli doctor. >&2
567
+ echo [cclaw] run-hook.cmd: node not found; cclaw hook skipped. Run npx cclaw-cli sync. >&2
673
568
  exit /b 0
674
569
  )
675
570
  node "%RUNTIME%" %*
@@ -681,7 +576,7 @@ if [ "$#" -lt 1 ]; then
681
576
  exit 1
682
577
  fi
683
578
  if ! command -v node >/dev/null 2>&1; then
684
- echo "[cclaw] run-hook.cmd: node not found; cclaw hook skipped. Run npx cclaw-cli doctor." >&2
579
+ echo "[cclaw] run-hook.cmd: node not found; cclaw hook skipped. Run npx cclaw-cli sync." >&2
685
580
  exit 0
686
581
  fi
687
582
  exec node "\${SCRIPT_DIR}/run-hook.mjs" "$@"
@@ -0,0 +1,8 @@
1
+ import { type IdeaFrameId } from "./idea-frames.js";
2
+ export interface IdeaCommandOptions {
3
+ frameIds?: readonly IdeaFrameId[];
4
+ mode?: "repo-grounded" | "elsewhere-software" | "elsewhere-non-software" | "narrow";
5
+ }
6
+ export declare function minimumDistinctIdeaFrames(frameCount: number, mode?: IdeaCommandOptions["mode"]): number;
7
+ export declare function ideaCommandContract(options?: IdeaCommandOptions): string;
8
+ export declare function ideaCommandSkillMarkdown(options?: IdeaCommandOptions): string;