@paths.design/caws-cli 10.2.0 → 11.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 (493) hide show
  1. package/README.md +125 -374
  2. package/dist/index.js +45 -787
  3. package/dist/init/harness-detect.d.ts +18 -0
  4. package/dist/init/harness-detect.d.ts.map +1 -0
  5. package/dist/init/harness-detect.js +90 -0
  6. package/dist/init/harness-detect.js.map +1 -0
  7. package/dist/init/hook-install.d.ts +53 -0
  8. package/dist/init/hook-install.d.ts.map +1 -0
  9. package/dist/init/hook-install.js +421 -0
  10. package/dist/init/hook-install.js.map +1 -0
  11. package/dist/init/hook-packs/manifest-claude-code.d.ts +4 -0
  12. package/dist/init/hook-packs/manifest-claude-code.d.ts.map +1 -0
  13. package/dist/init/hook-packs/manifest-claude-code.js +190 -0
  14. package/dist/init/hook-packs/manifest-claude-code.js.map +1 -0
  15. package/dist/init/hook-packs/register.d.ts +19 -0
  16. package/dist/init/hook-packs/register.d.ts.map +1 -0
  17. package/dist/init/hook-packs/register.js +37 -0
  18. package/dist/init/hook-packs/register.js.map +1 -0
  19. package/dist/init/hook-packs/types.d.ts +123 -0
  20. package/dist/init/hook-packs/types.d.ts.map +1 -0
  21. package/dist/init/hook-packs/types.js +29 -0
  22. package/dist/init/hook-packs/types.js.map +1 -0
  23. package/dist/shell/binding/resolve-binding.d.ts +4 -0
  24. package/dist/shell/binding/resolve-binding.d.ts.map +1 -0
  25. package/dist/shell/binding/resolve-binding.js +228 -0
  26. package/dist/shell/binding/resolve-binding.js.map +1 -0
  27. package/dist/shell/binding/types.d.ts +42 -0
  28. package/dist/shell/binding/types.d.ts.map +1 -0
  29. package/dist/shell/binding/types.js +21 -0
  30. package/dist/shell/binding/types.js.map +1 -0
  31. package/dist/shell/commands/claim.d.ts +14 -0
  32. package/dist/shell/commands/claim.d.ts.map +1 -0
  33. package/dist/shell/commands/claim.js +197 -0
  34. package/dist/shell/commands/claim.js.map +1 -0
  35. package/dist/shell/commands/doctor.d.ts +13 -0
  36. package/dist/shell/commands/doctor.d.ts.map +1 -0
  37. package/dist/shell/commands/doctor.js +97 -0
  38. package/dist/shell/commands/doctor.js.map +1 -0
  39. package/dist/shell/commands/evidence.d.ts +28 -0
  40. package/dist/shell/commands/evidence.d.ts.map +1 -0
  41. package/dist/shell/commands/evidence.js +166 -0
  42. package/dist/shell/commands/evidence.js.map +1 -0
  43. package/dist/shell/commands/gates.d.ts +19 -0
  44. package/dist/shell/commands/gates.d.ts.map +1 -0
  45. package/dist/shell/commands/gates.js +208 -0
  46. package/dist/shell/commands/gates.js.map +1 -0
  47. package/dist/shell/commands/init.d.ts +17 -0
  48. package/dist/shell/commands/init.d.ts.map +1 -0
  49. package/dist/shell/commands/init.js +168 -0
  50. package/dist/shell/commands/init.js.map +1 -0
  51. package/dist/shell/commands/scope.d.ts +11 -0
  52. package/dist/shell/commands/scope.d.ts.map +1 -0
  53. package/dist/shell/commands/scope.js +92 -0
  54. package/dist/shell/commands/scope.js.map +1 -0
  55. package/dist/shell/commands/specs.d.ts +41 -0
  56. package/dist/shell/commands/specs.d.ts.map +1 -0
  57. package/dist/shell/commands/specs.js +264 -0
  58. package/dist/shell/commands/specs.js.map +1 -0
  59. package/dist/shell/commands/status.d.ts +15 -0
  60. package/dist/shell/commands/status.d.ts.map +1 -0
  61. package/dist/shell/commands/status.js +106 -0
  62. package/dist/shell/commands/status.js.map +1 -0
  63. package/dist/shell/commands/waiver.d.ts +38 -0
  64. package/dist/shell/commands/waiver.d.ts.map +1 -0
  65. package/dist/shell/commands/waiver.js +240 -0
  66. package/dist/shell/commands/waiver.js.map +1 -0
  67. package/dist/shell/commands/worktree.d.ts +38 -0
  68. package/dist/shell/commands/worktree.d.ts.map +1 -0
  69. package/dist/shell/commands/worktree.js +286 -0
  70. package/dist/shell/commands/worktree.js.map +1 -0
  71. package/dist/shell/gates/disposition.d.ts +23 -0
  72. package/dist/shell/gates/disposition.d.ts.map +1 -0
  73. package/dist/shell/gates/disposition.js +117 -0
  74. package/dist/shell/gates/disposition.js.map +1 -0
  75. package/dist/shell/gates/gate-result-contract.d.ts +39 -0
  76. package/dist/shell/gates/gate-result-contract.d.ts.map +1 -0
  77. package/dist/shell/gates/gate-result-contract.js +150 -0
  78. package/dist/shell/gates/gate-result-contract.js.map +1 -0
  79. package/dist/shell/gates/local-evaluators/budget-limit.d.ts +24 -0
  80. package/dist/shell/gates/local-evaluators/budget-limit.d.ts.map +1 -0
  81. package/dist/shell/gates/local-evaluators/budget-limit.js +67 -0
  82. package/dist/shell/gates/local-evaluators/budget-limit.js.map +1 -0
  83. package/dist/shell/gates/local-evaluators/diff-helpers.d.ts +25 -0
  84. package/dist/shell/gates/local-evaluators/diff-helpers.d.ts.map +1 -0
  85. package/dist/shell/gates/local-evaluators/diff-helpers.js +74 -0
  86. package/dist/shell/gates/local-evaluators/diff-helpers.js.map +1 -0
  87. package/dist/shell/gates/local-evaluators/index.d.ts +28 -0
  88. package/dist/shell/gates/local-evaluators/index.d.ts.map +1 -0
  89. package/dist/shell/gates/local-evaluators/index.js +67 -0
  90. package/dist/shell/gates/local-evaluators/index.js.map +1 -0
  91. package/dist/shell/gates/local-evaluators/scope-boundary.d.ts +23 -0
  92. package/dist/shell/gates/local-evaluators/scope-boundary.d.ts.map +1 -0
  93. package/dist/shell/gates/local-evaluators/scope-boundary.js +67 -0
  94. package/dist/shell/gates/local-evaluators/scope-boundary.js.map +1 -0
  95. package/dist/shell/gates/local-evaluators/spec-completeness.d.ts +12 -0
  96. package/dist/shell/gates/local-evaluators/spec-completeness.d.ts.map +1 -0
  97. package/dist/shell/gates/local-evaluators/spec-completeness.js +73 -0
  98. package/dist/shell/gates/local-evaluators/spec-completeness.js.map +1 -0
  99. package/dist/shell/gates/quality-gates-adapter.d.ts +55 -0
  100. package/dist/shell/gates/quality-gates-adapter.d.ts.map +1 -0
  101. package/dist/shell/gates/quality-gates-adapter.js +161 -0
  102. package/dist/shell/gates/quality-gates-adapter.js.map +1 -0
  103. package/dist/shell/gates/waiver-filter.d.ts +58 -0
  104. package/dist/shell/gates/waiver-filter.d.ts.map +1 -0
  105. package/dist/shell/gates/waiver-filter.js +119 -0
  106. package/dist/shell/gates/waiver-filter.js.map +1 -0
  107. package/dist/shell/index.d.ts +54 -0
  108. package/dist/shell/index.d.ts.map +1 -0
  109. package/dist/shell/index.js +85 -0
  110. package/dist/shell/index.js.map +1 -0
  111. package/dist/shell/register.d.ts +11 -0
  112. package/dist/shell/register.d.ts.map +1 -0
  113. package/dist/shell/register.js +464 -0
  114. package/dist/shell/register.js.map +1 -0
  115. package/dist/shell/render/claim.d.ts +22 -0
  116. package/dist/shell/render/claim.d.ts.map +1 -0
  117. package/dist/shell/render/claim.js +75 -0
  118. package/dist/shell/render/claim.js.map +1 -0
  119. package/dist/shell/render/decision.d.ts +15 -0
  120. package/dist/shell/render/decision.d.ts.map +1 -0
  121. package/dist/shell/render/decision.js +66 -0
  122. package/dist/shell/render/decision.js.map +1 -0
  123. package/dist/shell/render/diagnostic.d.ts +19 -0
  124. package/dist/shell/render/diagnostic.d.ts.map +1 -0
  125. package/dist/shell/render/diagnostic.js +76 -0
  126. package/dist/shell/render/diagnostic.js.map +1 -0
  127. package/dist/shell/render/finding.d.ts +15 -0
  128. package/dist/shell/render/finding.d.ts.map +1 -0
  129. package/dist/shell/render/finding.js +57 -0
  130. package/dist/shell/render/finding.js.map +1 -0
  131. package/dist/shell/render/gates.d.ts +3 -0
  132. package/dist/shell/render/gates.d.ts.map +1 -0
  133. package/dist/shell/render/gates.js +56 -0
  134. package/dist/shell/render/gates.js.map +1 -0
  135. package/dist/shell/render/init-hook-pack.d.ts +16 -0
  136. package/dist/shell/render/init-hook-pack.d.ts.map +1 -0
  137. package/dist/shell/render/init-hook-pack.js +206 -0
  138. package/dist/shell/render/init-hook-pack.js.map +1 -0
  139. package/dist/shell/render/init.d.ts +11 -0
  140. package/dist/shell/render/init.d.ts.map +1 -0
  141. package/dist/shell/render/init.js +32 -0
  142. package/dist/shell/render/init.js.map +1 -0
  143. package/dist/shell/render/status.d.ts +26 -0
  144. package/dist/shell/render/status.d.ts.map +1 -0
  145. package/dist/shell/render/status.js +143 -0
  146. package/dist/shell/render/status.js.map +1 -0
  147. package/dist/shell/render/waiver.d.ts +21 -0
  148. package/dist/shell/render/waiver.d.ts.map +1 -0
  149. package/dist/shell/render/waiver.js +94 -0
  150. package/dist/shell/render/waiver.js.map +1 -0
  151. package/dist/shell/rules.d.ts +37 -0
  152. package/dist/shell/rules.d.ts.map +1 -0
  153. package/dist/shell/rules.js +51 -0
  154. package/dist/shell/rules.js.map +1 -0
  155. package/dist/shell/session/actor.d.ts +14 -0
  156. package/dist/shell/session/actor.d.ts.map +1 -0
  157. package/dist/shell/session/actor.js +34 -0
  158. package/dist/shell/session/actor.js.map +1 -0
  159. package/dist/shell/session/resolve-session.d.ts +5 -0
  160. package/dist/shell/session/resolve-session.d.ts.map +1 -0
  161. package/dist/shell/session/resolve-session.js +239 -0
  162. package/dist/shell/session/resolve-session.js.map +1 -0
  163. package/dist/shell/session/types.d.ts +56 -0
  164. package/dist/shell/session/types.d.ts.map +1 -0
  165. package/dist/shell/session/types.js +15 -0
  166. package/dist/shell/session/types.js.map +1 -0
  167. package/dist/store/agents-store.d.ts +3 -0
  168. package/dist/store/agents-store.d.ts.map +1 -0
  169. package/dist/store/agents-store.js +63 -0
  170. package/dist/store/agents-store.js.map +1 -0
  171. package/dist/store/apply-patch.d.ts +16 -0
  172. package/dist/store/apply-patch.d.ts.map +1 -0
  173. package/dist/store/apply-patch.js +191 -0
  174. package/dist/store/apply-patch.js.map +1 -0
  175. package/dist/store/atomic-write.d.ts +34 -0
  176. package/dist/store/atomic-write.d.ts.map +1 -0
  177. package/dist/store/atomic-write.js +174 -0
  178. package/dist/store/atomic-write.js.map +1 -0
  179. package/dist/store/doctor-snapshot.d.ts +20 -0
  180. package/dist/store/doctor-snapshot.d.ts.map +1 -0
  181. package/dist/store/doctor-snapshot.js +176 -0
  182. package/dist/store/doctor-snapshot.js.map +1 -0
  183. package/dist/store/events-store.d.ts +33 -0
  184. package/dist/store/events-store.d.ts.map +1 -0
  185. package/dist/store/events-store.js +297 -0
  186. package/dist/store/events-store.js.map +1 -0
  187. package/dist/store/index.d.ts +21 -0
  188. package/dist/store/index.d.ts.map +1 -0
  189. package/dist/store/index.js +47 -0
  190. package/dist/store/index.js.map +1 -0
  191. package/dist/store/init-store.d.ts +21 -0
  192. package/dist/store/init-store.d.ts.map +1 -0
  193. package/dist/store/init-store.js +295 -0
  194. package/dist/store/init-store.js.map +1 -0
  195. package/dist/store/json-store.d.ts +3 -0
  196. package/dist/store/json-store.d.ts.map +1 -0
  197. package/dist/store/json-store.js +65 -0
  198. package/dist/store/json-store.js.map +1 -0
  199. package/dist/store/lifecycle-lock.d.ts +34 -0
  200. package/dist/store/lifecycle-lock.d.ts.map +1 -0
  201. package/dist/store/lifecycle-lock.js +168 -0
  202. package/dist/store/lifecycle-lock.js.map +1 -0
  203. package/dist/store/lifecycle-transaction.d.ts +79 -0
  204. package/dist/store/lifecycle-transaction.d.ts.map +1 -0
  205. package/dist/store/lifecycle-transaction.js +319 -0
  206. package/dist/store/lifecycle-transaction.js.map +1 -0
  207. package/dist/store/policy-store.d.ts +3 -0
  208. package/dist/store/policy-store.d.ts.map +1 -0
  209. package/dist/store/policy-store.js +65 -0
  210. package/dist/store/policy-store.js.map +1 -0
  211. package/dist/store/repo-root.d.ts +46 -0
  212. package/dist/store/repo-root.d.ts.map +1 -0
  213. package/dist/store/repo-root.js +145 -0
  214. package/dist/store/repo-root.js.map +1 -0
  215. package/dist/store/rules.d.ts +69 -0
  216. package/dist/store/rules.d.ts.map +1 -0
  217. package/dist/store/rules.js +95 -0
  218. package/dist/store/rules.js.map +1 -0
  219. package/dist/store/specs-store.d.ts +3 -0
  220. package/dist/store/specs-store.d.ts.map +1 -0
  221. package/dist/store/specs-store.js +131 -0
  222. package/dist/store/specs-store.js.map +1 -0
  223. package/dist/store/specs-writer.d.ts +61 -0
  224. package/dist/store/specs-writer.d.ts.map +1 -0
  225. package/dist/store/specs-writer.js +506 -0
  226. package/dist/store/specs-writer.js.map +1 -0
  227. package/dist/store/types.d.ts +84 -0
  228. package/dist/store/types.d.ts.map +1 -0
  229. package/dist/store/types.js +14 -0
  230. package/dist/store/types.js.map +1 -0
  231. package/dist/store/waivers-store.d.ts +25 -0
  232. package/dist/store/waivers-store.d.ts.map +1 -0
  233. package/dist/store/waivers-store.js +232 -0
  234. package/dist/store/waivers-store.js.map +1 -0
  235. package/dist/store/worktrees-store.d.ts +3 -0
  236. package/dist/store/worktrees-store.d.ts.map +1 -0
  237. package/dist/store/worktrees-store.js +62 -0
  238. package/dist/store/worktrees-store.js.map +1 -0
  239. package/dist/store/worktrees-writer.d.ts +77 -0
  240. package/dist/store/worktrees-writer.d.ts.map +1 -0
  241. package/dist/store/worktrees-writer.js +674 -0
  242. package/dist/store/worktrees-writer.js.map +1 -0
  243. package/dist/store/yaml-patch.d.ts +7 -0
  244. package/dist/store/yaml-patch.d.ts.map +1 -0
  245. package/dist/store/yaml-patch.js +250 -0
  246. package/dist/store/yaml-patch.js.map +1 -0
  247. package/dist/store/yaml-store.d.ts +9 -0
  248. package/dist/store/yaml-store.d.ts.map +1 -0
  249. package/dist/store/yaml-store.js +121 -0
  250. package/dist/store/yaml-store.js.map +1 -0
  251. package/package.json +15 -13
  252. package/dist/budget-derivation.js +0 -751
  253. package/dist/cicd-optimizer.js +0 -504
  254. package/dist/commands/agents.js +0 -124
  255. package/dist/commands/archive.js +0 -500
  256. package/dist/commands/burnup.js +0 -198
  257. package/dist/commands/diagnose.js +0 -525
  258. package/dist/commands/evaluate.js +0 -314
  259. package/dist/commands/gates.js +0 -149
  260. package/dist/commands/init.js +0 -857
  261. package/dist/commands/iterate.js +0 -417
  262. package/dist/commands/mode.js +0 -269
  263. package/dist/commands/parallel.js +0 -242
  264. package/dist/commands/plan.js +0 -438
  265. package/dist/commands/provenance.js +0 -1143
  266. package/dist/commands/quality-monitor.js +0 -284
  267. package/dist/commands/scope.js +0 -264
  268. package/dist/commands/session.js +0 -312
  269. package/dist/commands/sidecar.js +0 -74
  270. package/dist/commands/specs.js +0 -1656
  271. package/dist/commands/status.js +0 -1172
  272. package/dist/commands/templates.js +0 -237
  273. package/dist/commands/tool.js +0 -136
  274. package/dist/commands/tutorial.js +0 -480
  275. package/dist/commands/validate.js +0 -357
  276. package/dist/commands/verify-acs.js +0 -443
  277. package/dist/commands/waivers.js +0 -599
  278. package/dist/commands/workflow.js +0 -243
  279. package/dist/commands/worktree.js +0 -502
  280. package/dist/config/lite-scope.js +0 -158
  281. package/dist/config/modes.js +0 -347
  282. package/dist/constants/spec-types.js +0 -65
  283. package/dist/gates/budget-limit.js +0 -121
  284. package/dist/gates/feedback.js +0 -260
  285. package/dist/gates/format.js +0 -179
  286. package/dist/gates/god-object.js +0 -117
  287. package/dist/gates/pipeline.js +0 -167
  288. package/dist/gates/scope-boundary.js +0 -112
  289. package/dist/gates/spec-completeness.js +0 -109
  290. package/dist/gates/todo-detection.js +0 -205
  291. package/dist/generators/jest-config-generator.js +0 -242
  292. package/dist/generators/working-spec.js +0 -237
  293. package/dist/minimal-cli.js +0 -88
  294. package/dist/parallel/parallel-manager.js +0 -433
  295. package/dist/policy/PolicyManager.js +0 -470
  296. package/dist/scaffold/claude-hooks.js +0 -443
  297. package/dist/scaffold/cursor-hooks.js +0 -177
  298. package/dist/scaffold/git-hooks.js +0 -928
  299. package/dist/scaffold/index.js +0 -794
  300. package/dist/session/session-manager.js +0 -653
  301. package/dist/sidecars/index.js +0 -33
  302. package/dist/sidecars/listeners.js +0 -40
  303. package/dist/sidecars/provenance-summary.js +0 -238
  304. package/dist/sidecars/quality-gaps.js +0 -258
  305. package/dist/sidecars/schema.js +0 -149
  306. package/dist/sidecars/spec-drift.js +0 -151
  307. package/dist/sidecars/waiver-draft.js +0 -176
  308. package/dist/spec/SpecFileManager.js +0 -419
  309. package/dist/templates/.caws/schemas/policy.schema.json +0 -117
  310. package/dist/templates/.caws/schemas/scope.schema.json +0 -52
  311. package/dist/templates/.caws/schemas/waivers.schema.json +0 -106
  312. package/dist/templates/.caws/schemas/working-spec.schema.json +0 -340
  313. package/dist/templates/.caws/schemas/worktrees.schema.json +0 -38
  314. package/dist/templates/.caws/templates/working-spec.template.yml +0 -80
  315. package/dist/templates/.caws/tools/README.md +0 -18
  316. package/dist/templates/.caws/tools/scope-guard.js +0 -203
  317. package/dist/templates/.caws/tools-allow.json +0 -331
  318. package/dist/templates/.caws/waivers.yml +0 -19
  319. package/dist/templates/.claude/README.md +0 -190
  320. package/dist/templates/.claude/hooks/audit.sh +0 -121
  321. package/dist/templates/.claude/hooks/block-dangerous.sh +0 -203
  322. package/dist/templates/.claude/hooks/classify_command.py +0 -592
  323. package/dist/templates/.claude/hooks/doc-frontmatter-check.sh +0 -173
  324. package/dist/templates/.claude/hooks/lite-sprawl-check.sh +0 -145
  325. package/dist/templates/.claude/hooks/naming-check.sh +0 -100
  326. package/dist/templates/.claude/hooks/protected-paths.sh +0 -39
  327. package/dist/templates/.claude/hooks/quality-check.sh +0 -81
  328. package/dist/templates/.claude/hooks/scan-secrets.sh +0 -85
  329. package/dist/templates/.claude/hooks/scope-guard.sh +0 -381
  330. package/dist/templates/.claude/hooks/session-caws-status.sh +0 -117
  331. package/dist/templates/.claude/hooks/session-log.sh +0 -634
  332. package/dist/templates/.claude/hooks/simplification-guard.sh +0 -92
  333. package/dist/templates/.claude/hooks/stop-worktree-check.sh +0 -46
  334. package/dist/templates/.claude/hooks/test_classify_command.py +0 -370
  335. package/dist/templates/.claude/hooks/test_wrapper_smoke.sh +0 -96
  336. package/dist/templates/.claude/hooks/validate-spec.sh +0 -76
  337. package/dist/templates/.claude/hooks/worktree-guard.sh +0 -220
  338. package/dist/templates/.claude/hooks/worktree-write-guard.sh +0 -190
  339. package/dist/templates/.claude/rules/git-safety.md +0 -26
  340. package/dist/templates/.claude/rules/worktree-isolation.md +0 -101
  341. package/dist/templates/.claude/settings.json +0 -141
  342. package/dist/templates/.cursor/README.md +0 -299
  343. package/dist/templates/.cursor/hooks/audit.sh +0 -55
  344. package/dist/templates/.cursor/hooks/block-dangerous.sh +0 -84
  345. package/dist/templates/.cursor/hooks/caws-quality-check.sh +0 -52
  346. package/dist/templates/.cursor/hooks/caws-scope-guard.sh +0 -130
  347. package/dist/templates/.cursor/hooks/format.sh +0 -38
  348. package/dist/templates/.cursor/hooks/naming-check.sh +0 -64
  349. package/dist/templates/.cursor/hooks/scan-secrets.sh +0 -51
  350. package/dist/templates/.cursor/hooks/scope-guard.sh +0 -52
  351. package/dist/templates/.cursor/hooks/session-log.sh +0 -924
  352. package/dist/templates/.cursor/hooks/validate-spec.sh +0 -83
  353. package/dist/templates/.cursor/hooks.json +0 -76
  354. package/dist/templates/.cursor/rules/00-claims-verification.mdc +0 -144
  355. package/dist/templates/.cursor/rules/01-working-style.mdc +0 -50
  356. package/dist/templates/.cursor/rules/02-quality-gates.mdc +0 -368
  357. package/dist/templates/.cursor/rules/03-naming-and-refactor.mdc +0 -33
  358. package/dist/templates/.cursor/rules/04-logging-language-style.mdc +0 -23
  359. package/dist/templates/.cursor/rules/05-safe-defaults-guards.mdc +0 -23
  360. package/dist/templates/.cursor/rules/06-typescript-conventions.mdc +0 -36
  361. package/dist/templates/.cursor/rules/07-process-ops.mdc +0 -20
  362. package/dist/templates/.cursor/rules/08-solid-and-architecture.mdc +0 -16
  363. package/dist/templates/.cursor/rules/09-docstrings.mdc +0 -89
  364. package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +0 -385
  365. package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +0 -381
  366. package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +0 -516
  367. package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +0 -578
  368. package/dist/templates/.cursor/rules/README.md +0 -148
  369. package/dist/templates/.github/copilot-instructions.md +0 -82
  370. package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +0 -5
  371. package/dist/templates/.idea/runConfigurations/CAWS_Validate.xml +0 -5
  372. package/dist/templates/.junie/guidelines.md +0 -73
  373. package/dist/templates/.vscode/launch.json +0 -17
  374. package/dist/templates/.vscode/settings.json +0 -95
  375. package/dist/templates/.windsurf/rules/caws-quality-standards.md +0 -54
  376. package/dist/templates/.windsurf/workflows/caws-guided-development.md +0 -92
  377. package/dist/templates/CLAUDE.md +0 -196
  378. package/dist/templates/COMMIT_CONVENTIONS.md +0 -86
  379. package/dist/templates/OIDC_SETUP.md +0 -300
  380. package/dist/templates/agents.md +0 -171
  381. package/dist/templates/codemod/README.md +0 -1
  382. package/dist/templates/codemod/test.js +0 -93
  383. package/dist/templates/docs/README.md +0 -151
  384. package/dist/templates/scripts/new_feature.sh +0 -80
  385. package/dist/templates/scripts/quality-gates/check-god-objects.js +0 -146
  386. package/dist/templates/scripts/quality-gates/run-quality-gates.js +0 -50
  387. package/dist/templates/scripts/v3/analysis/todo_analyzer.py +0 -1997
  388. package/dist/test-analysis.js +0 -786
  389. package/dist/tool-interface.js +0 -314
  390. package/dist/tool-loader.js +0 -303
  391. package/dist/tool-validator.js +0 -393
  392. package/dist/utils/agent-display.js +0 -210
  393. package/dist/utils/agent-session.js +0 -344
  394. package/dist/utils/async-utils.js +0 -188
  395. package/dist/utils/command-wrapper.js +0 -200
  396. package/dist/utils/event-log.js +0 -584
  397. package/dist/utils/event-renderer.js +0 -521
  398. package/dist/utils/finalization.js +0 -230
  399. package/dist/utils/git-lock.js +0 -119
  400. package/dist/utils/gitignore-updater.js +0 -158
  401. package/dist/utils/ide-detection.js +0 -133
  402. package/dist/utils/lifecycle-events.js +0 -94
  403. package/dist/utils/project-analysis.js +0 -367
  404. package/dist/utils/promise-utils.js +0 -72
  405. package/dist/utils/quality-gates-errors.js +0 -520
  406. package/dist/utils/quality-gates-utils.js +0 -387
  407. package/dist/utils/schema-validator.js +0 -50
  408. package/dist/utils/spec-resolver.js +0 -711
  409. package/dist/utils/typescript-detector.js +0 -369
  410. package/dist/utils/working-state.js +0 -530
  411. package/dist/utils/yaml-validation.js +0 -156
  412. package/dist/validation/spec-validation.js +0 -924
  413. package/dist/waivers-manager.js +0 -732
  414. package/dist/worktree/worktree-manager.js +0 -1735
  415. package/templates/.caws/schemas/policy.schema.json +0 -117
  416. package/templates/.caws/schemas/scope.schema.json +0 -52
  417. package/templates/.caws/schemas/waivers.schema.json +0 -106
  418. package/templates/.caws/schemas/working-spec.schema.json +0 -340
  419. package/templates/.caws/schemas/worktrees.schema.json +0 -38
  420. package/templates/.caws/templates/working-spec.template.yml +0 -80
  421. package/templates/.caws/tools/README.md +0 -18
  422. package/templates/.caws/tools/scope-guard.js +0 -203
  423. package/templates/.caws/tools-allow.json +0 -331
  424. package/templates/.caws/waivers.yml +0 -19
  425. package/templates/.claude/README.md +0 -190
  426. package/templates/.claude/hooks/audit.sh +0 -121
  427. package/templates/.claude/hooks/block-dangerous.sh +0 -203
  428. package/templates/.claude/hooks/classify_command.py +0 -592
  429. package/templates/.claude/hooks/doc-frontmatter-check.sh +0 -173
  430. package/templates/.claude/hooks/lite-sprawl-check.sh +0 -145
  431. package/templates/.claude/hooks/naming-check.sh +0 -100
  432. package/templates/.claude/hooks/protected-paths.sh +0 -39
  433. package/templates/.claude/hooks/quality-check.sh +0 -81
  434. package/templates/.claude/hooks/scan-secrets.sh +0 -85
  435. package/templates/.claude/hooks/scope-guard.sh +0 -381
  436. package/templates/.claude/hooks/session-caws-status.sh +0 -117
  437. package/templates/.claude/hooks/session-log.sh +0 -634
  438. package/templates/.claude/hooks/simplification-guard.sh +0 -92
  439. package/templates/.claude/hooks/stop-worktree-check.sh +0 -46
  440. package/templates/.claude/hooks/test_classify_command.py +0 -370
  441. package/templates/.claude/hooks/test_wrapper_smoke.sh +0 -96
  442. package/templates/.claude/hooks/validate-spec.sh +0 -76
  443. package/templates/.claude/hooks/worktree-guard.sh +0 -220
  444. package/templates/.claude/hooks/worktree-write-guard.sh +0 -190
  445. package/templates/.claude/rules/git-safety.md +0 -26
  446. package/templates/.claude/rules/worktree-isolation.md +0 -101
  447. package/templates/.claude/settings.json +0 -141
  448. package/templates/.cursor/README.md +0 -299
  449. package/templates/.cursor/hooks/audit.sh +0 -55
  450. package/templates/.cursor/hooks/block-dangerous.sh +0 -84
  451. package/templates/.cursor/hooks/caws-quality-check.sh +0 -52
  452. package/templates/.cursor/hooks/caws-scope-guard.sh +0 -130
  453. package/templates/.cursor/hooks/format.sh +0 -38
  454. package/templates/.cursor/hooks/naming-check.sh +0 -64
  455. package/templates/.cursor/hooks/scan-secrets.sh +0 -51
  456. package/templates/.cursor/hooks/scope-guard.sh +0 -52
  457. package/templates/.cursor/hooks/session-log.sh +0 -924
  458. package/templates/.cursor/hooks/validate-spec.sh +0 -83
  459. package/templates/.cursor/hooks.json +0 -76
  460. package/templates/.cursor/rules/00-claims-verification.mdc +0 -144
  461. package/templates/.cursor/rules/01-working-style.mdc +0 -50
  462. package/templates/.cursor/rules/02-quality-gates.mdc +0 -368
  463. package/templates/.cursor/rules/03-naming-and-refactor.mdc +0 -33
  464. package/templates/.cursor/rules/04-logging-language-style.mdc +0 -23
  465. package/templates/.cursor/rules/05-safe-defaults-guards.mdc +0 -23
  466. package/templates/.cursor/rules/06-typescript-conventions.mdc +0 -36
  467. package/templates/.cursor/rules/07-process-ops.mdc +0 -20
  468. package/templates/.cursor/rules/08-solid-and-architecture.mdc +0 -16
  469. package/templates/.cursor/rules/09-docstrings.mdc +0 -89
  470. package/templates/.cursor/rules/10-documentation-quality-standards.mdc +0 -385
  471. package/templates/.cursor/rules/11-scope-management-waivers.mdc +0 -381
  472. package/templates/.cursor/rules/12-implementation-completeness.mdc +0 -516
  473. package/templates/.cursor/rules/13-language-agnostic-standards.mdc +0 -578
  474. package/templates/.cursor/rules/README.md +0 -148
  475. package/templates/.github/copilot-instructions.md +0 -82
  476. package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +0 -5
  477. package/templates/.idea/runConfigurations/CAWS_Validate.xml +0 -5
  478. package/templates/.junie/guidelines.md +0 -73
  479. package/templates/.vscode/launch.json +0 -17
  480. package/templates/.vscode/settings.json +0 -95
  481. package/templates/.windsurf/rules/caws-quality-standards.md +0 -54
  482. package/templates/.windsurf/workflows/caws-guided-development.md +0 -92
  483. package/templates/CLAUDE.md +0 -196
  484. package/templates/COMMIT_CONVENTIONS.md +0 -86
  485. package/templates/OIDC_SETUP.md +0 -300
  486. package/templates/agents.md +0 -171
  487. package/templates/codemod/README.md +0 -1
  488. package/templates/codemod/test.js +0 -93
  489. package/templates/docs/README.md +0 -151
  490. package/templates/scripts/new_feature.sh +0 -80
  491. package/templates/scripts/quality-gates/check-god-objects.js +0 -146
  492. package/templates/scripts/quality-gates/run-quality-gates.js +0 -50
  493. package/templates/scripts/v3/analysis/todo_analyzer.py +0 -1997
