cclaw-cli 7.7.1 → 8.1.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 (282) hide show
  1. package/README.md +210 -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 +90 -508
  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/install.d.ts +27 -15
  73. package/dist/install.js +230 -1342
  74. package/dist/knowledge-store.d.ts +19 -163
  75. package/dist/knowledge-store.js +56 -590
  76. package/dist/logger.d.ts +8 -3
  77. package/dist/logger.js +13 -4
  78. package/dist/orchestrator-routing.d.ts +29 -0
  79. package/dist/orchestrator-routing.js +156 -0
  80. package/dist/run-persistence.d.ts +7 -118
  81. package/dist/run-persistence.js +29 -845
  82. package/dist/runtime/run-hook.entry.d.ts +1 -3
  83. package/dist/runtime/run-hook.entry.js +19 -4
  84. package/dist/runtime/run-hook.mjs +13 -1024
  85. package/dist/types.d.ts +25 -261
  86. package/dist/types.js +8 -36
  87. package/package.json +6 -3
  88. package/dist/artifact-linter/brainstorm.d.ts +0 -2
  89. package/dist/artifact-linter/brainstorm.js +0 -353
  90. package/dist/artifact-linter/design.d.ts +0 -18
  91. package/dist/artifact-linter/design.js +0 -444
  92. package/dist/artifact-linter/findings-dedup.d.ts +0 -56
  93. package/dist/artifact-linter/findings-dedup.js +0 -232
  94. package/dist/artifact-linter/plan.d.ts +0 -2
  95. package/dist/artifact-linter/plan.js +0 -826
  96. package/dist/artifact-linter/review-army.d.ts +0 -49
  97. package/dist/artifact-linter/review-army.js +0 -520
  98. package/dist/artifact-linter/review.d.ts +0 -2
  99. package/dist/artifact-linter/review.js +0 -113
  100. package/dist/artifact-linter/scope.d.ts +0 -2
  101. package/dist/artifact-linter/scope.js +0 -158
  102. package/dist/artifact-linter/shared.d.ts +0 -637
  103. package/dist/artifact-linter/shared.js +0 -2163
  104. package/dist/artifact-linter/ship.d.ts +0 -2
  105. package/dist/artifact-linter/ship.js +0 -250
  106. package/dist/artifact-linter/spec.d.ts +0 -2
  107. package/dist/artifact-linter/spec.js +0 -176
  108. package/dist/artifact-linter/tdd.d.ts +0 -118
  109. package/dist/artifact-linter/tdd.js +0 -1404
  110. package/dist/artifact-linter.d.ts +0 -15
  111. package/dist/artifact-linter.js +0 -517
  112. package/dist/codex-feature-flag.d.ts +0 -58
  113. package/dist/codex-feature-flag.js +0 -193
  114. package/dist/content/closeout-guidance.d.ts +0 -14
  115. package/dist/content/closeout-guidance.js +0 -44
  116. package/dist/content/diff-command.d.ts +0 -1
  117. package/dist/content/diff-command.js +0 -43
  118. package/dist/content/harness-doc.d.ts +0 -1
  119. package/dist/content/harness-doc.js +0 -65
  120. package/dist/content/hook-events.d.ts +0 -9
  121. package/dist/content/hook-events.js +0 -23
  122. package/dist/content/hook-manifest.d.ts +0 -81
  123. package/dist/content/hook-manifest.js +0 -156
  124. package/dist/content/hooks.d.ts +0 -11
  125. package/dist/content/hooks.js +0 -1972
  126. package/dist/content/idea.d.ts +0 -60
  127. package/dist/content/idea.js +0 -416
  128. package/dist/content/language-policy.d.ts +0 -2
  129. package/dist/content/language-policy.js +0 -13
  130. package/dist/content/learnings.d.ts +0 -6
  131. package/dist/content/learnings.js +0 -141
  132. package/dist/content/observe.d.ts +0 -19
  133. package/dist/content/observe.js +0 -86
  134. package/dist/content/opencode-plugin.d.ts +0 -1
  135. package/dist/content/opencode-plugin.js +0 -635
  136. package/dist/content/review-prompts.d.ts +0 -1
  137. package/dist/content/review-prompts.js +0 -104
  138. package/dist/content/runtime-shared-snippets.d.ts +0 -8
  139. package/dist/content/runtime-shared-snippets.js +0 -80
  140. package/dist/content/session-hooks.d.ts +0 -7
  141. package/dist/content/session-hooks.js +0 -107
  142. package/dist/content/skills-elicitation.d.ts +0 -1
  143. package/dist/content/skills-elicitation.js +0 -167
  144. package/dist/content/stage-command.d.ts +0 -2
  145. package/dist/content/stage-command.js +0 -17
  146. package/dist/content/stage-schema.d.ts +0 -117
  147. package/dist/content/stage-schema.js +0 -955
  148. package/dist/content/stages/_lint-metadata/index.d.ts +0 -2
  149. package/dist/content/stages/_lint-metadata/index.js +0 -97
  150. package/dist/content/stages/brainstorm.d.ts +0 -2
  151. package/dist/content/stages/brainstorm.js +0 -184
  152. package/dist/content/stages/design.d.ts +0 -2
  153. package/dist/content/stages/design.js +0 -288
  154. package/dist/content/stages/index.d.ts +0 -8
  155. package/dist/content/stages/index.js +0 -11
  156. package/dist/content/stages/plan.d.ts +0 -2
  157. package/dist/content/stages/plan.js +0 -191
  158. package/dist/content/stages/review.d.ts +0 -2
  159. package/dist/content/stages/review.js +0 -240
  160. package/dist/content/stages/schema-types.d.ts +0 -203
  161. package/dist/content/stages/schema-types.js +0 -1
  162. package/dist/content/stages/scope.d.ts +0 -2
  163. package/dist/content/stages/scope.js +0 -254
  164. package/dist/content/stages/ship.d.ts +0 -2
  165. package/dist/content/stages/ship.js +0 -159
  166. package/dist/content/stages/spec.d.ts +0 -2
  167. package/dist/content/stages/spec.js +0 -170
  168. package/dist/content/stages/tdd.d.ts +0 -4
  169. package/dist/content/stages/tdd.js +0 -273
  170. package/dist/content/state-contracts.d.ts +0 -1
  171. package/dist/content/state-contracts.js +0 -63
  172. package/dist/content/status-command.d.ts +0 -4
  173. package/dist/content/status-command.js +0 -109
  174. package/dist/content/subagent-context-skills.d.ts +0 -4
  175. package/dist/content/subagent-context-skills.js +0 -279
  176. package/dist/content/subagents.d.ts +0 -3
  177. package/dist/content/subagents.js +0 -997
  178. package/dist/content/templates.d.ts +0 -26
  179. package/dist/content/templates.js +0 -1692
  180. package/dist/content/track-render-context.d.ts +0 -18
  181. package/dist/content/track-render-context.js +0 -53
  182. package/dist/content/tree-command.d.ts +0 -1
  183. package/dist/content/tree-command.js +0 -64
  184. package/dist/content/utility-skills.d.ts +0 -30
  185. package/dist/content/utility-skills.js +0 -160
  186. package/dist/content/view-command.d.ts +0 -2
  187. package/dist/content/view-command.js +0 -92
  188. package/dist/delegation.d.ts +0 -649
  189. package/dist/delegation.js +0 -1539
  190. package/dist/early-loop.d.ts +0 -70
  191. package/dist/early-loop.js +0 -302
  192. package/dist/execution-topology.d.ts +0 -44
  193. package/dist/execution-topology.js +0 -95
  194. package/dist/gate-evidence.d.ts +0 -85
  195. package/dist/gate-evidence.js +0 -631
  196. package/dist/harness-adapters.d.ts +0 -151
  197. package/dist/harness-adapters.js +0 -756
  198. package/dist/harness-selection.d.ts +0 -31
  199. package/dist/harness-selection.js +0 -214
  200. package/dist/hook-schema.d.ts +0 -6
  201. package/dist/hook-schema.js +0 -114
  202. package/dist/hook-schemas/claude-hooks.v1.json +0 -10
  203. package/dist/hook-schemas/codex-hooks.v1.json +0 -10
  204. package/dist/hook-schemas/cursor-hooks.v1.json +0 -13
  205. package/dist/init-detect.d.ts +0 -2
  206. package/dist/init-detect.js +0 -50
  207. package/dist/internal/advance-stage/advance.d.ts +0 -89
  208. package/dist/internal/advance-stage/advance.js +0 -655
  209. package/dist/internal/advance-stage/cancel-run.d.ts +0 -8
  210. package/dist/internal/advance-stage/cancel-run.js +0 -19
  211. package/dist/internal/advance-stage/flow-state-coercion.d.ts +0 -3
  212. package/dist/internal/advance-stage/flow-state-coercion.js +0 -81
  213. package/dist/internal/advance-stage/helpers.d.ts +0 -14
  214. package/dist/internal/advance-stage/helpers.js +0 -145
  215. package/dist/internal/advance-stage/hook.d.ts +0 -8
  216. package/dist/internal/advance-stage/hook.js +0 -40
  217. package/dist/internal/advance-stage/parsers.d.ts +0 -72
  218. package/dist/internal/advance-stage/parsers.js +0 -357
  219. package/dist/internal/advance-stage/proactive-delegation-trace.d.ts +0 -24
  220. package/dist/internal/advance-stage/proactive-delegation-trace.js +0 -56
  221. package/dist/internal/advance-stage/review-loop.d.ts +0 -16
  222. package/dist/internal/advance-stage/review-loop.js +0 -199
  223. package/dist/internal/advance-stage/rewind.d.ts +0 -14
  224. package/dist/internal/advance-stage/rewind.js +0 -108
  225. package/dist/internal/advance-stage/start-flow.d.ts +0 -13
  226. package/dist/internal/advance-stage/start-flow.js +0 -241
  227. package/dist/internal/advance-stage/verify.d.ts +0 -21
  228. package/dist/internal/advance-stage/verify.js +0 -185
  229. package/dist/internal/advance-stage.d.ts +0 -7
  230. package/dist/internal/advance-stage.js +0 -138
  231. package/dist/internal/cohesion-contract-stub.d.ts +0 -24
  232. package/dist/internal/cohesion-contract-stub.js +0 -148
  233. package/dist/internal/compound-readiness.d.ts +0 -23
  234. package/dist/internal/compound-readiness.js +0 -102
  235. package/dist/internal/detect-public-api-changes.d.ts +0 -5
  236. package/dist/internal/detect-public-api-changes.js +0 -45
  237. package/dist/internal/detect-supply-chain-changes.d.ts +0 -6
  238. package/dist/internal/detect-supply-chain-changes.js +0 -138
  239. package/dist/internal/early-loop-status.d.ts +0 -7
  240. package/dist/internal/early-loop-status.js +0 -93
  241. package/dist/internal/envelope-validate.d.ts +0 -7
  242. package/dist/internal/envelope-validate.js +0 -66
  243. package/dist/internal/flow-state-repair.d.ts +0 -20
  244. package/dist/internal/flow-state-repair.js +0 -104
  245. package/dist/internal/plan-split-waves.d.ts +0 -190
  246. package/dist/internal/plan-split-waves.js +0 -764
  247. package/dist/internal/runtime-integrity.d.ts +0 -7
  248. package/dist/internal/runtime-integrity.js +0 -268
  249. package/dist/internal/slice-commit.d.ts +0 -7
  250. package/dist/internal/slice-commit.js +0 -619
  251. package/dist/internal/tdd-loop-status.d.ts +0 -14
  252. package/dist/internal/tdd-loop-status.js +0 -68
  253. package/dist/internal/tdd-red-evidence.d.ts +0 -7
  254. package/dist/internal/tdd-red-evidence.js +0 -153
  255. package/dist/internal/waiver-grant.d.ts +0 -62
  256. package/dist/internal/waiver-grant.js +0 -294
  257. package/dist/internal/wave-status.d.ts +0 -74
  258. package/dist/internal/wave-status.js +0 -506
  259. package/dist/managed-resources.d.ts +0 -53
  260. package/dist/managed-resources.js +0 -313
  261. package/dist/policy.d.ts +0 -10
  262. package/dist/policy.js +0 -167
  263. package/dist/retro-gate.d.ts +0 -9
  264. package/dist/retro-gate.js +0 -47
  265. package/dist/run-archive.d.ts +0 -61
  266. package/dist/run-archive.js +0 -391
  267. package/dist/runs.d.ts +0 -2
  268. package/dist/runs.js +0 -2
  269. package/dist/stack-detection.d.ts +0 -116
  270. package/dist/stack-detection.js +0 -489
  271. package/dist/streaming/event-stream.d.ts +0 -31
  272. package/dist/streaming/event-stream.js +0 -114
  273. package/dist/tdd-cycle.d.ts +0 -107
  274. package/dist/tdd-cycle.js +0 -289
  275. package/dist/tdd-verification-evidence.d.ts +0 -17
  276. package/dist/tdd-verification-evidence.js +0 -122
  277. package/dist/track-heuristics.d.ts +0 -27
  278. package/dist/track-heuristics.js +0 -154
  279. package/dist/util/slice-id.d.ts +0 -58
  280. package/dist/util/slice-id.js +0 -89
  281. package/dist/worktree-manager.d.ts +0 -20
  282. 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
- }