cclaw-cli 6.14.4 → 7.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.
Files changed (86) hide show
  1. package/README.md +0 -2
  2. package/dist/artifact-linter/brainstorm.js +1 -1
  3. package/dist/artifact-linter/design.js +2 -2
  4. package/dist/artifact-linter/findings-dedup.js +1 -1
  5. package/dist/artifact-linter/plan.js +6 -6
  6. package/dist/artifact-linter/review-army.d.ts +1 -1
  7. package/dist/artifact-linter/review-army.js +1 -1
  8. package/dist/artifact-linter/scope.js +6 -6
  9. package/dist/artifact-linter/shared.d.ts +37 -73
  10. package/dist/artifact-linter/shared.js +30 -37
  11. package/dist/artifact-linter/spec.js +1 -1
  12. package/dist/artifact-linter/tdd.d.ts +20 -33
  13. package/dist/artifact-linter/tdd.js +89 -639
  14. package/dist/artifact-linter.js +11 -32
  15. package/dist/cli.js +1 -1
  16. package/dist/config.js +1 -1
  17. package/dist/constants.js +1 -1
  18. package/dist/content/core-agents.d.ts +8 -26
  19. package/dist/content/core-agents.js +48 -94
  20. package/dist/content/examples.d.ts +1 -1
  21. package/dist/content/examples.js +4 -4
  22. package/dist/content/hooks.js +62 -149
  23. package/dist/content/idea.js +2 -2
  24. package/dist/content/iron-laws.js +1 -1
  25. package/dist/content/node-hooks.js +2 -2
  26. package/dist/content/skills-elicitation.js +2 -2
  27. package/dist/content/skills.d.ts +4 -6
  28. package/dist/content/skills.js +14 -53
  29. package/dist/content/stage-schema.d.ts +3 -3
  30. package/dist/content/stage-schema.js +8 -46
  31. package/dist/content/stages/brainstorm.js +5 -5
  32. package/dist/content/stages/plan.js +2 -2
  33. package/dist/content/stages/review.js +1 -1
  34. package/dist/content/stages/schema-types.d.ts +1 -1
  35. package/dist/content/stages/scope.js +1 -1
  36. package/dist/content/stages/spec.js +2 -2
  37. package/dist/content/stages/tdd.js +43 -108
  38. package/dist/content/start-command.js +3 -3
  39. package/dist/content/subagent-context-skills.js +5 -3
  40. package/dist/content/subagents.js +13 -74
  41. package/dist/content/templates.d.ts +6 -6
  42. package/dist/content/templates.js +23 -24
  43. package/dist/content/utility-skills.d.ts +1 -1
  44. package/dist/content/utility-skills.js +1 -1
  45. package/dist/delegation.d.ts +79 -139
  46. package/dist/delegation.js +83 -215
  47. package/dist/early-loop.js +1 -1
  48. package/dist/flow-state.d.ts +24 -129
  49. package/dist/flow-state.js +5 -30
  50. package/dist/gate-evidence.d.ts +2 -7
  51. package/dist/gate-evidence.js +2 -59
  52. package/dist/harness-adapters.d.ts +1 -1
  53. package/dist/harness-adapters.js +11 -10
  54. package/dist/install.js +24 -459
  55. package/dist/internal/advance-stage/advance.d.ts +5 -5
  56. package/dist/internal/advance-stage/advance.js +9 -24
  57. package/dist/internal/advance-stage/parsers.d.ts +1 -1
  58. package/dist/internal/advance-stage/review-loop.d.ts +1 -1
  59. package/dist/internal/advance-stage/review-loop.js +3 -3
  60. package/dist/internal/advance-stage/start-flow.js +1 -3
  61. package/dist/internal/advance-stage.js +4 -23
  62. package/dist/internal/cohesion-contract-stub.d.ts +8 -13
  63. package/dist/internal/cohesion-contract-stub.js +18 -24
  64. package/dist/internal/flow-state-repair.d.ts +1 -1
  65. package/dist/internal/plan-split-waves.d.ts +18 -21
  66. package/dist/internal/plan-split-waves.js +16 -19
  67. package/dist/internal/wave-status.d.ts +3 -6
  68. package/dist/internal/wave-status.js +5 -27
  69. package/dist/policy.js +1 -1
  70. package/dist/run-persistence.js +10 -44
  71. package/dist/runtime/run-hook.mjs +3 -3
  72. package/dist/track-heuristics.js +1 -1
  73. package/dist/types.d.ts +2 -2
  74. package/package.json +1 -1
  75. package/dist/integration-fanin.d.ts +0 -44
  76. package/dist/integration-fanin.js +0 -180
  77. package/dist/internal/set-checkpoint-mode.d.ts +0 -16
  78. package/dist/internal/set-checkpoint-mode.js +0 -72
  79. package/dist/internal/set-integration-overseer-mode.d.ts +0 -14
  80. package/dist/internal/set-integration-overseer-mode.js +0 -69
  81. package/dist/internal/set-worktree-mode.d.ts +0 -10
  82. package/dist/internal/set-worktree-mode.js +0 -28
  83. package/dist/worktree-manager.d.ts +0 -50
  84. package/dist/worktree-manager.js +0 -136
  85. package/dist/worktree-types.d.ts +0 -36
  86. package/dist/worktree-types.js +0 -6
