cclaw-cli 7.7.1 → 8.1.1

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 (284) hide show
  1. package/README.md +211 -134
  2. package/dist/artifact-frontmatter.d.ts +51 -0
  3. package/dist/artifact-frontmatter.js +131 -0
  4. package/dist/artifact-paths.d.ts +7 -27
  5. package/dist/artifact-paths.js +20 -249
  6. package/dist/cancel.d.ts +16 -0
  7. package/dist/cancel.js +66 -0
  8. package/dist/cli.d.ts +2 -27
  9. package/dist/cli.js +107 -511
  10. package/dist/compound.d.ts +26 -0
  11. package/dist/compound.js +96 -0
  12. package/dist/config.d.ts +14 -51
  13. package/dist/config.js +23 -359
  14. package/dist/constants.d.ts +11 -18
  15. package/dist/constants.js +19 -106
  16. package/dist/content/antipatterns.d.ts +1 -0
  17. package/dist/content/antipatterns.js +109 -0
  18. package/dist/content/artifact-templates.d.ts +10 -0
  19. package/dist/content/artifact-templates.js +550 -0
  20. package/dist/content/cancel-command.d.ts +2 -2
  21. package/dist/content/cancel-command.js +25 -17
  22. package/dist/content/core-agents.d.ts +9 -233
  23. package/dist/content/core-agents.js +39 -768
  24. package/dist/content/decision-protocol.d.ts +1 -12
  25. package/dist/content/decision-protocol.js +27 -20
  26. package/dist/content/examples.d.ts +8 -42
  27. package/dist/content/examples.js +293 -425
  28. package/dist/content/idea-command.d.ts +2 -0
  29. package/dist/content/idea-command.js +38 -0
  30. package/dist/content/iron-laws.d.ts +4 -138
  31. package/dist/content/iron-laws.js +18 -197
  32. package/dist/content/meta-skill.d.ts +1 -3
  33. package/dist/content/meta-skill.js +57 -134
  34. package/dist/content/node-hooks.d.ts +12 -8
  35. package/dist/content/node-hooks.js +188 -838
  36. package/dist/content/recovery.d.ts +8 -0
  37. package/dist/content/recovery.js +179 -0
  38. package/dist/content/reference-patterns.d.ts +4 -13
  39. package/dist/content/reference-patterns.js +260 -389
  40. package/dist/content/research-playbooks.d.ts +8 -8
  41. package/dist/content/research-playbooks.js +108 -121
  42. package/dist/content/review-loop.d.ts +6 -192
  43. package/dist/content/review-loop.js +29 -731
  44. package/dist/content/skills.d.ts +8 -38
  45. package/dist/content/skills.js +681 -732
  46. package/dist/content/specialist-prompts/architect.d.ts +1 -0
  47. package/dist/content/specialist-prompts/architect.js +225 -0
  48. package/dist/content/specialist-prompts/brainstormer.d.ts +1 -0
  49. package/dist/content/specialist-prompts/brainstormer.js +168 -0
  50. package/dist/content/specialist-prompts/index.d.ts +2 -0
  51. package/dist/content/specialist-prompts/index.js +14 -0
  52. package/dist/content/specialist-prompts/planner.d.ts +1 -0
  53. package/dist/content/specialist-prompts/planner.js +182 -0
  54. package/dist/content/specialist-prompts/reviewer.d.ts +1 -0
  55. package/dist/content/specialist-prompts/reviewer.js +193 -0
  56. package/dist/content/specialist-prompts/security-reviewer.d.ts +1 -0
  57. package/dist/content/specialist-prompts/security-reviewer.js +133 -0
  58. package/dist/content/specialist-prompts/slice-builder.d.ts +1 -0
  59. package/dist/content/specialist-prompts/slice-builder.js +232 -0
  60. package/dist/content/stage-playbooks.d.ts +8 -0
  61. package/dist/content/stage-playbooks.js +404 -0
  62. package/dist/content/start-command.d.ts +2 -12
  63. package/dist/content/start-command.js +221 -207
  64. package/dist/flow-state.d.ts +21 -178
  65. package/dist/flow-state.js +67 -170
  66. package/dist/fs-utils.d.ts +6 -26
  67. package/dist/fs-utils.js +29 -162
  68. package/dist/gitignore.d.ts +2 -1
  69. package/dist/gitignore.js +51 -34
  70. package/dist/harness-detect.d.ts +10 -0
  71. package/dist/harness-detect.js +29 -0
  72. package/dist/harness-prompt.d.ts +26 -0
  73. package/dist/harness-prompt.js +142 -0
  74. package/dist/install.d.ts +35 -15
  75. package/dist/install.js +238 -1347
  76. package/dist/knowledge-store.d.ts +19 -163
  77. package/dist/knowledge-store.js +56 -590
  78. package/dist/logger.d.ts +8 -3
  79. package/dist/logger.js +13 -4
  80. package/dist/orchestrator-routing.d.ts +29 -0
  81. package/dist/orchestrator-routing.js +156 -0
  82. package/dist/run-persistence.d.ts +7 -118
  83. package/dist/run-persistence.js +29 -845
  84. package/dist/runtime/run-hook.entry.d.ts +1 -3
  85. package/dist/runtime/run-hook.entry.js +19 -4
  86. package/dist/runtime/run-hook.mjs +13 -1024
  87. package/dist/types.d.ts +25 -261
  88. package/dist/types.js +8 -36
  89. package/package.json +6 -3
  90. package/dist/artifact-linter/brainstorm.d.ts +0 -2
  91. package/dist/artifact-linter/brainstorm.js +0 -353
  92. package/dist/artifact-linter/design.d.ts +0 -18
  93. package/dist/artifact-linter/design.js +0 -444
  94. package/dist/artifact-linter/findings-dedup.d.ts +0 -56
  95. package/dist/artifact-linter/findings-dedup.js +0 -232
  96. package/dist/artifact-linter/plan.d.ts +0 -2
  97. package/dist/artifact-linter/plan.js +0 -826
  98. package/dist/artifact-linter/review-army.d.ts +0 -49
  99. package/dist/artifact-linter/review-army.js +0 -520
  100. package/dist/artifact-linter/review.d.ts +0 -2
  101. package/dist/artifact-linter/review.js +0 -113
  102. package/dist/artifact-linter/scope.d.ts +0 -2
  103. package/dist/artifact-linter/scope.js +0 -158
  104. package/dist/artifact-linter/shared.d.ts +0 -637
  105. package/dist/artifact-linter/shared.js +0 -2163
  106. package/dist/artifact-linter/ship.d.ts +0 -2
  107. package/dist/artifact-linter/ship.js +0 -250
  108. package/dist/artifact-linter/spec.d.ts +0 -2
  109. package/dist/artifact-linter/spec.js +0 -176
  110. package/dist/artifact-linter/tdd.d.ts +0 -118
  111. package/dist/artifact-linter/tdd.js +0 -1404
  112. package/dist/artifact-linter.d.ts +0 -15
  113. package/dist/artifact-linter.js +0 -517
  114. package/dist/codex-feature-flag.d.ts +0 -58
  115. package/dist/codex-feature-flag.js +0 -193
  116. package/dist/content/closeout-guidance.d.ts +0 -14
  117. package/dist/content/closeout-guidance.js +0 -44
  118. package/dist/content/diff-command.d.ts +0 -1
  119. package/dist/content/diff-command.js +0 -43
  120. package/dist/content/harness-doc.d.ts +0 -1
  121. package/dist/content/harness-doc.js +0 -65
  122. package/dist/content/hook-events.d.ts +0 -9
  123. package/dist/content/hook-events.js +0 -23
  124. package/dist/content/hook-manifest.d.ts +0 -81
  125. package/dist/content/hook-manifest.js +0 -156
  126. package/dist/content/hooks.d.ts +0 -11
  127. package/dist/content/hooks.js +0 -1972
  128. package/dist/content/idea.d.ts +0 -60
  129. package/dist/content/idea.js +0 -416
  130. package/dist/content/language-policy.d.ts +0 -2
  131. package/dist/content/language-policy.js +0 -13
  132. package/dist/content/learnings.d.ts +0 -6
  133. package/dist/content/learnings.js +0 -141
  134. package/dist/content/observe.d.ts +0 -19
  135. package/dist/content/observe.js +0 -86
  136. package/dist/content/opencode-plugin.d.ts +0 -1
  137. package/dist/content/opencode-plugin.js +0 -635
  138. package/dist/content/review-prompts.d.ts +0 -1
  139. package/dist/content/review-prompts.js +0 -104
  140. package/dist/content/runtime-shared-snippets.d.ts +0 -8
  141. package/dist/content/runtime-shared-snippets.js +0 -80
  142. package/dist/content/session-hooks.d.ts +0 -7
  143. package/dist/content/session-hooks.js +0 -107
  144. package/dist/content/skills-elicitation.d.ts +0 -1
  145. package/dist/content/skills-elicitation.js +0 -167
  146. package/dist/content/stage-command.d.ts +0 -2
  147. package/dist/content/stage-command.js +0 -17
  148. package/dist/content/stage-schema.d.ts +0 -117
  149. package/dist/content/stage-schema.js +0 -955
  150. package/dist/content/stages/_lint-metadata/index.d.ts +0 -2
  151. package/dist/content/stages/_lint-metadata/index.js +0 -97
  152. package/dist/content/stages/brainstorm.d.ts +0 -2
  153. package/dist/content/stages/brainstorm.js +0 -184
  154. package/dist/content/stages/design.d.ts +0 -2
  155. package/dist/content/stages/design.js +0 -288
  156. package/dist/content/stages/index.d.ts +0 -8
  157. package/dist/content/stages/index.js +0 -11
  158. package/dist/content/stages/plan.d.ts +0 -2
  159. package/dist/content/stages/plan.js +0 -191
  160. package/dist/content/stages/review.d.ts +0 -2
  161. package/dist/content/stages/review.js +0 -240
  162. package/dist/content/stages/schema-types.d.ts +0 -203
  163. package/dist/content/stages/schema-types.js +0 -1
  164. package/dist/content/stages/scope.d.ts +0 -2
  165. package/dist/content/stages/scope.js +0 -254
  166. package/dist/content/stages/ship.d.ts +0 -2
  167. package/dist/content/stages/ship.js +0 -159
  168. package/dist/content/stages/spec.d.ts +0 -2
  169. package/dist/content/stages/spec.js +0 -170
  170. package/dist/content/stages/tdd.d.ts +0 -4
  171. package/dist/content/stages/tdd.js +0 -273
  172. package/dist/content/state-contracts.d.ts +0 -1
  173. package/dist/content/state-contracts.js +0 -63
  174. package/dist/content/status-command.d.ts +0 -4
  175. package/dist/content/status-command.js +0 -109
  176. package/dist/content/subagent-context-skills.d.ts +0 -4
  177. package/dist/content/subagent-context-skills.js +0 -279
  178. package/dist/content/subagents.d.ts +0 -3
  179. package/dist/content/subagents.js +0 -997
  180. package/dist/content/templates.d.ts +0 -26
  181. package/dist/content/templates.js +0 -1692
  182. package/dist/content/track-render-context.d.ts +0 -18
  183. package/dist/content/track-render-context.js +0 -53
  184. package/dist/content/tree-command.d.ts +0 -1
  185. package/dist/content/tree-command.js +0 -64
  186. package/dist/content/utility-skills.d.ts +0 -30
  187. package/dist/content/utility-skills.js +0 -160
  188. package/dist/content/view-command.d.ts +0 -2
  189. package/dist/content/view-command.js +0 -92
  190. package/dist/delegation.d.ts +0 -649
  191. package/dist/delegation.js +0 -1539
  192. package/dist/early-loop.d.ts +0 -70
  193. package/dist/early-loop.js +0 -302
  194. package/dist/execution-topology.d.ts +0 -44
  195. package/dist/execution-topology.js +0 -95
  196. package/dist/gate-evidence.d.ts +0 -85
  197. package/dist/gate-evidence.js +0 -631
  198. package/dist/harness-adapters.d.ts +0 -151
  199. package/dist/harness-adapters.js +0 -756
  200. package/dist/harness-selection.d.ts +0 -31
  201. package/dist/harness-selection.js +0 -214
  202. package/dist/hook-schema.d.ts +0 -6
  203. package/dist/hook-schema.js +0 -114
  204. package/dist/hook-schemas/claude-hooks.v1.json +0 -10
  205. package/dist/hook-schemas/codex-hooks.v1.json +0 -10
  206. package/dist/hook-schemas/cursor-hooks.v1.json +0 -13
  207. package/dist/init-detect.d.ts +0 -2
  208. package/dist/init-detect.js +0 -50
  209. package/dist/internal/advance-stage/advance.d.ts +0 -89
  210. package/dist/internal/advance-stage/advance.js +0 -655
  211. package/dist/internal/advance-stage/cancel-run.d.ts +0 -8
  212. package/dist/internal/advance-stage/cancel-run.js +0 -19
  213. package/dist/internal/advance-stage/flow-state-coercion.d.ts +0 -3
  214. package/dist/internal/advance-stage/flow-state-coercion.js +0 -81
  215. package/dist/internal/advance-stage/helpers.d.ts +0 -14
  216. package/dist/internal/advance-stage/helpers.js +0 -145
  217. package/dist/internal/advance-stage/hook.d.ts +0 -8
  218. package/dist/internal/advance-stage/hook.js +0 -40
  219. package/dist/internal/advance-stage/parsers.d.ts +0 -72
  220. package/dist/internal/advance-stage/parsers.js +0 -357
  221. package/dist/internal/advance-stage/proactive-delegation-trace.d.ts +0 -24
  222. package/dist/internal/advance-stage/proactive-delegation-trace.js +0 -56
  223. package/dist/internal/advance-stage/review-loop.d.ts +0 -16
  224. package/dist/internal/advance-stage/review-loop.js +0 -199
  225. package/dist/internal/advance-stage/rewind.d.ts +0 -14
  226. package/dist/internal/advance-stage/rewind.js +0 -108
  227. package/dist/internal/advance-stage/start-flow.d.ts +0 -13
  228. package/dist/internal/advance-stage/start-flow.js +0 -241
  229. package/dist/internal/advance-stage/verify.d.ts +0 -21
  230. package/dist/internal/advance-stage/verify.js +0 -185
  231. package/dist/internal/advance-stage.d.ts +0 -7
  232. package/dist/internal/advance-stage.js +0 -138
  233. package/dist/internal/cohesion-contract-stub.d.ts +0 -24
  234. package/dist/internal/cohesion-contract-stub.js +0 -148
  235. package/dist/internal/compound-readiness.d.ts +0 -23
  236. package/dist/internal/compound-readiness.js +0 -102
  237. package/dist/internal/detect-public-api-changes.d.ts +0 -5
  238. package/dist/internal/detect-public-api-changes.js +0 -45
  239. package/dist/internal/detect-supply-chain-changes.d.ts +0 -6
  240. package/dist/internal/detect-supply-chain-changes.js +0 -138
  241. package/dist/internal/early-loop-status.d.ts +0 -7
  242. package/dist/internal/early-loop-status.js +0 -93
  243. package/dist/internal/envelope-validate.d.ts +0 -7
  244. package/dist/internal/envelope-validate.js +0 -66
  245. package/dist/internal/flow-state-repair.d.ts +0 -20
  246. package/dist/internal/flow-state-repair.js +0 -104
  247. package/dist/internal/plan-split-waves.d.ts +0 -190
  248. package/dist/internal/plan-split-waves.js +0 -764
  249. package/dist/internal/runtime-integrity.d.ts +0 -7
  250. package/dist/internal/runtime-integrity.js +0 -268
  251. package/dist/internal/slice-commit.d.ts +0 -7
  252. package/dist/internal/slice-commit.js +0 -619
  253. package/dist/internal/tdd-loop-status.d.ts +0 -14
  254. package/dist/internal/tdd-loop-status.js +0 -68
  255. package/dist/internal/tdd-red-evidence.d.ts +0 -7
  256. package/dist/internal/tdd-red-evidence.js +0 -153
  257. package/dist/internal/waiver-grant.d.ts +0 -62
  258. package/dist/internal/waiver-grant.js +0 -294
  259. package/dist/internal/wave-status.d.ts +0 -74
  260. package/dist/internal/wave-status.js +0 -506
  261. package/dist/managed-resources.d.ts +0 -53
  262. package/dist/managed-resources.js +0 -313
  263. package/dist/policy.d.ts +0 -10
  264. package/dist/policy.js +0 -167
  265. package/dist/retro-gate.d.ts +0 -9
  266. package/dist/retro-gate.js +0 -47
  267. package/dist/run-archive.d.ts +0 -61
  268. package/dist/run-archive.js +0 -391
  269. package/dist/runs.d.ts +0 -2
  270. package/dist/runs.js +0 -2
  271. package/dist/stack-detection.d.ts +0 -116
  272. package/dist/stack-detection.js +0 -489
  273. package/dist/streaming/event-stream.d.ts +0 -31
  274. package/dist/streaming/event-stream.js +0 -114
  275. package/dist/tdd-cycle.d.ts +0 -107
  276. package/dist/tdd-cycle.js +0 -289
  277. package/dist/tdd-verification-evidence.d.ts +0 -17
  278. package/dist/tdd-verification-evidence.js +0 -122
  279. package/dist/track-heuristics.d.ts +0 -27
  280. package/dist/track-heuristics.js +0 -154
  281. package/dist/util/slice-id.d.ts +0 -58
  282. package/dist/util/slice-id.js +0 -89
  283. package/dist/worktree-manager.d.ts +0 -20
  284. package/dist/worktree-manager.js +0 -108