@@ -1,584 +0,0 @@
1
- /**
2
- * @fileoverview Event Log — append-only provenance surface
3
- *
4
- * CAWS events are written once to `.caws/events.jsonl` and never rewritten.
5
- * Every other view (per-spec state, session registry, provenance chain) is
6
- * a pure function of this log. See docs/internal/EVENTS_LOG_MIGRATION.md
7
- * for the full design.
8
- *
9
- * Contract highlights:
10
- * - Append-only. Readers tolerate partial last lines.
11
- * - Hash-chained. Each event carries prev_hash and event_hash (sha256).
12
- * - Fail-loud. Events missing a required spec_id throw; no silent writes.
13
- * - Cross-platform file lock via `fs.openSync(lockPath, 'wx')` sentinel.
14
- *
15
- * This file ships as Phase 1 (dual-write). State-layer writes in
16
- * working-state.js continue unchanged; this log is additive.
17
- *
18
- * @author @darianrosebrook
19
- */
20
-
21
- const fs = require('fs');
22
- const path = require('path');
23
- const crypto = require('crypto');
24
-
25
- // ---------------------------------------------------------------------------
26
- // Constants
27
- // ---------------------------------------------------------------------------
28
-
29
- const EVENTS_FILE = '.caws/events.jsonl';
30
- const LOCK_SUFFIX = '.lock';
31
- const HASH_DOMAIN = 'caws.events.v1';
32
- const LOCK_RETRY_MS = 20;
33
- const LOCK_RETRY_MAX = 50; // ~1s total
34
-
35
- /**
36
- * Events that require a spec_id in their payload. appendEvent throws if
37
- * spec_id is missing for any event listed here. This is the fence that
38
- * prevents the `.caws/state/undefined.json` bug class.
39
- */
40
- const REQUIRES_SPEC_ID = new Set([
41
- 'validation_completed',
42
- 'evaluation_completed',
43
- 'verify_acs_completed',
44
- 'gates_evaluated',
45
- 'spec_created',
46
- 'spec_updated',
47
- 'spec_closed',
48
- 'spec_archived',
49
- 'spec_deleted',
50
- 'spec_drift_detected',
51
- 'waiver_applied',
52
- ]);
53
-
54
- /**
55
- * Events that optionally carry a spec_id. These are allowed to omit it.
56
- * Any event not in REQUIRES_SPEC_ID and not in OPTIONAL_SPEC_ID is
57
- * treated as an unknown event type — appendEvent will still write it,
58
- * but it's recorded as-is without spec_id validation.
59
- */
60
- const OPTIONAL_SPEC_ID = new Set([
61
- 'session_started',
62
- 'session_ended',
63
- 'commit_made',
64
- 'branch_switched',
65
- 'worktree_created',
66
- 'worktree_merged',
67
- 'worktree_destroyed',
68
- ]);
69
-
70
- // ---------------------------------------------------------------------------
71
- // Helpers
72
- // ---------------------------------------------------------------------------
73
-
74
- /**
75
- * Resolve the absolute path to `.caws/events.jsonl` for a project root.
76
- * @param {string} projectRoot
77
- * @returns {string}
78
- */
79
- function getEventsPath(projectRoot) {
80
- return path.join(projectRoot, EVENTS_FILE);
81
- }
82
-
83
- /**
84
- * Canonicalize an event object for hashing. Keys are sorted alphabetically
85
- * and serialized with no whitespace, so two structurally-equivalent events
86
- * always produce the same hash regardless of key insertion order.
87
- *
88
- * This is a deliberate subset of RFC 8785 (JCS) — sufficient for our flat
89
- * event shape but not a full implementation.
90
- *
91
- * @param {object} obj
92
- * @returns {string}
93
- */
94
- function canonicalJson(obj) {
95
- if (obj === null) return 'null';
96
- if (typeof obj === 'number') {
97
- if (!Number.isFinite(obj)) {
98
- throw new Error(`canonicalJson: non-finite number ${obj}`);
99
- }
100
- return JSON.stringify(obj);
101
- }
102
- if (typeof obj === 'string') return JSON.stringify(obj);
103
- if (typeof obj === 'boolean') return obj ? 'true' : 'false';
104
- if (Array.isArray(obj)) {
105
- return '[' + obj.map(canonicalJson).join(',') + ']';
106
- }
107
- if (typeof obj === 'object') {
108
- const keys = Object.keys(obj).sort();
109
- const parts = keys.map((k) => JSON.stringify(k) + ':' + canonicalJson(obj[k]));
110
- return '{' + parts.join(',') + '}';
111
- }
112
- throw new Error(`canonicalJson: unsupported type ${typeof obj}`);
113
- }
114
-
115
- /**
116
- * Compute sha256 of the domain-separated canonical JSON of an event.
117
- * The `event_hash` field, if present on the input, is stripped before
118
- * hashing so the hash can be stored back on the event itself.
119
- *
120
- * @param {object} event
121
- * @returns {string} "sha256:<hex>"
122
- */
123
- function computeEventHash(event) {
124
- const withoutHash = { ...event };
125
- delete withoutHash.event_hash;
126
- const canonical = canonicalJson(withoutHash);
127
- const hash = crypto
128
- .createHash('sha256')
129
- .update(HASH_DOMAIN)
130
- .update('\x00')
131
- .update(canonical)
132
- .digest('hex');
133
- return `sha256:${hash}`;
134
- }
135
-
136
- /**
137
- * Sleep for a number of milliseconds. Used by the async lock retry loop.
138
- * @param {number} ms
139
- * @returns {Promise<void>}
140
- */
141
- function sleep(ms) {
142
- return new Promise((resolve) => { globalThis.setTimeout(resolve, ms); });
143
- }
144
-
145
- /**
146
- * Synchronously sleep for a number of milliseconds. Node has no built-in
147
- * sync sleep; `Atomics.wait` on a dummy Int32Array blocks the thread
148
- * without CPU burn and is the least-ugly cross-platform option.
149
- * Used by the sync lock retry loop for call sites that cannot await.
150
- * @param {number} ms
151
- */
152
- function sleepSync(ms) {
153
- const buf = new Int32Array(new SharedArrayBuffer(4));
154
- Atomics.wait(buf, 0, 0, ms);
155
- }
156
-
157
- /**
158
- * Acquire an exclusive lock on the events file by creating a sentinel
159
- * lockfile with the `wx` flag (fails atomically if the file already
160
- * exists). Retries with a short backoff.
161
- *
162
- * Returns an opaque handle that must be passed to releaseLock.
163
- *
164
- * @param {string} eventsPath
165
- * @returns {Promise<{lockPath: string, fd: number}>}
166
- */
167
- async function acquireLock(eventsPath) {
168
- const lockPath = eventsPath + LOCK_SUFFIX;
169
- const dir = path.dirname(eventsPath);
170
- if (!fs.existsSync(dir)) {
171
- fs.mkdirSync(dir, { recursive: true });
172
- }
173
-
174
- for (let attempt = 0; attempt < LOCK_RETRY_MAX; attempt++) {
175
- try {
176
- const fd = fs.openSync(lockPath, 'wx');
177
- // Write pid to the lock so stale locks are diagnosable.
178
- fs.writeSync(fd, String(process.pid));
179
- return { lockPath, fd };
180
- } catch (err) {
181
- if (err.code !== 'EEXIST') throw err;
182
- // Check for stale lock (>30s old) — clean it up so one crashed
183
- // writer doesn't block forever. This is still race-prone against
184
- // another writer that just grabbed it; we accept that risk because
185
- // the worst case is a rejected append, not corruption.
186
- try {
187
- const stat = fs.statSync(lockPath);
188
- if (Date.now() - stat.mtimeMs > 30_000) {
189
- fs.unlinkSync(lockPath);
190
- continue; // retry immediately without backoff
191
- }
192
- } catch {
193
- /* stat failed — file may have been released; retry */
194
- }
195
- await sleep(LOCK_RETRY_MS);
196
- }
197
- }
198
- throw new Error(
199
- `event-log: could not acquire lock on ${lockPath} after ${LOCK_RETRY_MAX * LOCK_RETRY_MS}ms — another writer may be stuck`
200
- );
201
- }
202
-
203
- /**
204
- * Synchronous lock acquirer. Same behavior as `acquireLock` but blocks
205
- * the thread via `sleepSync`. Intended for call sites that cannot await
206
- * (e.g. session-manager.startSession/endSession which are synchronous
207
- * for historical reasons).
208
- *
209
- * @param {string} eventsPath
210
- * @returns {{lockPath: string, fd: number}}
211
- */
212
- function acquireLockSync(eventsPath) {
213
- const lockPath = eventsPath + LOCK_SUFFIX;
214
- const dir = path.dirname(eventsPath);
215
- if (!fs.existsSync(dir)) {
216
- fs.mkdirSync(dir, { recursive: true });
217
- }
218
-
219
- for (let attempt = 0; attempt < LOCK_RETRY_MAX; attempt++) {
220
- try {
221
- const fd = fs.openSync(lockPath, 'wx');
222
- fs.writeSync(fd, String(process.pid));
223
- return { lockPath, fd };
224
- } catch (err) {
225
- if (err.code !== 'EEXIST') throw err;
226
- try {
227
- const stat = fs.statSync(lockPath);
228
- if (Date.now() - stat.mtimeMs > 30_000) {
229
- fs.unlinkSync(lockPath);
230
- continue;
231
- }
232
- } catch {
233
- /* stat failed — retry */
234
- }
235
- sleepSync(LOCK_RETRY_MS);
236
- }
237
- }
238
- throw new Error(
239
- `event-log: could not acquire lock on ${lockPath} after ${LOCK_RETRY_MAX * LOCK_RETRY_MS}ms — another writer may be stuck`
240
- );
241
- }
242
-
243
- /**
244
- * Release a lock acquired via acquireLock. Never throws; a failed release
245
- * is logged but not propagated, because the caller has already written.
246
- *
247
- * @param {{lockPath: string, fd: number}} handle
248
- */
249
- function releaseLock(handle) {
250
- try {
251
- fs.closeSync(handle.fd);
252
- } catch {
253
- /* close failure is non-fatal; the unlink below is the real release */
254
- }
255
- try {
256
- fs.unlinkSync(handle.lockPath);
257
- } catch (err) {
258
- if (err.code !== 'ENOENT') {
259
- // Surface unexpected release failures on stderr so they're diagnosable.
260
-
261
- console.error(`event-log: failed to release lock ${handle.lockPath}: ${err.message}`);
262
- }
263
- }
264
- }
265
-
266
- /**
267
- * Read the last non-empty line of a file without loading the whole file
268
- * into memory. Used to find the tail of the event log for seq/prev_hash
269
- * continuity.
270
- *
271
- * Returns `null` if the file does not exist or contains only whitespace.
272
- *
273
- * @param {string} filePath
274
- * @returns {string|null}
275
- */
276
- function readLastLine(filePath) {
277
- if (!fs.existsSync(filePath)) return null;
278
- const stat = fs.statSync(filePath);
279
- if (stat.size === 0) return null;
280
-
281
- // Read from the end in chunks until we have at least one complete line.
282
- const fd = fs.openSync(filePath, 'r');
283
- try {
284
- const chunkSize = 4096;
285
- let buffer = Buffer.alloc(0);
286
- let pos = stat.size;
287
- while (pos > 0) {
288
- const readSize = Math.min(chunkSize, pos);
289
- pos -= readSize;
290
- const chunk = Buffer.alloc(readSize);
291
- fs.readSync(fd, chunk, 0, readSize, pos);
292
- buffer = Buffer.concat([chunk, buffer]);
293
- const text = buffer.toString('utf8');
294
- const lines = text.split('\n').filter((l) => l.length > 0);
295
- if (lines.length >= 2 || pos === 0) {
296
- return lines[lines.length - 1] || null;
297
- }
298
- }
299
- return null;
300
- } finally {
301
- fs.closeSync(fd);
302
- }
303
- }
304
-
305
- // ---------------------------------------------------------------------------
306
- // Public API
307
- // ---------------------------------------------------------------------------
308
-
309
- /**
310
- * Append a single event to the project's event log.
311
- *
312
- * The event is stamped with a monotonic `seq`, an ISO-8601 `ts`, a
313
- * `prev_hash` linking it to the previous event, and an `event_hash`
314
- * computed over its canonical JSON (excluding the hash field itself).
315
- *
316
- * This function is **intentionally non-tolerant**:
317
- * - If `event` is an event type that requires `spec_id` and one is
318
- * missing, it throws. Do NOT wrap calls in `try { ... } catch {}`.
319
- * - If the lock cannot be acquired, it throws.
320
- * - If the last line of the file is malformed, it throws.
321
- *
322
- * Silent loss of provenance is the current failure mode of `.caws/state/`
323
- * and the whole point of this module is to reverse that default.
324
- *
325
- * @param {object} params
326
- * @param {string} params.actor — who emitted the event (cli, hook, session, agent, subagent-name)
327
- * @param {string} params.event — event type from the v0 vocabulary
328
- * @param {string} [params.spec_id] — required for spec-scoped events, optional otherwise
329
- * @param {object} [params.data] — event-type-specific payload
330
- * @param {object} [options]
331
- * @param {string} [options.projectRoot] — defaults to cwd
332
- * @param {string} [options.session_id] — session correlator; defaults to env CAWS_SESSION_ID or "standalone"
333
- * @returns {Promise<{seq: number, event_hash: string, prev_hash: string}>}
334
- */
335
- /**
336
- * Shared contract validation for both the async and sync append paths.
337
- * Throws on any violation. Returns a normalized descriptor the
338
- * file-writing helper consumes.
339
- *
340
- * @param {object} params
341
- * @param {object} options
342
- * @returns {{actor: string, event: string, spec_id: (string|undefined), data: (object|undefined), sessionId: string, eventsPath: string}}
343
- */
344
- function validateAppendParams(params, options) {
345
- const { actor, event, spec_id, data } = params || {};
346
- const projectRoot = options.projectRoot || process.cwd();
347
- const sessionId = options.session_id || process.env.CAWS_SESSION_ID || 'standalone';
348
-
349
- if (!actor || typeof actor !== 'string') {
350
- throw new Error('event-log.appendEvent: `actor` is required (non-empty string)');
351
- }
352
- if (!event || typeof event !== 'string') {
353
- throw new Error('event-log.appendEvent: `event` is required (non-empty string)');
354
- }
355
- if (REQUIRES_SPEC_ID.has(event)) {
356
- if (!spec_id || typeof spec_id !== 'string' || spec_id.trim() === '') {
357
- throw new Error(
358
- `event-log.appendEvent: event "${event}" requires a non-empty spec_id ` +
359
- `(got ${JSON.stringify(spec_id)}). This is the fence that prevents the ` +
360
- `.caws/state/undefined.json bug class — do not catch this error and continue.`
361
- );
362
- }
363
- }
364
-
365
- return {
366
- actor,
367
- event,
368
- spec_id,
369
- data,
370
- sessionId,
371
- eventsPath: getEventsPath(projectRoot),
372
- };
373
- }
374
-
375
- /**
376
- * Shared critical section: read tail, build event, write. Assumes the
377
- * caller already holds the lock. Returns the new event (with seq, hashes).
378
- *
379
- * @param {object} ctx — output of validateAppendParams
380
- * @returns {{seq: number, event_hash: string, prev_hash: string}}
381
- */
382
- function writeEventUnderLock(ctx) {
383
- const { actor, event, spec_id, data, sessionId, eventsPath } = ctx;
384
-
385
- const lastLine = readLastLine(eventsPath);
386
- let seq = 1;
387
- let prevHash = '';
388
- if (lastLine !== null) {
389
- let lastEvent;
390
- try {
391
- lastEvent = JSON.parse(lastLine);
392
- } catch (parseErr) {
393
- throw new Error(
394
- `event-log.appendEvent: last line of ${eventsPath} is malformed: ${parseErr.message}. ` +
395
- `The log is corrupt; manual inspection required before continuing.`
396
- );
397
- }
398
- if (typeof lastEvent.seq !== 'number' || !Number.isInteger(lastEvent.seq)) {
399
- throw new Error(
400
- `event-log.appendEvent: last event missing integer seq (got ${JSON.stringify(lastEvent.seq)})`
401
- );
402
- }
403
- seq = lastEvent.seq + 1;
404
- prevHash = typeof lastEvent.event_hash === 'string' ? lastEvent.event_hash : '';
405
- }
406
-
407
- const newEvent = {
408
- seq,
409
- ts: new Date().toISOString(),
410
- session_id: sessionId,
411
- actor,
412
- event,
413
- };
414
- if (spec_id !== undefined && spec_id !== null && spec_id !== '') {
415
- newEvent.spec_id = spec_id;
416
- }
417
- if (data !== undefined) {
418
- newEvent.data = data;
419
- }
420
- newEvent.prev_hash = prevHash;
421
- newEvent.event_hash = computeEventHash(newEvent);
422
-
423
- fs.appendFileSync(eventsPath, JSON.stringify(newEvent) + '\n', { encoding: 'utf8' });
424
-
425
- return {
426
- seq: newEvent.seq,
427
- event_hash: newEvent.event_hash,
428
- prev_hash: newEvent.prev_hash,
429
- };
430
- }
431
-
432
- async function appendEvent(params, options = {}) {
433
- const ctx = validateAppendParams(params, options);
434
- const handle = await acquireLock(ctx.eventsPath);
435
- try {
436
- return writeEventUnderLock(ctx);
437
- } finally {
438
- releaseLock(handle);
439
- }
440
- }
441
-
442
- /**
443
- * Synchronous variant of `appendEvent`. Same contract, same fail-loud
444
- * behavior. Intended for call sites that cannot await (synchronous
445
- * session manager functions, hooks, etc.). Blocks the thread during
446
- * lock contention via `Atomics.wait`.
447
- *
448
- * Prefer `appendEvent` in async contexts — it cooperates with the event
449
- * loop instead of blocking it.
450
- *
451
- * @param {object} params — same as appendEvent
452
- * @param {object} [options] — same as appendEvent
453
- * @returns {{seq: number, event_hash: string, prev_hash: string}}
454
- */
455
- function appendEventSync(params, options = {}) {
456
- const ctx = validateAppendParams(params, options);
457
- const handle = acquireLockSync(ctx.eventsPath);
458
- try {
459
- return writeEventUnderLock(ctx);
460
- } finally {
461
- releaseLock(handle);
462
- }
463
- }
464
-
465
- /**
466
- * Read all events from the project's event log, in seq order.
467
- *
468
- * Tolerates a partial trailing line (from a crashed writer) by discarding
469
- * it. Returns an array of parsed events. The caller is responsible for
470
- * filtering by spec_id or event type.
471
- *
472
- * This is intentionally eager (not a stream) in Phase 1 — the expected
473
- * event log size for CLI-scale projects is under 10k lines, well within
474
- * memory. A streaming reader is a future addition when compaction lands.
475
- *
476
- * @param {object} [options]
477
- * @param {string} [options.projectRoot] — defaults to cwd
478
- * @param {boolean} [options.strict] — if true, throw on any malformed line (default false: discard trailing partial)
479
- * @returns {object[]}
480
- */
481
- function readEvents(options = {}) {
482
- const projectRoot = options.projectRoot || process.cwd();
483
- const strict = options.strict === true;
484
- const eventsPath = getEventsPath(projectRoot);
485
-
486
- if (!fs.existsSync(eventsPath)) return [];
487
- const content = fs.readFileSync(eventsPath, 'utf8');
488
- if (content.length === 0) return [];
489
-
490
- const lines = content.split('\n');
491
- // The file ends in \n, so the split yields a trailing empty element.
492
- // Any other empty element is a malformed blank line.
493
- const events = [];
494
- for (let i = 0; i < lines.length; i++) {
495
- const line = lines[i];
496
- const isLast = i === lines.length - 1;
497
- if (line.length === 0) {
498
- if (isLast) continue; // normal trailing newline
499
- if (strict) {
500
- throw new Error(`event-log.readEvents: empty line at index ${i} (strict mode)`);
501
- }
502
- continue;
503
- }
504
- try {
505
- events.push(JSON.parse(line));
506
- } catch (err) {
507
- if (isLast) {
508
- // Partial trailing line from a crashed writer — tolerate unless strict.
509
- if (strict) {
510
- throw new Error(
511
- `event-log.readEvents: partial trailing line (strict mode): ${err.message}`
512
- );
513
- }
514
- // Drop it silently; the next append will overwrite it.
515
- continue;
516
- }
517
- throw new Error(
518
- `event-log.readEvents: malformed line at index ${i}: ${err.message}. ` +
519
- `The log is corrupt; manual inspection required.`
520
- );
521
- }
522
- }
523
- return events;
524
- }
525
-
526
- /**
527
- * Verify the hash chain of the event log end-to-end. Walks every event,
528
- * recomputes its event_hash, and asserts prev_hash matches the previous
529
- * event's event_hash.
530
- *
531
- * Intended for a future `caws events verify` command; exported here so
532
- * tests can use it to prove chain continuity.
533
- *
534
- * @param {object} [options]
535
- * @param {string} [options.projectRoot]
536
- * @returns {{ok: boolean, count: number, firstBadSeq?: number, reason?: string}}
537
- */
538
- function verifyChain(options = {}) {
539
- const events = readEvents({ ...options, strict: true });
540
- let prevHash = '';
541
- for (const event of events) {
542
- if (event.prev_hash !== prevHash) {
543
- return {
544
- ok: false,
545
- count: events.length,
546
- firstBadSeq: event.seq,
547
- reason: `prev_hash mismatch at seq ${event.seq}: expected ${JSON.stringify(prevHash)}, got ${JSON.stringify(event.prev_hash)}`,
548
- };
549
- }
550
- const recomputed = computeEventHash(event);
551
- if (recomputed !== event.event_hash) {
552
- return {
553
- ok: false,
554
- count: events.length,
555
- firstBadSeq: event.seq,
556
- reason: `event_hash mismatch at seq ${event.seq}: stored ${event.event_hash}, recomputed ${recomputed}`,
557
- };
558
- }
559
- prevHash = event.event_hash;
560
- }
561
- return { ok: true, count: events.length };
562
- }
563
-
564
- // ---------------------------------------------------------------------------
565
- // Exports
566
- // ---------------------------------------------------------------------------
567
-
568
- module.exports = {
569
- appendEvent,
570
- appendEventSync,
571
- readEvents,
572
- verifyChain,
573
-
574
- // Exposed for tests and the renderer; not part of the stable public API.
575
- _internal: {
576
- canonicalJson,
577
- computeEventHash,
578
- readLastLine,
579
- REQUIRES_SPEC_ID,
580
- OPTIONAL_SPEC_ID,
581
- HASH_DOMAIN,
582
- EVENTS_FILE,
583
- },
584
- };