package/dist/policy.js CHANGED
@@ -71,7 +71,7 @@ export async function policyChecks(projectRoot, options = {}) {
71
71
  { file: runtimeFile("skills/flow-view/SKILL.md"), needle: "## Diff Subcommand", name: "utility_skill:view:diff_section" },
72
72
  { file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:sdd:hard_gate" },
73
73
  { file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "## Status Contract", name: "utility_skill:sdd:status_contract" },
74
- { file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "slice-implementer", name: "utility_skill:sdd:implementer_template" },
74
+ { file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "slice-builder", name: "utility_skill:sdd:implementer_template" },
75
75
  { file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "## Model & Harness Routing Notes", name: "utility_skill:sdd:routing_notes" },
76
76
  { file: runtimeFile("skills/parallel-dispatch/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:parallel:hard_gate" },
77
77
  { file: runtimeFile("skills/parallel-dispatch/SKILL.md"), needle: "Review Army", name: "utility_skill:parallel:review_army" },
@@ -232,10 +232,10 @@ function coerceRepoSignals(value) {
232
232
  };
233
233
  }
234
234
  /**
235
- * Wave 24 follow-up (v6.1.1) — preserve `flow-state.json#taskClass`
235
+ * preserve `flow-state.json#taskClass`
236
236
  * across read/write round-trips. Before this audit fix the persistence
237
- * layer silently dropped the field, which made the Wave 24 bugfix-skip
238
- * (`mandatoryAgentsFor` short-circuit) and the Wave 25 artifact-validation
237
+ * layer silently dropped the field, which made the bugfix-skip
238
+ * (`mandatoryAgentsFor` short-circuit) and the artifact-validation
239
239
  * demotion both dead in practice: the only entry point that classified
240
240
  * a run was the unit-test harness passing `options.taskClass` directly
241
241
  * to `checkMandatoryDelegations`. The accepted union mirrors
@@ -471,13 +471,10 @@ function coerceFlowState(parsed) {
471
471
  const taskClass = coerceTaskClass(parsed.taskClass);
472
472
  const repoSignals = coerceRepoSignals(parsed.repoSignals);
473
473
  const completedStageMeta = sanitizeCompletedStageMeta(parsed.completedStageMeta);
474
- const tddCutoverSliceId = coerceTddCutoverSliceId(parsed.tddCutoverSliceId);
475
- const tddWorktreeCutoverSliceId = coerceTddCutoverSliceId(parsed.tddWorktreeCutoverSliceId);
476
- const worktreeExecutionMode = coerceWorktreeExecutionMode(parsed.worktreeExecutionMode);
477
- const tddCheckpointMode = coerceTddCheckpointMode(parsed.tddCheckpointMode);
478
- const integrationOverseerMode = coerceIntegrationOverseerMode(parsed.integrationOverseerMode);
479
- const legacyContinuation = typeof parsed.legacyContinuation === "boolean" ? parsed.legacyContinuation : undefined;
480
474
  const tddGreenMinElapsedMs = coerceTddGreenMinElapsedMs(parsed.tddGreenMinElapsedMs);
475
+ const packageVersion = typeof parsed.packageVersion === "string" && parsed.packageVersion.trim().length > 0
476
+ ? parsed.packageVersion.trim()
477
+ : undefined;
481
478
  const state = {
482
479
  schemaVersion: FLOW_STATE_SCHEMA_VERSION,
483
480
  activeRunId,
@@ -490,13 +487,8 @@ function coerceFlowState(parsed) {
490
487
  ...(taskClass !== undefined ? { taskClass } : {}),
491
488
  ...(repoSignals ? { repoSignals } : {}),
492
489
  ...(completedStageMeta ? { completedStageMeta } : {}),
493
- ...(tddCutoverSliceId ? { tddCutoverSliceId } : {}),
494
- ...(tddWorktreeCutoverSliceId ? { tddWorktreeCutoverSliceId } : {}),
495
- ...(worktreeExecutionMode !== undefined ? { worktreeExecutionMode } : {}),
496
- ...(tddCheckpointMode !== undefined ? { tddCheckpointMode } : {}),
497
- ...(integrationOverseerMode !== undefined ? { integrationOverseerMode } : {}),
498
- ...(legacyContinuation !== undefined ? { legacyContinuation } : {}),
499
490
  ...(tddGreenMinElapsedMs !== undefined ? { tddGreenMinElapsedMs } : {}),
491
+ ...(packageVersion ? { packageVersion } : {}),
500
492
  skippedStages: sanitizeSkippedStages(parsed.skippedStages, track),
501
493
  staleStages: sanitizeStaleStages(parsed.staleStages),
502
494
  rewinds: sanitizeRewinds(parsed.rewinds),
@@ -507,33 +499,7 @@ function coerceFlowState(parsed) {
507
499
  return { state };
508
500
  }
509
501
  /**
510
- * v6.12.0 best-effort coercion for `tddCutoverSliceId`. Returns the value
511
- * only when it matches the canonical slice id shape `S-<digits>`; otherwise
512
- * returns null so the field is omitted from the rehydrated state.
513
- */
514
- function coerceTddCutoverSliceId(value) {
515
- if (typeof value !== "string")
516
- return null;
517
- const trimmed = value.trim();
518
- return /^S-\d+$/u.test(trimmed) ? trimmed : null;
519
- }
520
- function coerceWorktreeExecutionMode(value) {
521
- if (value === "single-tree" || value === "worktree-first")
522
- return value;
523
- return undefined;
524
- }
525
- function coerceTddCheckpointMode(value) {
526
- if (value === "per-slice" || value === "global-red")
527
- return value;
528
- return undefined;
529
- }
530
- function coerceIntegrationOverseerMode(value) {
531
- if (value === "conditional" || value === "always")
532
- return value;
533
- return undefined;
534
- }
535
- /**
536
- * v6.14.2 — coerce `tddGreenMinElapsedMs` from disk. Mirrors the
502
+ * coerce `tddGreenMinElapsedMs` from disk. Mirrors the
537
503
  * defensive read in `effectiveTddGreenMinElapsedMs`: numbers ≥ 0 round
538
504
  * down to integers; everything else (NaN, strings, negatives) returns
539
505
  * undefined so the field is omitted from the rehydrated state and the
@@ -757,10 +723,10 @@ export async function writeFlowStateGuarded(projectRoot, state, options = {}) {
757
723
  await writeFlowState(projectRoot, state, options);
758
724
  }
759
725
  /**
760
- * v6.9.0 — backfill missing `completedStageMeta` rows for any stage that
726
+ * backfill missing `completedStageMeta` rows for any stage that
761
727
  * already lives in `completedStages` but has no audit timestamp. Uses the
762
728
  * stage's artifact mtime when available, otherwise the current time. This
763
- * runs as part of `flow-state-repair` so legacy v6.8 flow-state.json files
729
+ * runs as part of `flow-state-repair` so older flow-state.json files
764
730
  * get their meta carried forward without a destructive rewrite.
765
731
  */
766
732
  async function backfillCompletedStageMeta(projectRoot, state) {
@@ -56,7 +56,7 @@ var REQUIRED_GITIGNORE_PATTERNS = [
56
56
  ".opencode/commands/cc-*.md",
57
57
  ".opencode/commands/cc.md",
58
58
  // Codex uses skill-kind shims under `.agents/skills/cc*/` since
59
- // v0.40.0 (renamed from the `cclaw-cc*` layout in v0.39.0/v0.39.1).
59
+ // Codex shim layout (renamed from the older `cclaw-cc*` layout).
60
60
  // `cclaw sync` and `cclaw uninstall` both auto-remove the legacy
61
61
  // `cclaw-cc*` directories.
62
62
  ".agents/skills/cc/SKILL.md",
@@ -676,7 +676,7 @@ async function handleSessionStart(runtime) {
676
676
  );
677
677
  const knowledge = await buildKnowledgeDigest(runtime.root, state.currentStage, knowledgeRaw);
678
678
 
679
- // Wave 21 honest-core: session-start no longer runs background helper
679
+ // honest-core: session-start no longer runs background helper
680
680
  // pipelines or digest caches. It rehydrates flow + knowledge only.
681
681
  const ralphLoopLine = "";
682
682
  const earlyLoopLine = "";
@@ -746,7 +746,7 @@ async function handleSessionStart(runtime) {
746
746
  if (metaContent.length > 0) {
747
747
  parts.push(metaContent);
748
748
  }
749
- // v6.9.0: load iron-laws content into the session-start digest so the
749
+ // load iron-laws content into the session-start digest so the
750
750
  // non-negotiable workflow constraints are visible from the first turn,
751
751
  // not lazily on tool dispatch.
752
752
  if (ironLawsContent.length > 0) {
@@ -51,7 +51,7 @@ const DEFAULT_RULES = {
51
51
  }
52
52
  };
53
53
  // Fixed evaluation order: narrow-to-broad. Overriding this was never wired
54
- // into runtime, so cclaw stopped offering the knob in v0.38.0.
54
+ // into runtime, so cclaw no longer offers that knob.
55
55
  const EVALUATION_ORDER = ["standard", "medium", "quick"];
56
56
  const DEFAULT_FALLBACK = "standard";
57
57
  const ADAPTIVE_ELICITATION_STAGES = new Set(["brainstorm", "scope", "design"]);
package/dist/types.d.ts CHANGED
@@ -34,7 +34,7 @@ export type LanguageRulePack = (typeof LANGUAGE_RULE_PACKS)[number];
34
34
  * - `triggers`: additional substrings that push a prompt toward this track.
35
35
  * - `veto`: substrings that forbid this track even if a trigger matches.
36
36
  *
37
- * Removed in v0.38.0:
37
+ * Removed:
38
38
  * - `patterns` (regex): no runtime ever consumed them; kept authors honest
39
39
  * about what cclaw actually enforces.
40
40
  */
@@ -51,7 +51,7 @@ export interface TrackHeuristicRule {
51
51
  * result — which is why we only ship `triggers`, `veto`, and `fallback`, not
52
52
  * regex patterns or priority overrides.
53
53
  *
54
- * Removed in v0.38.0:
54
+ * Removed:
55
55
  * - `priority`: track evaluation order is always `standard -> medium -> quick`
56
56
  * (narrow-to-broad matching). Overriding it was never wired.
57
57
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "6.14.4",
3
+ "version": "7.0.0",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,44 +0,0 @@
1
- import { type FlowState } from "./flow-state.js";
2
- import type { WorktreeLaneId } from "./worktree-types.js";
3
- export type FanInEventKind = "applied" | "conflict" | "resolved" | "abandoned";
4
- export interface FanInLaneOptions {
5
- projectRoot: string;
6
- /** Lane directory under `.cclaw/worktrees/<laneId>`. */
7
- laneId: WorktreeLaneId;
8
- /** Integration branch to receive the patch (must already exist locally). */
9
- integrationBranch: string;
10
- /**
11
- * Baseline ref for `git diff` in the lane (fork point vs integration).
12
- * When omitted, computed as `git merge-base <integration> HEAD` in the lane.
13
- */
14
- baseRef?: string;
15
- }
16
- export interface FanInLaneResult {
17
- ok: boolean;
18
- event: FanInEventKind;
19
- details: string;
20
- }
21
- /**
22
- * Build a unified diff from `baseRef..HEAD` in the lane worktree and apply it
23
- * to the integration branch in the main repo using three-way merge.
24
- * On conflict, the integration branch working tree is reset and, when possible,
25
- * git HEAD is restored to the branch that was checked out before fan-in.
26
- */
27
- export declare function fanInLane(options: FanInLaneOptions): Promise<FanInLaneResult>;
28
- export interface ResolverDispatchHint {
29
- sliceId: string;
30
- command: string;
31
- }
32
- /**
33
- * Returns the canonical CLI hint for resolving fan-in conflicts for a slice.
34
- */
35
- export declare function buildResolveConflictDispatchHint(sliceId: string): ResolverDispatchHint;
36
- /**
37
- * Merge every lane that recorded a completed GREEN `ownerLaneId` for the
38
- * active run, then emit `cclaw_fanin_*` audit rows. Does nothing in
39
- * `single-tree` mode or when git is unavailable.
40
- */
41
- export declare function runTddDeterministicFanInBeforeAdvance(projectRoot: string, flowState: FlowState): Promise<{
42
- ok: boolean;
43
- issues: string[];
44
- }>;
@@ -1,180 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { execFile } from "node:child_process";
4
- import { promisify } from "node:util";
5
- import { exists } from "./fs-utils.js";
6
- import { readDelegationEvents, recordCclawFanInAudit } from "./delegation.js";
7
- import { effectiveWorktreeExecutionMode } from "./flow-state.js";
8
- const execFileAsync = promisify(execFile);
9
- const WORKTREES_SEG = ".cclaw/worktrees";
10
- /**
11
- * Build a unified diff from `baseRef..HEAD` in the lane worktree and apply it
12
- * to the integration branch in the main repo using three-way merge.
13
- * On conflict, the integration branch working tree is reset and, when possible,
14
- * git HEAD is restored to the branch that was checked out before fan-in.
15
- */
16
- export async function fanInLane(options) {
17
- const { projectRoot, laneId, integrationBranch } = options;
18
- const workdir = path.join(projectRoot, WORKTREES_SEG, laneId);
19
- if (!(await exists(workdir))) {
20
- return { ok: false, event: "abandoned", details: `missing lane workdir ${workdir}` };
21
- }
22
- let integrationRef;
23
- try {
24
- integrationRef = (await execFileAsync("git", ["rev-parse", "--verify", integrationBranch], {
25
- cwd: projectRoot
26
- })).stdout.trim();
27
- }
28
- catch {
29
- return {
30
- ok: false,
31
- event: "abandoned",
32
- details: `integration branch/ref not found: ${integrationBranch}`
33
- };
34
- }
35
- let baseRef = options.baseRef?.trim() ?? "";
36
- if (baseRef.length === 0) {
37
- try {
38
- baseRef = (await execFileAsync("git", ["merge-base", integrationRef, "HEAD"], { cwd: workdir })).stdout.trim();
39
- }
40
- catch (err) {
41
- return {
42
- ok: false,
43
- event: "abandoned",
44
- details: `cannot merge-base lane ${laneId} with ${integrationBranch}: ${err instanceof Error ? err.message : String(err)}`
45
- };
46
- }
47
- }
48
- const patchFile = path.join(projectRoot, WORKTREES_SEG, `.fanin-${laneId}.patch`);
49
- let restoreBranch = null;
50
- try {
51
- let curBranch = "";
52
- try {
53
- curBranch = (await execFileAsync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: projectRoot })).stdout.trim();
54
- }
55
- catch {
56
- curBranch = "";
57
- }
58
- if (curBranch.length > 0 && curBranch !== integrationBranch && curBranch !== "HEAD") {
59
- restoreBranch = curBranch;
60
- }
61
- const { stdout: diffOut } = await execFileAsync("git", ["diff", `${baseRef}..HEAD`], { cwd: workdir, maxBuffer: 64 * 1024 * 1024 });
62
- if (diffOut.trim().length === 0) {
63
- return { ok: true, event: "applied", details: "empty diff; nothing to merge" };
64
- }
65
- await fs.writeFile(patchFile, diffOut, "utf8");
66
- await execFileAsync("git", ["checkout", integrationBranch], { cwd: projectRoot });
67
- try {
68
- await execFileAsync("git", ["apply", "--3way", patchFile], { cwd: projectRoot });
69
- return { ok: true, event: "applied", details: `applied lane ${laneId} onto ${integrationBranch}` };
70
- }
71
- catch (err) {
72
- await execFileAsync("git", ["checkout", "--", "."], { cwd: projectRoot }).catch(() => undefined);
73
- if (restoreBranch) {
74
- await execFileAsync("git", ["checkout", restoreBranch], { cwd: projectRoot }).catch(() => undefined);
75
- }
76
- const msg = err instanceof Error ? err.message : String(err);
77
- return {
78
- ok: false,
79
- event: "conflict",
80
- details: `git apply --3way reported conflicts for lane ${laneId}: ${msg}`
81
- };
82
- }
83
- }
84
- finally {
85
- await fs.rm(patchFile, { force: true });
86
- }
87
- }
88
- /**
89
- * Returns the canonical CLI hint for resolving fan-in conflicts for a slice.
90
- */
91
- export function buildResolveConflictDispatchHint(sliceId) {
92
- return {
93
- sliceId,
94
- command: `slice-implementer --phase resolve-conflict --slice ${sliceId}`
95
- };
96
- }
97
- /**
98
- * Merge every lane that recorded a completed GREEN `ownerLaneId` for the
99
- * active run, then emit `cclaw_fanin_*` audit rows. Does nothing in
100
- * `single-tree` mode or when git is unavailable.
101
- */
102
- export async function runTddDeterministicFanInBeforeAdvance(projectRoot, flowState) {
103
- if (effectiveWorktreeExecutionMode(flowState) !== "worktree-first") {
104
- return { ok: true, issues: [] };
105
- }
106
- let integrationBranch;
107
- try {
108
- integrationBranch = (await execFileAsync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: projectRoot })).stdout.trim();
109
- }
110
- catch {
111
- return {
112
- ok: false,
113
- issues: ["worktree fan-in: cannot read current git branch (not a repository or detached HEAD unsupported here)."]
114
- };
115
- }
116
- const { events } = await readDelegationEvents(projectRoot);
117
- const runId = flowState.activeRunId;
118
- const laneToSlices = new Map();
119
- for (const e of events) {
120
- if (e.runId !== runId || e.stage !== "tdd")
121
- continue;
122
- if (e.agent !== "slice-implementer")
123
- continue;
124
- if (e.status !== "completed" || e.phase !== "green")
125
- continue;
126
- const lane = e.ownerLaneId?.trim();
127
- const sid = e.sliceId?.trim();
128
- if (!lane || !sid)
129
- continue;
130
- if (!laneToSlices.has(lane))
131
- laneToSlices.set(lane, new Set());
132
- laneToSlices.get(lane).add(sid);
133
- }
134
- if (laneToSlices.size === 0) {
135
- return { ok: true, issues: [] };
136
- }
137
- const issues = [];
138
- for (const [laneId, sliceSet] of laneToSlices) {
139
- const result = await fanInLane({
140
- projectRoot,
141
- laneId: laneId,
142
- integrationBranch,
143
- baseRef: undefined
144
- });
145
- const sliceIds = [...sliceSet].sort();
146
- if (!result.ok && result.event === "conflict") {
147
- await recordCclawFanInAudit(projectRoot, {
148
- kind: "cclaw_fanin_conflict",
149
- runId,
150
- laneId,
151
- sliceIds,
152
- integrationBranch,
153
- details: result.details
154
- });
155
- issues.push(`${result.details} — ${buildResolveConflictDispatchHint(sliceIds[0] ?? "S-1").command}`);
156
- continue;
157
- }
158
- if (!result.ok) {
159
- await recordCclawFanInAudit(projectRoot, {
160
- kind: "cclaw_fanin_abandoned",
161
- runId,
162
- laneId,
163
- sliceIds,
164
- integrationBranch,
165
- details: result.details
166
- });
167
- issues.push(result.details);
168
- continue;
169
- }
170
- await recordCclawFanInAudit(projectRoot, {
171
- kind: "cclaw_fanin_applied",
172
- runId,
173
- laneId,
174
- sliceIds,
175
- integrationBranch,
176
- details: result.details
177
- });
178
- }
179
- return { ok: issues.length === 0, issues };
180
- }
@@ -1,16 +0,0 @@
1
- import type { Writable } from "node:stream";
2
- export interface SetCheckpointModeArgs {
3
- mode: "per-slice" | "global-red";
4
- reason: string | null;
5
- }
6
- export declare function parseSetCheckpointModeArgs(tokens: string[]): SetCheckpointModeArgs | null;
7
- /**
8
- * v6.14.2 — set `flow-state.json::tddCheckpointMode` without advancing
9
- * the stage DAG. Mirrors `set-worktree-mode`. The `--reason` flag is
10
- * optional but recommended for the audit trail; it is currently passed
11
- * through to the writer subsystem string so operators can grep the
12
- * `.flow-state.guard.json` sidecar.
13
- */
14
- export declare function runSetCheckpointMode(projectRoot: string, tokens: string[], io: {
15
- stderr: Writable;
16
- }): Promise<number>;
@@ -1,72 +0,0 @@
1
- import { readFlowState, writeFlowState } from "../runs.js";
2
- export function parseSetCheckpointModeArgs(tokens) {
3
- let mode = null;
4
- let reason = null;
5
- let positional = null;
6
- for (const token of tokens) {
7
- if (token.startsWith("--mode=")) {
8
- const raw = token.slice("--mode=".length).trim();
9
- if (raw === "per-slice" || raw === "global-red") {
10
- mode = raw;
11
- }
12
- else {
13
- return null;
14
- }
15
- continue;
16
- }
17
- if (token.startsWith("--reason=")) {
18
- const raw = token.slice("--reason=".length).trim();
19
- if (raw.length > 0)
20
- reason = raw;
21
- continue;
22
- }
23
- if (token.startsWith("--")) {
24
- // unknown flag — let the caller surface usage.
25
- return null;
26
- }
27
- if (positional === null) {
28
- positional = token.trim();
29
- continue;
30
- }
31
- return null;
32
- }
33
- if (mode === null && positional !== null) {
34
- if (positional === "per-slice" || positional === "global-red") {
35
- mode = positional;
36
- }
37
- else {
38
- return null;
39
- }
40
- }
41
- if (mode === null)
42
- return null;
43
- return { mode, reason };
44
- }
45
- /**
46
- * v6.14.2 — set `flow-state.json::tddCheckpointMode` without advancing
47
- * the stage DAG. Mirrors `set-worktree-mode`. The `--reason` flag is
48
- * optional but recommended for the audit trail; it is currently passed
49
- * through to the writer subsystem string so operators can grep the
50
- * `.flow-state.guard.json` sidecar.
51
- */
52
- export async function runSetCheckpointMode(projectRoot, tokens, io) {
53
- const parsed = parseSetCheckpointModeArgs(tokens);
54
- if (!parsed) {
55
- io.stderr.write("cclaw internal set-checkpoint-mode: usage: <per-slice|global-red> [--reason=\"<short>\"] " +
56
- "(or --mode=<per-slice|global-red>)\n");
57
- return 1;
58
- }
59
- const state = await readFlowState(projectRoot);
60
- const writerSubsystem = parsed.reason
61
- ? `set-checkpoint-mode:${slugifyReason(parsed.reason)}`
62
- : "set-checkpoint-mode";
63
- await writeFlowState(projectRoot, { ...state, tddCheckpointMode: parsed.mode }, { writerSubsystem });
64
- return 0;
65
- }
66
- function slugifyReason(reason) {
67
- return (reason
68
- .toLowerCase()
69
- .replace(/[^a-z0-9_-]+/gu, "-")
70
- .replace(/^-+|-+$/gu, "")
71
- .slice(0, 60) || "unspecified");
72
- }
@@ -1,14 +0,0 @@
1
- import type { Writable } from "node:stream";
2
- export interface SetIntegrationOverseerModeArgs {
3
- mode: "conditional" | "always";
4
- reason: string | null;
5
- }
6
- export declare function parseSetIntegrationOverseerModeArgs(tokens: string[]): SetIntegrationOverseerModeArgs | null;
7
- /**
8
- * v6.14.2 — set `flow-state.json::integrationOverseerMode` without
9
- * advancing the stage DAG. Mirrors `set-worktree-mode` and
10
- * `set-checkpoint-mode`.
11
- */
12
- export declare function runSetIntegrationOverseerMode(projectRoot: string, tokens: string[], io: {
13
- stderr: Writable;
14
- }): Promise<number>;
@@ -1,69 +0,0 @@
1
- import { readFlowState, writeFlowState } from "../runs.js";
2
- export function parseSetIntegrationOverseerModeArgs(tokens) {
3
- let mode = null;
4
- let reason = null;
5
- let positional = null;
6
- for (const token of tokens) {
7
- if (token.startsWith("--mode=")) {
8
- const raw = token.slice("--mode=".length).trim();
9
- if (raw === "conditional" || raw === "always") {
10
- mode = raw;
11
- }
12
- else {
13
- return null;
14
- }
15
- continue;
16
- }
17
- if (token.startsWith("--reason=")) {
18
- const raw = token.slice("--reason=".length).trim();
19
- if (raw.length > 0)
20
- reason = raw;
21
- continue;
22
- }
23
- if (token.startsWith("--")) {
24
- return null;
25
- }
26
- if (positional === null) {
27
- positional = token.trim();
28
- continue;
29
- }
30
- return null;
31
- }
32
- if (mode === null && positional !== null) {
33
- if (positional === "conditional" || positional === "always") {
34
- mode = positional;
35
- }
36
- else {
37
- return null;
38
- }
39
- }
40
- if (mode === null)
41
- return null;
42
- return { mode, reason };
43
- }
44
- /**
45
- * v6.14.2 — set `flow-state.json::integrationOverseerMode` without
46
- * advancing the stage DAG. Mirrors `set-worktree-mode` and
47
- * `set-checkpoint-mode`.
48
- */
49
- export async function runSetIntegrationOverseerMode(projectRoot, tokens, io) {
50
- const parsed = parseSetIntegrationOverseerModeArgs(tokens);
51
- if (!parsed) {
52
- io.stderr.write("cclaw internal set-integration-overseer-mode: usage: <conditional|always> [--reason=\"<short>\"] " +
53
- "(or --mode=<conditional|always>)\n");
54
- return 1;
55
- }
56
- const state = await readFlowState(projectRoot);
57
- const writerSubsystem = parsed.reason
58
- ? `set-integration-overseer-mode:${slugifyReason(parsed.reason)}`
59
- : "set-integration-overseer-mode";
60
- await writeFlowState(projectRoot, { ...state, integrationOverseerMode: parsed.mode }, { writerSubsystem });
61
- return 0;
62
- }
63
- function slugifyReason(reason) {
64
- return (reason
65
- .toLowerCase()
66
- .replace(/[^a-z0-9_-]+/gu, "-")
67
- .replace(/^-+|-+$/gu, "")
68
- .slice(0, 60) || "unspecified");
69
- }
@@ -1,10 +0,0 @@
1
- import type { Writable } from "node:stream";
2
- export declare function parseSetWorktreeModeArgs(tokens: string[]): {
3
- mode: "single-tree" | "worktree-first";
4
- } | null;
5
- /**
6
- * Set `flow-state.json::worktreeExecutionMode` without advancing the stage DAG.
7
- */
8
- export declare function runSetWorktreeMode(projectRoot: string, tokens: string[], io: {
9
- stderr: Writable;
10
- }): Promise<number>;
@@ -1,28 +0,0 @@
1
- import { readFlowState, writeFlowState } from "../runs.js";
2
- export function parseSetWorktreeModeArgs(tokens) {
3
- let mode = null;
4
- for (const token of tokens) {
5
- if (token.startsWith("--mode=")) {
6
- const raw = token.slice("--mode=".length).trim();
7
- if (raw === "single-tree" || raw === "worktree-first") {
8
- mode = raw;
9
- }
10
- }
11
- }
12
- if (!mode)
13
- return null;
14
- return { mode };
15
- }
16
- /**
17
- * Set `flow-state.json::worktreeExecutionMode` without advancing the stage DAG.
18
- */
19
- export async function runSetWorktreeMode(projectRoot, tokens, io) {
20
- const parsed = parseSetWorktreeModeArgs(tokens);
21
- if (!parsed) {
22
- io.stderr.write("cclaw internal set-worktree-mode: usage: --mode=single-tree|worktree-first\n");
23
- return 1;
24
- }
25
- const state = await readFlowState(projectRoot);
26
- await writeFlowState(projectRoot, { ...state, worktreeExecutionMode: parsed.mode }, { writerSubsystem: "set-worktree-mode" });
27
- return 0;
28
- }
@@ -1,50 +0,0 @@
1
- import type { GitBaseRef, WorktreeLaneId } from "./worktree-types.js";
2
- export interface CreateLaneOptions {
3
- /** Repository root that owns `.cclaw/`. */
4
- projectRoot: string;
5
- /** TDD slice id (e.g. `S-7`). */
6
- sliceId: string;
7
- /** Git ref to create the worktree from (e.g. `HEAD`, branch name). */
8
- baseRef: GitBaseRef;
9
- }
10
- export interface CreateLaneResult {
11
- laneId: WorktreeLaneId;
12
- workdir: string;
13
- branchName: string;
14
- }
15
- /**
16
- * Create a dedicated git worktree for a slice under `.cclaw/worktrees/`.
17
- * Uses branch namespace `cclaw/lane/<sliceId>-<suffix>`. Does not commit.
18
- */
19
- export declare function createLane(options: CreateLaneOptions): Promise<CreateLaneResult>;
20
- /**
21
- * Assert the lane worktree exists, has a clean working tree, and matches
22
- * the expected baseline ref (merge-base check with `baseRef`).
23
- */
24
- export declare function verifyLaneClean(projectRoot: string, laneId: WorktreeLaneId, baseRef: GitBaseRef): Promise<{
25
- ok: true;
26
- } | {
27
- ok: false;
28
- reason: string;
29
- }>;
30
- /**
31
- * Prepare the lane for interactive work (no-op placeholder for harness parity).
32
- */
33
- export declare function attachLane(_laneId: WorktreeLaneId): Promise<void>;
34
- /**
35
- * Release local harness attachment (no-op).
36
- */
37
- export declare function detachLane(_laneId: WorktreeLaneId): Promise<void>;
38
- /**
39
- * Remove the worktree directory and prune git metadata.
40
- */
41
- export declare function cleanupLane(projectRoot: string, laneId: WorktreeLaneId, options?: {
42
- force?: boolean;
43
- }): Promise<void>;
44
- export interface PruneStaleLanesOptions {
45
- olderThanHours: number;
46
- }
47
- /**
48
- * Remove lane worktrees older than the threshold based on directory mtime.
49
- */
50
- export declare function pruneStaleLanes(projectRoot: string, options: PruneStaleLanesOptions): Promise<string[]>;