@@ -1,619 +0,0 @@
1
- import { execFile } from "node:child_process";
2
- import path from "node:path";
3
- import { promisify } from "node:util";
4
- import { readConfig, resolveLockfileTwinPolicy, resolveTddCommitMode, resolveTddIsolationMode, resolveTddWorktreeRoot } from "../config.js";
5
- import { readDelegationLedger } from "../delegation.js";
6
- import { exists } from "../fs-utils.js";
7
- import { loadStackAdapter } from "../stack-detection.js";
8
- import { cleanupWorktree, commitAndMergeBack, createSliceWorktree, WorktreeMergeConflictError, WorktreeUnsupportedError } from "../worktree-manager.js";
9
- const execFileAsync = promisify(execFile);
10
- function parseCsv(raw) {
11
- return raw
12
- .split(",")
13
- .map((value) => value.trim())
14
- .filter((value) => value.length > 0);
15
- }
16
- function normalizePathLike(value) {
17
- const slashes = value.replace(/\\/gu, "/");
18
- const withoutDot = slashes.replace(/^\.\//u, "");
19
- return withoutDot.replace(/\/+$/u, "");
20
- }
21
- function parseSliceCommitArgs(tokens) {
22
- let sliceId = "";
23
- let spanId = "";
24
- let taskId;
25
- let title;
26
- let runId;
27
- let worktreePath;
28
- const claimedPaths = [];
29
- let prepareWorktree = false;
30
- let json = false;
31
- let quiet = false;
32
- for (let i = 0; i < tokens.length; i += 1) {
33
- const token = tokens[i];
34
- const next = tokens[i + 1];
35
- const valueFrom = (flag) => {
36
- if (token.startsWith(`${flag}=`))
37
- return token.slice(flag.length + 1);
38
- if (token === flag && next && !next.startsWith("--")) {
39
- i += 1;
40
- return next;
41
- }
42
- throw new Error(`${flag} requires a value.`);
43
- };
44
- if (token === "--json") {
45
- json = true;
46
- continue;
47
- }
48
- if (token === "--quiet") {
49
- quiet = true;
50
- continue;
51
- }
52
- if (token === "--prepare-worktree") {
53
- prepareWorktree = true;
54
- continue;
55
- }
56
- if (token.startsWith("--slice=") || token === "--slice") {
57
- sliceId = valueFrom("--slice").trim();
58
- continue;
59
- }
60
- if (token.startsWith("--span-id=") || token === "--span-id") {
61
- spanId = valueFrom("--span-id").trim();
62
- continue;
63
- }
64
- if (token.startsWith("--task-id=") || token === "--task-id") {
65
- taskId = valueFrom("--task-id").trim();
66
- continue;
67
- }
68
- if (token.startsWith("--title=") || token === "--title") {
69
- title = valueFrom("--title").trim();
70
- continue;
71
- }
72
- if (token.startsWith("--run-id=") || token === "--run-id") {
73
- runId = valueFrom("--run-id").trim();
74
- continue;
75
- }
76
- if (token.startsWith("--worktree-path=") || token === "--worktree-path") {
77
- const resolved = valueFrom("--worktree-path").trim();
78
- if (resolved.length > 0) {
79
- worktreePath = resolved;
80
- }
81
- continue;
82
- }
83
- if (token.startsWith("--claimed-paths=") || token === "--claimed-paths") {
84
- claimedPaths.push(...parseCsv(valueFrom("--claimed-paths")));
85
- continue;
86
- }
87
- if (token.startsWith("--claimed-path=") || token === "--claimed-path") {
88
- const one = valueFrom("--claimed-path").trim();
89
- if (one.length > 0)
90
- claimedPaths.push(one);
91
- continue;
92
- }
93
- throw new Error(`Unknown flag for internal slice-commit: ${token}`);
94
- }
95
- if (sliceId.length === 0) {
96
- throw new Error("internal slice-commit requires --slice=<S-N>.");
97
- }
98
- if (spanId.length === 0) {
99
- throw new Error("internal slice-commit requires --span-id=<span-id>.");
100
- }
101
- return {
102
- sliceId,
103
- spanId,
104
- taskId,
105
- title,
106
- runId,
107
- worktreePath,
108
- claimedPaths,
109
- prepareWorktree,
110
- json,
111
- quiet
112
- };
113
- }
114
- function output(io, args, payload, channel = "stdout") {
115
- if (args.quiet && channel === "stdout")
116
- return;
117
- const writer = channel === "stdout" ? io.stdout : io.stderr;
118
- if (args.json) {
119
- writer.write(`${JSON.stringify(payload)}\n`);
120
- return;
121
- }
122
- const message = typeof payload.message === "string"
123
- ? payload.message
124
- : JSON.stringify(payload);
125
- writer.write(`${message}\n`);
126
- }
127
- function parsePorcelainPaths(raw) {
128
- const out = [];
129
- for (const line of raw.split(/\r?\n/gu)) {
130
- const trimmed = line.trimEnd();
131
- if (trimmed.length < 4)
132
- continue;
133
- // porcelain line shape: XY<space><path>
134
- const status = trimmed.slice(0, 2);
135
- if (status === "??") {
136
- const p = normalizePathLike(trimmed.slice(3).trim());
137
- if (p.length > 0)
138
- out.push(p);
139
- continue;
140
- }
141
- let p = trimmed.slice(3).trim();
142
- const renameIdx = p.indexOf(" -> ");
143
- if (renameIdx >= 0) {
144
- p = p.slice(renameIdx + 4);
145
- }
146
- p = normalizePathLike(p.replace(/^"/u, "").replace(/"$/u, ""));
147
- if (p.length > 0)
148
- out.push(p);
149
- }
150
- return [...new Set(out)];
151
- }
152
- async function gitChangedPaths(cwd) {
153
- const { stdout: statusRaw } = await execFileAsync("git", ["status", "--porcelain", "-uall"], {
154
- cwd
155
- });
156
- return parsePorcelainPaths(statusRaw);
157
- }
158
- function matchesClaimedPath(changedPath, claimedPaths) {
159
- const changed = normalizePathLike(changedPath);
160
- return claimedPaths.some((rawClaimed) => {
161
- const claimed = normalizePathLike(rawClaimed);
162
- if (claimed.length === 0)
163
- return false;
164
- if (changed === claimed)
165
- return true;
166
- return changed.startsWith(`${claimed}/`);
167
- });
168
- }
169
- /**
170
- * 7.6.0 — match a candidate path against a stack-adapter glob pattern.
171
- *
172
- * Adapter globs are intentionally simple: literal paths (`Cargo.toml`),
173
- * recursive prefix (`**\/Cargo.toml`), or single-level wildcard
174
- * (`*.csproj`). We translate those shapes here without pulling in a
175
- * full glob library so the slice-commit hook stays dependency-light.
176
- */
177
- function matchesAdapterGlob(candidate, glob) {
178
- const normalizedCandidate = normalizePathLike(candidate);
179
- const normalizedGlob = normalizePathLike(glob);
180
- if (normalizedGlob.length === 0)
181
- return false;
182
- if (normalizedGlob.includes("**")) {
183
- // `**/foo` → match either `foo` at root or any nested `foo`.
184
- if (normalizedGlob.startsWith("**/")) {
185
- const tail = normalizedGlob.slice(3);
186
- if (tail === normalizedCandidate)
187
- return true;
188
- return normalizedCandidate.endsWith(`/${tail}`);
189
- }
190
- // Generic ** in the middle: collapse to suffix match for simplicity.
191
- const tail = normalizedGlob.split("**/").pop() ?? "";
192
- return tail.length > 0 && normalizedCandidate.endsWith(tail);
193
- }
194
- if (normalizedGlob.includes("*")) {
195
- // Single-segment wildcard like `*.csproj`. Convert to a basic regex.
196
- const regexSrc = normalizedGlob
197
- .split("/")
198
- .map((segment) => segment
199
- .replace(/[.+?^${}()|[\]\\]/gu, "\\$&")
200
- .replace(/\*/gu, "[^/]*"))
201
- .join("/");
202
- return new RegExp(`^${regexSrc}$`, "u").test(normalizedCandidate);
203
- }
204
- return normalizedGlob === normalizedCandidate;
205
- }
206
- /**
207
- * Find lockfile twins whose manifestGlob matches at least one claimed
208
- * path. The returned twins are the candidates whose lockfileGlob we
209
- * should auto-include / auto-revert when they drift.
210
- */
211
- function activeLockfileTwins(adapter, claimedPaths) {
212
- if (adapter.lockfileTwins.length === 0)
213
- return [];
214
- const active = [];
215
- for (const twin of adapter.lockfileTwins) {
216
- const claimedManifest = claimedPaths.some((claimed) => matchesAdapterGlob(claimed, twin.manifestGlob));
217
- if (claimedManifest)
218
- active.push(twin);
219
- }
220
- return active;
221
- }
222
- /**
223
- * Partition a candidate path: `is it a lockfile twin we should
224
- * auto-handle?`. Returns the twin entry that matches, or null.
225
- */
226
- function findMatchingLockfileTwin(changedPath, twins) {
227
- for (const twin of twins) {
228
- if (matchesAdapterGlob(changedPath, twin.lockfileGlob)) {
229
- return twin;
230
- }
231
- }
232
- return null;
233
- }
234
- async function resolveClaimedPathsFromLedger(projectRoot, args) {
235
- const ledger = await readDelegationLedger(projectRoot);
236
- const matches = ledger.entries.filter((entry) => entry.stage === "tdd" &&
237
- entry.agent === "slice-builder" &&
238
- entry.sliceId === args.sliceId &&
239
- entry.spanId === args.spanId &&
240
- (!args.runId || entry.runId === args.runId) &&
241
- Array.isArray(entry.claimedPaths) &&
242
- entry.claimedPaths.length > 0);
243
- matches.sort((a, b) => {
244
- const aTs = a.ts ?? a.startTs ?? "";
245
- const bTs = b.ts ?? b.startTs ?? "";
246
- return aTs < bTs ? 1 : aTs > bTs ? -1 : 0;
247
- });
248
- const fromLedger = matches[0]?.claimedPaths ?? [];
249
- return [...new Set(fromLedger.map((p) => normalizePathLike(p)).filter((p) => p.length > 0))];
250
- }
251
- export async function runSliceCommitCommand(projectRoot, tokens, io) {
252
- let args;
253
- try {
254
- args = parseSliceCommitArgs(tokens);
255
- }
256
- catch (err) {
257
- io.stderr.write(`cclaw internal slice-commit: ${err instanceof Error ? err.message : String(err)}\n`);
258
- return 1;
259
- }
260
- const config = await readConfig(projectRoot).catch(() => null);
261
- const commitMode = resolveTddCommitMode(config);
262
- const isolationMode = resolveTddIsolationMode(config);
263
- const worktreeRoot = resolveTddWorktreeRoot(config);
264
- const lockfileTwinPolicy = resolveLockfileTwinPolicy(config);
265
- const stackAdapter = await loadStackAdapter(projectRoot);
266
- const gitPresent = await exists(path.join(projectRoot, ".git"));
267
- if (args.prepareWorktree) {
268
- if (!gitPresent) {
269
- output(io, args, {
270
- ok: true,
271
- skipped: true,
272
- reason: "no-git",
273
- message: "slice-worktree skipped: .git is missing"
274
- });
275
- return 0;
276
- }
277
- if (isolationMode === "in-place") {
278
- output(io, args, {
279
- ok: true,
280
- skipped: true,
281
- reason: "isolation-in-place",
282
- isolationMode,
283
- message: "slice-worktree skipped: tdd.isolationMode=in-place"
284
- });
285
- return 0;
286
- }
287
- try {
288
- const { stdout } = await execFileAsync("git", ["rev-parse", "HEAD"], { cwd: projectRoot });
289
- const prepared = await createSliceWorktree(args.sliceId, stdout.trim(), args.claimedPaths, {
290
- projectRoot,
291
- worktreeRoot
292
- });
293
- output(io, args, {
294
- ok: true,
295
- prepared: true,
296
- sliceId: args.sliceId,
297
- spanId: args.spanId,
298
- worktreePath: prepared.path,
299
- baseRef: prepared.ref
300
- });
301
- return 0;
302
- }
303
- catch (error) {
304
- if (error instanceof WorktreeUnsupportedError) {
305
- output(io, args, {
306
- ok: true,
307
- skipped: true,
308
- reason: "worktree-unavailable",
309
- degradedCommitMode: "agent-required",
310
- message: error.message
311
- });
312
- return 0;
313
- }
314
- output(io, args, {
315
- ok: false,
316
- errorCode: "worktree_prepare_failed",
317
- details: {
318
- message: error instanceof Error ? error.message : String(error)
319
- },
320
- message: `worktree_prepare_failed: ${error instanceof Error ? error.message : String(error)}`
321
- }, "stderr");
322
- return 1;
323
- }
324
- }
325
- if (commitMode !== "managed-per-slice") {
326
- output(io, args, {
327
- ok: true,
328
- skipped: true,
329
- reason: "commit-mode-not-managed",
330
- commitMode,
331
- message: `slice-commit skipped: commitMode=${commitMode}`
332
- });
333
- return 0;
334
- }
335
- if (!gitPresent) {
336
- output(io, args, {
337
- ok: true,
338
- skipped: true,
339
- reason: "no-git",
340
- message: "slice-commit skipped: .git is missing"
341
- });
342
- return 0;
343
- }
344
- const claimedPaths = args.claimedPaths.length > 0
345
- ? [...new Set(args.claimedPaths.map((p) => normalizePathLike(p)).filter((p) => p.length > 0))]
346
- : await resolveClaimedPathsFromLedger(projectRoot, args);
347
- if (claimedPaths.length === 0) {
348
- output(io, args, {
349
- ok: false,
350
- errorCode: "slice_commit_claimed_paths_missing",
351
- details: {
352
- sliceId: args.sliceId,
353
- spanId: args.spanId
354
- },
355
- message: `slice_commit_claimed_paths_missing: no claimed paths for ${args.sliceId}/${args.spanId}`
356
- }, "stderr");
357
- return 2;
358
- }
359
- let managedWorktreePath = null;
360
- let activeCwd = projectRoot;
361
- let degradedToInPlace = false;
362
- const requestedWorktreePath = typeof args.worktreePath === "string" && args.worktreePath.trim().length > 0
363
- ? path.resolve(projectRoot, args.worktreePath.trim())
364
- : null;
365
- if (requestedWorktreePath && await exists(requestedWorktreePath)) {
366
- managedWorktreePath = requestedWorktreePath;
367
- activeCwd = requestedWorktreePath;
368
- }
369
- else if (isolationMode !== "in-place") {
370
- try {
371
- const { stdout } = await execFileAsync("git", ["rev-parse", "HEAD"], { cwd: projectRoot });
372
- const prepared = await createSliceWorktree(args.sliceId, stdout.trim(), claimedPaths, {
373
- projectRoot,
374
- worktreeRoot
375
- });
376
- managedWorktreePath = prepared.path;
377
- activeCwd = prepared.path;
378
- }
379
- catch (error) {
380
- if (error instanceof WorktreeUnsupportedError) {
381
- output(io, args, {
382
- ok: true,
383
- skipped: true,
384
- reason: "worktree-unavailable",
385
- degradedCommitMode: "agent-required",
386
- message: error.message
387
- });
388
- return 0;
389
- }
390
- output(io, args, {
391
- ok: false,
392
- errorCode: "worktree_prepare_failed",
393
- details: {
394
- message: error instanceof Error ? error.message : String(error)
395
- },
396
- message: `worktree_prepare_failed: ${error instanceof Error ? error.message : String(error)}`
397
- }, "stderr");
398
- return 1;
399
- }
400
- }
401
- const cleanupManagedWorktree = async () => {
402
- if (!managedWorktreePath)
403
- return;
404
- await cleanupWorktree(managedWorktreePath, { projectRoot }).catch(() => undefined);
405
- };
406
- let changedPaths = await gitChangedPaths(activeCwd);
407
- if (changedPaths.length === 0 && managedWorktreePath && activeCwd !== projectRoot) {
408
- const rootChangedPaths = await gitChangedPaths(projectRoot);
409
- if (rootChangedPaths.length > 0) {
410
- activeCwd = projectRoot;
411
- changedPaths = rootChangedPaths;
412
- degradedToInPlace = true;
413
- }
414
- }
415
- if (changedPaths.length === 0) {
416
- await cleanupManagedWorktree();
417
- output(io, args, {
418
- ok: true,
419
- skipped: true,
420
- reason: "no-changes",
421
- message: `slice-commit skipped: no working-tree changes for ${args.sliceId}`
422
- });
423
- return 0;
424
- }
425
- const initialDrift = changedPaths.filter((p) => !matchesClaimedPath(p, claimedPaths));
426
- const twinsForCommit = activeLockfileTwins(stackAdapter, claimedPaths);
427
- // 7.6.0 — split drift into "lockfile twin drift" (handle per policy)
428
- // vs "true drift" (always rejected).
429
- const lockfileTwinDrift = [];
430
- const trueDrift = [];
431
- for (const driftPath of initialDrift) {
432
- const twin = findMatchingLockfileTwin(driftPath, twinsForCommit);
433
- if (twin) {
434
- lockfileTwinDrift.push({ path: driftPath, twin });
435
- }
436
- else {
437
- trueDrift.push(driftPath);
438
- }
439
- }
440
- // Report a separate true-drift error when there is actual non-twin
441
- // drift, regardless of policy: the operator's claim should still
442
- // cover everything they changed.
443
- if (trueDrift.length > 0) {
444
- output(io, args, {
445
- ok: false,
446
- errorCode: "slice_commit_path_drift",
447
- details: {
448
- sliceId: args.sliceId,
449
- spanId: args.spanId,
450
- claimedPaths,
451
- driftPaths: trueDrift
452
- },
453
- message: `slice_commit_path_drift: ${trueDrift.join(", ")}`
454
- }, "stderr");
455
- return 2;
456
- }
457
- // strict-fence: lockfile twins still count as drift.
458
- if (lockfileTwinDrift.length > 0 && lockfileTwinPolicy === "strict-fence") {
459
- const driftPaths = lockfileTwinDrift.map((entry) => entry.path);
460
- output(io, args, {
461
- ok: false,
462
- errorCode: "slice_commit_path_drift",
463
- details: {
464
- sliceId: args.sliceId,
465
- spanId: args.spanId,
466
- claimedPaths,
467
- driftPaths,
468
- lockfileTwinPolicy,
469
- stackAdapterId: stackAdapter.id
470
- },
471
- message: `slice_commit_path_drift: ${driftPaths.join(", ")} (lockfileTwinPolicy=strict-fence)`
472
- }, "stderr");
473
- return 2;
474
- }
475
- // auto-revert: restore the lockfile, then exclude from changed set.
476
- const revertedTwinPaths = [];
477
- if (lockfileTwinDrift.length > 0 && lockfileTwinPolicy === "auto-revert") {
478
- for (const entry of lockfileTwinDrift) {
479
- try {
480
- await execFileAsync("git", ["restore", "--", entry.path], { cwd: activeCwd });
481
- revertedTwinPaths.push(entry.path);
482
- }
483
- catch {
484
- // Fall through; if restore fails the drift will reappear in the
485
- // recomputed status and we'll reject as drift.
486
- }
487
- }
488
- changedPaths = await gitChangedPaths(activeCwd);
489
- const remainingDrift = changedPaths.filter((p) => !matchesClaimedPath(p, claimedPaths));
490
- if (remainingDrift.length > 0) {
491
- output(io, args, {
492
- ok: false,
493
- errorCode: "slice_commit_path_drift",
494
- details: {
495
- sliceId: args.sliceId,
496
- spanId: args.spanId,
497
- claimedPaths,
498
- driftPaths: remainingDrift,
499
- lockfileTwinPolicy,
500
- stackAdapterId: stackAdapter.id
501
- },
502
- message: `slice_commit_path_drift: ${remainingDrift.join(", ")}`
503
- }, "stderr");
504
- return 2;
505
- }
506
- }
507
- // auto-include: add the twin path(s) to the effective claim so the
508
- // commit picks them up. We don't mutate the persisted claim — only
509
- // the in-memory list used for the upcoming `git add`.
510
- const effectiveCommitPaths = [...claimedPaths];
511
- const includedTwinPaths = [];
512
- if (lockfileTwinDrift.length > 0 && lockfileTwinPolicy === "auto-include") {
513
- for (const entry of lockfileTwinDrift) {
514
- if (!effectiveCommitPaths.includes(entry.path)) {
515
- effectiveCommitPaths.push(entry.path);
516
- }
517
- includedTwinPaths.push(entry.path);
518
- }
519
- }
520
- const changedInClaim = changedPaths.filter((p) => matchesClaimedPath(p, claimedPaths) ||
521
- (lockfileTwinPolicy === "auto-include" &&
522
- findMatchingLockfileTwin(p, twinsForCommit) !== null));
523
- if (changedInClaim.length === 0) {
524
- await cleanupManagedWorktree();
525
- output(io, args, {
526
- ok: true,
527
- skipped: true,
528
- reason: "claimed-paths-unchanged",
529
- message: `slice-commit skipped: no changes within claimed paths for ${args.sliceId}`
530
- });
531
- return 0;
532
- }
533
- try {
534
- await execFileAsync("git", ["add", "--", ...effectiveCommitPaths], {
535
- cwd: activeCwd
536
- });
537
- const taskPart = args.taskId && args.taskId.length > 0 ? args.taskId : "task";
538
- const titlePart = args.title && args.title.length > 0 ? args.title : "slice update";
539
- const header = `${args.sliceId}/${taskPart}: ${titlePart}`;
540
- const body = [
541
- `span-id: ${args.spanId}`,
542
- `run-id: ${args.runId ?? "unknown"}`,
543
- "phase-cycle: red->green->refactor->doc"
544
- ].join("\n");
545
- await execFileAsync("git", ["commit", "-m", header, "-m", body], {
546
- cwd: activeCwd
547
- });
548
- }
549
- catch (err) {
550
- const message = err instanceof Error ? err.message : String(err);
551
- if (/nothing to commit/iu.test(message)) {
552
- await cleanupManagedWorktree();
553
- output(io, args, {
554
- ok: true,
555
- skipped: true,
556
- reason: "nothing-to-commit",
557
- message: `slice-commit skipped: nothing to commit for ${args.sliceId}`
558
- });
559
- return 0;
560
- }
561
- output(io, args, {
562
- ok: false,
563
- errorCode: "slice_commit_failed",
564
- details: { message },
565
- message: `slice_commit_failed: ${message}`
566
- }, "stderr");
567
- return 1;
568
- }
569
- const { stdout: shaStdout } = await execFileAsync("git", ["rev-parse", "HEAD"], {
570
- cwd: activeCwd
571
- });
572
- let commitSha = shaStdout.trim();
573
- if (managedWorktreePath && activeCwd !== projectRoot) {
574
- try {
575
- const merged = await commitAndMergeBack(activeCwd, `merge ${args.sliceId}`, { projectRoot });
576
- commitSha = merged.commitSha;
577
- }
578
- catch (error) {
579
- if (error instanceof WorktreeMergeConflictError) {
580
- output(io, args, {
581
- ok: false,
582
- errorCode: "worktree_merge_conflict",
583
- details: {
584
- sliceId: args.sliceId,
585
- spanId: args.spanId,
586
- worktreePath: activeCwd,
587
- message: error.message
588
- },
589
- message: error.message
590
- }, "stderr");
591
- return 2;
592
- }
593
- output(io, args, {
594
- ok: false,
595
- errorCode: "slice_commit_failed",
596
- details: { message: error instanceof Error ? error.message : String(error) },
597
- message: `slice_commit_failed: ${error instanceof Error ? error.message : String(error)}`
598
- }, "stderr");
599
- return 1;
600
- }
601
- }
602
- await cleanupManagedWorktree();
603
- output(io, args, {
604
- ok: true,
605
- commitSha,
606
- sliceId: args.sliceId,
607
- spanId: args.spanId,
608
- claimedPaths,
609
- changedPaths: changedInClaim,
610
- worktreePath: managedWorktreePath ?? undefined,
611
- degradedToInPlace: degradedToInPlace || undefined,
612
- lockfileTwinPolicy,
613
- lockfileTwinsIncluded: includedTwinPaths.length > 0 ? includedTwinPaths : undefined,
614
- lockfileTwinsReverted: revertedTwinPaths.length > 0 ? revertedTwinPaths : undefined,
615
- stackAdapterId: stackAdapter.id,
616
- message: `slice commit created for ${args.sliceId}: ${commitSha}`
617
- });
618
- return 0;
619
- }
@@ -1,14 +0,0 @@
1
- import type { Writable } from "node:stream";
2
- import { type RalphLoopStatus } from "../tdd-cycle.js";
3
- interface InternalIo {
4
- stdout: Writable;
5
- stderr: Writable;
6
- }
7
- /**
8
- * Produces a one-line "Ralph Loop: iter=X, slices=Y, acClosed=Z, redOpen=..."
9
- * summary — suitable for bootstrap surfaces where the user
10
- * just needs a progress indicator, not the full slice breakdown.
11
- */
12
- export declare function formatRalphLoopStatusLine(status: RalphLoopStatus): string;
13
- export declare function runTddLoopStatusCommand(projectRoot: string, argv: string[], io: InternalIo): Promise<number>;
14
- export {};
@@ -1,68 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { RUNTIME_ROOT } from "../constants.js";
4
- import { writeFileSafe } from "../fs-utils.js";
5
- import { readFlowState } from "../runs.js";
6
- import { computeRalphLoopStatus, parseTddCycleLog } from "../tdd-cycle.js";
7
- function parseArgs(tokens) {
8
- const args = { json: false, quiet: false, write: true };
9
- for (const token of tokens) {
10
- if (token === "--json")
11
- args.json = true;
12
- else if (token === "--quiet")
13
- args.quiet = true;
14
- else if (token === "--no-write")
15
- args.write = false;
16
- else if (token === "--write")
17
- args.write = true;
18
- else
19
- throw new Error(`Unknown tdd-loop-status flag: ${token}`);
20
- }
21
- return args;
22
- }
23
- function stateDir(projectRoot) {
24
- return path.join(projectRoot, RUNTIME_ROOT, "state");
25
- }
26
- async function readCycleLog(projectRoot) {
27
- const filePath = path.join(stateDir(projectRoot), "tdd-cycle-log.jsonl");
28
- try {
29
- return await fs.readFile(filePath, "utf8");
30
- }
31
- catch (err) {
32
- if (err.code === "ENOENT")
33
- return "";
34
- throw err;
35
- }
36
- }
37
- /**
38
- * Produces a one-line "Ralph Loop: iter=X, slices=Y, acClosed=Z, redOpen=..."
39
- * summary — suitable for bootstrap surfaces where the user
40
- * just needs a progress indicator, not the full slice breakdown.
41
- */
42
- export function formatRalphLoopStatusLine(status) {
43
- const redOpen = status.redOpenSlices.length > 0
44
- ? status.redOpenSlices.join(",")
45
- : "none";
46
- return `Ralph Loop: iter=${status.loopIteration}, slices=${status.sliceCount}, acClosed=${status.acClosed.length}, redOpen=${redOpen}`;
47
- }
48
- export async function runTddLoopStatusCommand(projectRoot, argv, io) {
49
- const args = parseArgs(argv);
50
- const flow = await readFlowState(projectRoot).catch(() => null);
51
- const runId = flow?.activeRunId ?? "active";
52
- const text = await readCycleLog(projectRoot);
53
- const entries = parseTddCycleLog(text);
54
- const status = computeRalphLoopStatus(entries, { runId });
55
- if (args.write) {
56
- const target = path.join(stateDir(projectRoot), "ralph-loop.json");
57
- await writeFileSafe(target, `${JSON.stringify(status, null, 2)}\n`);
58
- }
59
- if (!args.quiet) {
60
- if (args.json) {
61
- io.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
62
- }
63
- else {
64
- io.stdout.write(`${formatRalphLoopStatusLine(status)}\n`);
65
- }
66
- }
67
- return 0;
68
- }