@paths.design/caws-cli 9.3.2 → 10.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 (286) hide show
  1. package/README.md +71 -32
  2. package/dist/budget-derivation.js +221 -74
  3. package/dist/commands/archive.js +67 -28
  4. package/dist/commands/burnup.js +20 -11
  5. package/dist/commands/diagnose.js +34 -22
  6. package/dist/commands/evaluate.js +41 -15
  7. package/dist/commands/gates.js +149 -0
  8. package/dist/commands/init.js +150 -19
  9. package/dist/commands/iterate.js +81 -4
  10. package/dist/commands/parallel.js +4 -0
  11. package/dist/commands/plan.js +9 -19
  12. package/dist/commands/provenance.js +53 -17
  13. package/dist/commands/quality-monitor.js +64 -45
  14. package/dist/commands/scope.js +264 -0
  15. package/dist/commands/sidecar.js +74 -0
  16. package/dist/commands/specs.js +381 -45
  17. package/dist/commands/status.js +117 -9
  18. package/dist/commands/templates.js +0 -8
  19. package/dist/commands/tutorial.js +10 -9
  20. package/dist/commands/validate.js +70 -6
  21. package/dist/commands/verify-acs.js +48 -76
  22. package/dist/commands/waivers.js +212 -13
  23. package/dist/commands/worktree.js +131 -26
  24. package/dist/error-handler.js +2 -13
  25. package/dist/gates/budget-limit.js +121 -0
  26. package/dist/gates/feedback.js +260 -0
  27. package/dist/gates/format.js +179 -0
  28. package/dist/gates/god-object.js +117 -0
  29. package/dist/gates/pipeline.js +167 -0
  30. package/dist/gates/scope-boundary.js +93 -0
  31. package/dist/gates/spec-completeness.js +109 -0
  32. package/dist/gates/todo-detection.js +205 -0
  33. package/dist/index.js +157 -151
  34. package/dist/parallel/parallel-manager.js +3 -3
  35. package/dist/policy/PolicyManager.js +51 -17
  36. package/dist/scaffold/claude-hooks.js +24 -1
  37. package/dist/scaffold/git-hooks.js +45 -102
  38. package/dist/scaffold/index.js +4 -3
  39. package/dist/session/session-manager.js +105 -14
  40. package/dist/sidecars/index.js +33 -0
  41. package/dist/sidecars/listeners.js +40 -0
  42. package/dist/sidecars/provenance-summary.js +238 -0
  43. package/dist/sidecars/quality-gaps.js +258 -0
  44. package/dist/sidecars/schema.js +149 -0
  45. package/dist/sidecars/spec-drift.js +151 -0
  46. package/dist/sidecars/waiver-draft.js +176 -0
  47. package/dist/templates/.caws/schemas/policy.schema.json +112 -0
  48. package/dist/templates/.caws/schemas/scope.schema.json +3 -3
  49. package/dist/templates/.caws/schemas/waivers.schema.json +96 -20
  50. package/dist/templates/.caws/schemas/working-spec.schema.json +264 -57
  51. package/dist/templates/.caws/schemas/worktrees.schema.json +3 -1
  52. package/dist/templates/.caws/templates/working-spec.template.yml +10 -4
  53. package/dist/templates/.caws/tools/scope-guard.js +66 -15
  54. package/dist/templates/.claude/README.md +1 -1
  55. package/dist/templates/.claude/hooks/audit.sh +0 -0
  56. package/dist/templates/.claude/hooks/block-dangerous.sh +52 -11
  57. package/dist/templates/.claude/hooks/classify_command.py +592 -0
  58. package/dist/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
  59. package/dist/templates/.claude/hooks/protected-paths.sh +39 -0
  60. package/dist/templates/.claude/hooks/quality-check.sh +23 -10
  61. package/dist/templates/.claude/hooks/scope-guard.sh +136 -55
  62. package/dist/templates/.claude/hooks/session-caws-status.sh +2 -2
  63. package/dist/templates/.claude/hooks/session-log.sh +76 -3
  64. package/dist/templates/.claude/hooks/stop-worktree-check.sh +1 -1
  65. package/dist/templates/.claude/hooks/test_classify_command.py +370 -0
  66. package/dist/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
  67. package/dist/templates/.claude/hooks/worktree-guard.sh +2 -2
  68. package/dist/templates/.claude/hooks/worktree-write-guard.sh +97 -4
  69. package/dist/templates/.claude/settings.json +31 -0
  70. package/dist/templates/.cursor/hooks/caws-quality-check.sh +4 -4
  71. package/dist/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
  72. package/dist/templates/.cursor/hooks/session-log.sh +924 -0
  73. package/dist/templates/.cursor/hooks.json +25 -0
  74. package/dist/templates/.cursor/rules/02-quality-gates.mdc +3 -5
  75. package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
  76. package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
  77. package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
  78. package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
  79. package/dist/templates/.github/copilot-instructions.md +5 -5
  80. package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
  81. package/dist/templates/.junie/guidelines.md +2 -2
  82. package/dist/templates/.vscode/settings.json +3 -1
  83. package/dist/templates/.windsurf/rules/caws-quality-standards.md +2 -2
  84. package/dist/templates/.windsurf/workflows/caws-guided-development.md +3 -3
  85. package/dist/templates/CLAUDE.md +77 -8
  86. package/dist/templates/agents.md +50 -9
  87. package/dist/templates/docs/README.md +8 -7
  88. package/dist/templates/scripts/new_feature.sh +80 -0
  89. package/dist/test-analysis.js +43 -30
  90. package/dist/tool-loader.js +1 -1
  91. package/dist/utils/agent-session.js +202 -0
  92. package/dist/utils/detection.js +8 -2
  93. package/dist/utils/event-log.js +584 -0
  94. package/dist/utils/event-renderer.js +521 -0
  95. package/dist/utils/finalization.js +7 -6
  96. package/dist/utils/gitignore-updater.js +3 -0
  97. package/dist/utils/lifecycle-events.js +94 -0
  98. package/dist/utils/quality-gates-utils.js +29 -44
  99. package/dist/utils/schema-validator.js +50 -0
  100. package/dist/utils/spec-resolver.js +93 -21
  101. package/dist/utils/working-state.js +530 -0
  102. package/dist/validation/spec-validation.js +191 -31
  103. package/dist/waivers-manager.js +144 -6
  104. package/dist/worktree/worktree-manager.js +598 -95
  105. package/package.json +9 -8
  106. package/templates/.caws/schemas/policy.schema.json +112 -0
  107. package/templates/.caws/schemas/scope.schema.json +3 -3
  108. package/templates/.caws/schemas/waivers.schema.json +96 -20
  109. package/templates/.caws/schemas/working-spec.schema.json +264 -57
  110. package/templates/.caws/schemas/worktrees.schema.json +3 -1
  111. package/templates/.caws/templates/working-spec.template.yml +10 -4
  112. package/templates/.caws/tools/scope-guard.js +66 -15
  113. package/templates/.claude/README.md +1 -1
  114. package/templates/.claude/hooks/block-dangerous.sh +52 -11
  115. package/templates/.claude/hooks/classify_command.py +592 -0
  116. package/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
  117. package/templates/.claude/hooks/protected-paths.sh +39 -0
  118. package/templates/.claude/hooks/quality-check.sh +23 -10
  119. package/templates/.claude/hooks/scope-guard.sh +136 -55
  120. package/templates/.claude/hooks/session-caws-status.sh +2 -2
  121. package/templates/.claude/hooks/session-log.sh +76 -3
  122. package/templates/.claude/hooks/stop-worktree-check.sh +1 -1
  123. package/templates/.claude/hooks/test_classify_command.py +370 -0
  124. package/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
  125. package/templates/.claude/hooks/worktree-guard.sh +2 -2
  126. package/templates/.claude/hooks/worktree-write-guard.sh +97 -4
  127. package/templates/.claude/settings.json +31 -0
  128. package/templates/.cursor/hooks/caws-quality-check.sh +4 -4
  129. package/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
  130. package/templates/.cursor/hooks/session-log.sh +924 -0
  131. package/templates/.cursor/hooks.json +25 -0
  132. package/templates/.cursor/rules/02-quality-gates.mdc +3 -5
  133. package/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
  134. package/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
  135. package/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
  136. package/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
  137. package/templates/.github/copilot-instructions.md +5 -5
  138. package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
  139. package/templates/.junie/guidelines.md +2 -2
  140. package/templates/.vscode/settings.json +3 -1
  141. package/templates/.windsurf/rules/caws-quality-standards.md +2 -2
  142. package/templates/.windsurf/workflows/caws-guided-development.md +3 -3
  143. package/templates/CLAUDE.md +77 -8
  144. package/templates/{AGENTS.md → agents.md} +50 -9
  145. package/templates/docs/README.md +8 -7
  146. package/templates/scripts/new_feature.sh +80 -0
  147. package/dist/budget-derivation.d.ts +0 -74
  148. package/dist/budget-derivation.d.ts.map +0 -1
  149. package/dist/cicd-optimizer.d.ts +0 -142
  150. package/dist/cicd-optimizer.d.ts.map +0 -1
  151. package/dist/commands/archive.d.ts +0 -51
  152. package/dist/commands/archive.d.ts.map +0 -1
  153. package/dist/commands/burnup.d.ts +0 -6
  154. package/dist/commands/burnup.d.ts.map +0 -1
  155. package/dist/commands/diagnose.d.ts +0 -52
  156. package/dist/commands/diagnose.d.ts.map +0 -1
  157. package/dist/commands/evaluate.d.ts +0 -8
  158. package/dist/commands/evaluate.d.ts.map +0 -1
  159. package/dist/commands/init.d.ts +0 -5
  160. package/dist/commands/init.d.ts.map +0 -1
  161. package/dist/commands/iterate.d.ts +0 -8
  162. package/dist/commands/iterate.d.ts.map +0 -1
  163. package/dist/commands/mode.d.ts +0 -25
  164. package/dist/commands/mode.d.ts.map +0 -1
  165. package/dist/commands/parallel.d.ts +0 -7
  166. package/dist/commands/parallel.d.ts.map +0 -1
  167. package/dist/commands/plan.d.ts +0 -49
  168. package/dist/commands/plan.d.ts.map +0 -1
  169. package/dist/commands/provenance.d.ts +0 -32
  170. package/dist/commands/provenance.d.ts.map +0 -1
  171. package/dist/commands/quality-gates.d.ts +0 -6
  172. package/dist/commands/quality-gates.d.ts.map +0 -1
  173. package/dist/commands/quality-gates.js +0 -444
  174. package/dist/commands/quality-monitor.d.ts +0 -17
  175. package/dist/commands/quality-monitor.d.ts.map +0 -1
  176. package/dist/commands/session.d.ts +0 -7
  177. package/dist/commands/session.d.ts.map +0 -1
  178. package/dist/commands/specs.d.ts +0 -77
  179. package/dist/commands/specs.d.ts.map +0 -1
  180. package/dist/commands/status.d.ts +0 -44
  181. package/dist/commands/status.d.ts.map +0 -1
  182. package/dist/commands/templates.d.ts +0 -74
  183. package/dist/commands/templates.d.ts.map +0 -1
  184. package/dist/commands/tool.d.ts +0 -13
  185. package/dist/commands/tool.d.ts.map +0 -1
  186. package/dist/commands/troubleshoot.d.ts +0 -8
  187. package/dist/commands/troubleshoot.d.ts.map +0 -1
  188. package/dist/commands/troubleshoot.js +0 -104
  189. package/dist/commands/tutorial.d.ts +0 -55
  190. package/dist/commands/tutorial.d.ts.map +0 -1
  191. package/dist/commands/validate.d.ts +0 -15
  192. package/dist/commands/validate.d.ts.map +0 -1
  193. package/dist/commands/waivers.d.ts +0 -8
  194. package/dist/commands/waivers.d.ts.map +0 -1
  195. package/dist/commands/workflow.d.ts +0 -85
  196. package/dist/commands/workflow.d.ts.map +0 -1
  197. package/dist/commands/worktree.d.ts +0 -7
  198. package/dist/commands/worktree.d.ts.map +0 -1
  199. package/dist/config/index.d.ts +0 -29
  200. package/dist/config/index.d.ts.map +0 -1
  201. package/dist/config/lite-scope.d.ts +0 -33
  202. package/dist/config/lite-scope.d.ts.map +0 -1
  203. package/dist/config/modes.d.ts +0 -264
  204. package/dist/config/modes.d.ts.map +0 -1
  205. package/dist/constants/spec-types.d.ts +0 -93
  206. package/dist/constants/spec-types.d.ts.map +0 -1
  207. package/dist/error-handler.d.ts +0 -151
  208. package/dist/error-handler.d.ts.map +0 -1
  209. package/dist/generators/jest-config-generator.d.ts +0 -32
  210. package/dist/generators/jest-config-generator.d.ts.map +0 -1
  211. package/dist/generators/jest-config.d.ts +0 -32
  212. package/dist/generators/jest-config.d.ts.map +0 -1
  213. package/dist/generators/jest-config.js +0 -242
  214. package/dist/generators/working-spec.d.ts +0 -13
  215. package/dist/generators/working-spec.d.ts.map +0 -1
  216. package/dist/index-new.d.ts +0 -5
  217. package/dist/index-new.d.ts.map +0 -1
  218. package/dist/index-new.js +0 -317
  219. package/dist/index.d.ts +0 -5
  220. package/dist/index.d.ts.map +0 -1
  221. package/dist/index.js.backup +0 -4711
  222. package/dist/minimal-cli.d.ts +0 -3
  223. package/dist/minimal-cli.d.ts.map +0 -1
  224. package/dist/parallel/parallel-manager.d.ts +0 -67
  225. package/dist/parallel/parallel-manager.d.ts.map +0 -1
  226. package/dist/policy/PolicyManager.d.ts +0 -104
  227. package/dist/policy/PolicyManager.d.ts.map +0 -1
  228. package/dist/scaffold/claude-hooks.d.ts +0 -28
  229. package/dist/scaffold/claude-hooks.d.ts.map +0 -1
  230. package/dist/scaffold/cursor-hooks.d.ts +0 -7
  231. package/dist/scaffold/cursor-hooks.d.ts.map +0 -1
  232. package/dist/scaffold/git-hooks.d.ts +0 -38
  233. package/dist/scaffold/git-hooks.d.ts.map +0 -1
  234. package/dist/scaffold/index.d.ts +0 -17
  235. package/dist/scaffold/index.d.ts.map +0 -1
  236. package/dist/session/session-manager.d.ts +0 -94
  237. package/dist/session/session-manager.d.ts.map +0 -1
  238. package/dist/spec/SpecFileManager.d.ts +0 -146
  239. package/dist/spec/SpecFileManager.d.ts.map +0 -1
  240. package/dist/templates/.cursor/hooks/caws-tool-validation.sh +0 -121
  241. package/dist/templates/.github/copilot/instructions.md +0 -311
  242. package/dist/test-analysis.d.ts +0 -231
  243. package/dist/test-analysis.d.ts.map +0 -1
  244. package/dist/tool-interface.d.ts +0 -236
  245. package/dist/tool-interface.d.ts.map +0 -1
  246. package/dist/tool-loader.d.ts +0 -77
  247. package/dist/tool-loader.d.ts.map +0 -1
  248. package/dist/tool-validator.d.ts +0 -72
  249. package/dist/tool-validator.d.ts.map +0 -1
  250. package/dist/utils/async-utils.d.ts +0 -73
  251. package/dist/utils/async-utils.d.ts.map +0 -1
  252. package/dist/utils/command-wrapper.d.ts +0 -66
  253. package/dist/utils/command-wrapper.d.ts.map +0 -1
  254. package/dist/utils/detection.d.ts +0 -14
  255. package/dist/utils/detection.d.ts.map +0 -1
  256. package/dist/utils/error-categories.d.ts +0 -52
  257. package/dist/utils/error-categories.d.ts.map +0 -1
  258. package/dist/utils/finalization.d.ts +0 -17
  259. package/dist/utils/finalization.d.ts.map +0 -1
  260. package/dist/utils/git-lock.d.ts +0 -13
  261. package/dist/utils/git-lock.d.ts.map +0 -1
  262. package/dist/utils/gitignore-updater.d.ts +0 -39
  263. package/dist/utils/gitignore-updater.d.ts.map +0 -1
  264. package/dist/utils/ide-detection.d.ts +0 -89
  265. package/dist/utils/ide-detection.d.ts.map +0 -1
  266. package/dist/utils/project-analysis.d.ts +0 -34
  267. package/dist/utils/project-analysis.d.ts.map +0 -1
  268. package/dist/utils/promise-utils.d.ts +0 -30
  269. package/dist/utils/promise-utils.d.ts.map +0 -1
  270. package/dist/utils/quality-gates-utils.d.ts +0 -49
  271. package/dist/utils/quality-gates-utils.d.ts.map +0 -1
  272. package/dist/utils/quality-gates.d.ts +0 -49
  273. package/dist/utils/quality-gates.d.ts.map +0 -1
  274. package/dist/utils/quality-gates.js +0 -402
  275. package/dist/utils/spec-resolver.d.ts +0 -80
  276. package/dist/utils/spec-resolver.d.ts.map +0 -1
  277. package/dist/utils/typescript-detector.d.ts +0 -66
  278. package/dist/utils/typescript-detector.d.ts.map +0 -1
  279. package/dist/utils/yaml-validation.d.ts +0 -32
  280. package/dist/utils/yaml-validation.d.ts.map +0 -1
  281. package/dist/validation/spec-validation.d.ts +0 -43
  282. package/dist/validation/spec-validation.d.ts.map +0 -1
  283. package/dist/waivers-manager.d.ts +0 -167
  284. package/dist/waivers-manager.d.ts.map +0 -1
  285. package/dist/worktree/worktree-manager.d.ts +0 -54
  286. package/dist/worktree/worktree-manager.d.ts.map +0 -1
@@ -0,0 +1,80 @@
1
+ #!/bin/bash
2
+ # Create a new CAWS feature spec + optional worktree
3
+ #
4
+ # Usage:
5
+ # scripts/new_feature.sh FEAT-001 "Feature description"
6
+ # scripts/new_feature.sh FEAT-001 "Feature description" --worktree
7
+ #
8
+ # This script:
9
+ # 1. Creates a feature spec in .caws/specs/<ID>.yaml
10
+ # 2. Optionally creates a git worktree for isolated development
11
+
12
+ set -euo pipefail
13
+
14
+ if [ $# -lt 2 ]; then
15
+ echo "usage: scripts/new_feature.sh <SPEC-ID> <title> [--worktree]" >&2
16
+ echo " example: scripts/new_feature.sh FEAT-001 'Add user authentication'" >&2
17
+ exit 1
18
+ fi
19
+
20
+ SPEC_ID="$1"
21
+ TITLE="$2"
22
+ USE_WORKTREE="${3:-}"
23
+
24
+ # Validate spec ID format (PREFIX-NUMBER, e.g., FEAT-001, AUTH-042)
25
+ if ! echo "$SPEC_ID" | grep -qE '^[A-Z]+-[0-9]+$'; then
26
+ echo "error: spec ID must be in format PREFIX-NUMBER (e.g., FEAT-001, AUTH-042)" >&2
27
+ exit 1
28
+ fi
29
+
30
+ SPEC_FILE=".caws/specs/${SPEC_ID}.yaml"
31
+
32
+ # Check if spec already exists
33
+ if [ -f "$SPEC_FILE" ]; then
34
+ echo "error: spec $SPEC_ID already exists at $SPEC_FILE" >&2
35
+ exit 1
36
+ fi
37
+
38
+ # Create spec directory if needed
39
+ mkdir -p .caws/specs
40
+
41
+ # Generate spec scaffold
42
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
43
+ cat > "$SPEC_FILE" << EOF
44
+ id: ${SPEC_ID}
45
+ type: feature
46
+ title: "${TITLE}"
47
+ status: draft
48
+ risk_tier: 2
49
+ mode: development
50
+ created_at: "${TIMESTAMP}"
51
+ updated_at: "${TIMESTAMP}"
52
+ blast_radius:
53
+ modules: []
54
+ data_migration: false
55
+ operational_rollback_slo: 5m
56
+ scope:
57
+ in: []
58
+ out:
59
+ - docs/reference/v1/
60
+ - .caws/
61
+ threats: []
62
+ invariants: []
63
+ acceptance: []
64
+ acceptance_criteria: []
65
+ milestones: []
66
+ EOF
67
+
68
+ echo "Created feature spec: $SPEC_FILE"
69
+
70
+ # Optionally create worktree
71
+ if [ "$USE_WORKTREE" = "--worktree" ]; then
72
+ BRANCH_NAME="feat/${SPEC_ID,,}" # lowercase
73
+ WORKTREE_DIR=".caws/worktrees/${SPEC_ID,,}"
74
+
75
+ git worktree add "$WORKTREE_DIR" -b "$BRANCH_NAME"
76
+ echo "Created worktree: $WORKTREE_DIR (branch: $BRANCH_NAME)"
77
+ echo ""
78
+ echo "To start working:"
79
+ echo " cd $WORKTREE_DIR"
80
+ fi
@@ -7,6 +7,7 @@
7
7
  const fs = require('fs-extra');
8
8
  const path = require('path');
9
9
  const yaml = require('js-yaml');
10
+ const { resolveSpec } = require('./utils/spec-resolver');
10
11
 
11
12
  /**
12
13
  * Waiver Pattern Learning Engine
@@ -198,7 +199,7 @@ class WaiverPatternLearner {
198
199
  /**
199
200
  * Analyze budget overrun patterns
200
201
  */
201
- analyzeBudgetOverruns(waivers, specs) {
202
+ analyzeBudgetOverruns(waivers, _specs) {
202
203
  const budgetWaivers = waivers.filter((w) => w.gates?.includes('budget_limit'));
203
204
 
204
205
  if (budgetWaivers.length === 0) {
@@ -266,7 +267,7 @@ class WaiverPatternLearner {
266
267
  /**
267
268
  * Identify risk factors from waiver patterns
268
269
  */
269
- identifyRiskFactors(waivers, specs) {
270
+ identifyRiskFactors(waivers, _specs) {
270
271
  // Simple risk factor identification based on waiver frequency
271
272
  const riskFactors = [];
272
273
 
@@ -595,17 +596,17 @@ class BudgetPredictor {
595
596
  /**
596
597
  * Main Test Analysis CLI handler
597
598
  */
598
- async function testAnalysisCommand(subcommand, options = []) {
599
+ async function testAnalysisCommand(subcommand, options = [], commandOptions = {}) {
599
600
  const chalk = (await import('chalk')).default;
600
601
 
601
602
  try {
602
603
  switch (subcommand) {
603
604
  case 'assess-budget':
604
- return await handleAssessBudget(options);
605
+ return await handleAssessBudget(options, commandOptions);
605
606
  case 'analyze-patterns':
606
607
  return await handleAnalyzePatterns(options);
607
608
  case 'find-similar':
608
- return await handleFindSimilar(options);
609
+ return await handleFindSimilar(options, commandOptions);
609
610
  default:
610
611
  console.log(chalk.red('Unknown test-analysis subcommand'));
611
612
  console.log('Available commands:');
@@ -619,28 +620,49 @@ async function testAnalysisCommand(subcommand, options = []) {
619
620
  }
620
621
  }
621
622
 
623
+ /**
624
+ * Resolve the current spec for analysis commands.
625
+ * Supports explicit `--spec <path>` for compatibility, but prefers
626
+ * the suite-standard resolver and `--spec-id`.
627
+ * @param {string[]} optionArgs
628
+ * @param {Object} commandOptions
629
+ * @returns {Promise<{spec: Object, specPath: string}>}
630
+ */
631
+ async function resolveAnalysisSpec(optionArgs = [], commandOptions = {}) {
632
+ let specFile = null;
633
+ if (Array.isArray(optionArgs) && optionArgs.includes('--spec')) {
634
+ const specIndex = optionArgs.indexOf('--spec');
635
+ if (specIndex + 1 < optionArgs.length) {
636
+ specFile = optionArgs[specIndex + 1];
637
+ }
638
+ }
639
+
640
+ const resolved = await resolveSpec({
641
+ specId: commandOptions.specId,
642
+ specFile,
643
+ warnLegacy: false,
644
+ interactive: false,
645
+ });
646
+
647
+ return {
648
+ spec: resolved.spec,
649
+ specPath: resolved.path,
650
+ };
651
+ }
652
+
622
653
  /**
623
654
  * Handle budget assessment command
624
655
  */
625
- async function handleAssessBudget(options) {
656
+ async function handleAssessBudget(options, commandOptions = {}) {
626
657
  const chalk = (await import('chalk')).default;
627
658
  const predictor = new BudgetPredictor();
628
659
 
629
- // Load current spec
630
- let specPath = '.caws/working-spec.yaml';
631
- if (options.includes('--spec')) {
632
- const specIndex = options.indexOf('--spec');
633
- if (specIndex + 1 < options.length) {
634
- specPath = options[specIndex + 1];
635
- }
636
- }
637
-
638
660
  try {
639
- const specContent = fs.readFileSync(specPath, 'utf8');
640
- const spec = yaml.load(specContent);
661
+ const { spec, specPath } = await resolveAnalysisSpec(options, commandOptions);
641
662
 
642
663
  console.log(chalk.cyan(`Budget Assessment for ${spec.id}`));
643
664
  console.log('==============================================');
665
+ console.log(chalk.gray(`Spec: ${path.relative(process.cwd(), specPath)}`));
644
666
 
645
667
  const result = predictor.assessBudget(spec);
646
668
 
@@ -681,7 +703,7 @@ async function handleAssessBudget(options) {
681
703
  /**
682
704
  * Handle pattern analysis command
683
705
  */
684
- async function handleAnalyzePatterns(options) {
706
+ async function handleAnalyzePatterns(_options) {
685
707
  const chalk = (await import('chalk')).default;
686
708
  const learner = new WaiverPatternLearner();
687
709
 
@@ -727,25 +749,16 @@ async function handleAnalyzePatterns(options) {
727
749
  /**
728
750
  * Handle find similar projects command
729
751
  */
730
- async function handleFindSimilar(options) {
752
+ async function handleFindSimilar(options, commandOptions = {}) {
731
753
  const chalk = (await import('chalk')).default;
732
754
  const matcher = new ProjectSimilarityMatcher();
733
755
 
734
- // Load current spec
735
- let specPath = '.caws/working-spec.yaml';
736
- if (options.includes('--spec')) {
737
- const specIndex = options.indexOf('--spec');
738
- if (specIndex + 1 < options.length) {
739
- specPath = options[specIndex + 1];
740
- }
741
- }
742
-
743
756
  try {
744
- const specContent = fs.readFileSync(specPath, 'utf8');
745
- const spec = yaml.load(specContent);
757
+ const { spec, specPath } = await resolveAnalysisSpec(options, commandOptions);
746
758
 
747
759
  console.log(chalk.cyan(`Finding projects similar to ${spec.id}`));
748
760
  console.log('==============================================');
761
+ console.log(chalk.gray(`Spec: ${path.relative(process.cwd(), specPath)}`));
749
762
 
750
763
  const similar = matcher.findSimilarProjects(spec);
751
764
 
@@ -128,7 +128,7 @@ class ToolLoader extends EventEmitter {
128
128
  this.emit('tool:loaded', { id: toolId, metadata: tool.metadata });
129
129
 
130
130
  return tool;
131
- }, `Tool loading failed: ${toolId}`);
131
+ }, 'Tool loading failed');
132
132
  }
133
133
 
134
134
  /**
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Agent Session Identity
3
+ *
4
+ * Provides a unified way to identify the current agent session across
5
+ * Claude Code, Cursor, and other IDE agent environments.
6
+ *
7
+ * Sources checked (first match wins):
8
+ * 1. CLAUDE_SESSION_ID — set by Claude Code automatically
9
+ * 2. .caws/agents.json — written by Cursor session-log hook (conversation_id)
10
+ * 3. CURSOR_TRACE_ID — set by Cursor (per-request, not stable, last resort)
11
+ *
12
+ * The agent registry (.caws/agents.json) also tracks active agents for
13
+ * multi-agent coordination. Entries expire after a configurable TTL.
14
+ *
15
+ * @author @darianrosebrook
16
+ */
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+
21
+ const AGENTS_REGISTRY = '.caws/agents.json';
22
+ const DEFAULT_TTL_MS = 30 * 60 * 1000; // 30 minutes
23
+
24
+ /**
25
+ * Get the current agent's session ID from the best available source.
26
+ * @param {string} [projectRoot] - Project root (for reading agent registry)
27
+ * @returns {string|null} Session ID or null if not in an agent context
28
+ */
29
+ function getAgentSessionId(projectRoot) {
30
+ // 1. Claude Code — most reliable, set automatically
31
+ if (process.env.CLAUDE_SESSION_ID) {
32
+ return process.env.CLAUDE_SESSION_ID;
33
+ }
34
+
35
+ // 2. Agent registry — written by Cursor session-log hook
36
+ if (projectRoot) {
37
+ const registry = loadAgentRegistry(projectRoot);
38
+ const active = findActiveAgent(registry);
39
+ if (active) {
40
+ return active.sessionId;
41
+ }
42
+ }
43
+
44
+ // 3. Cursor trace ID — per-request, not stable, but better than nothing
45
+ if (process.env.CURSOR_TRACE_ID) {
46
+ return `cursor:${process.env.CURSOR_TRACE_ID}`;
47
+ }
48
+
49
+ return null;
50
+ }
51
+
52
+ /**
53
+ * Get the agent platform name.
54
+ * @returns {string} 'claude-code' | 'cursor' | 'unknown'
55
+ */
56
+ function getAgentPlatform() {
57
+ if (process.env.CLAUDE_SESSION_ID) return 'claude-code';
58
+ if (process.env.CURSOR_TRACE_ID) return 'cursor';
59
+ return 'unknown';
60
+ }
61
+
62
+ /**
63
+ * Load the agent registry, pruning stale entries.
64
+ * @param {string} root - Project root
65
+ * @returns {object} Registry with { agents: { [sessionId]: entry } }
66
+ */
67
+ function loadAgentRegistry(root) {
68
+ const registryPath = path.join(root, AGENTS_REGISTRY);
69
+ let registry = { version: 1, agents: {} };
70
+
71
+ if (fs.existsSync(registryPath)) {
72
+ try {
73
+ registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
74
+ } catch {
75
+ // Corrupt file — start fresh
76
+ registry = { version: 1, agents: {} };
77
+ }
78
+ }
79
+
80
+ // Prune stale entries on every read
81
+ const now = Date.now();
82
+ let pruned = false;
83
+ for (const [id, entry] of Object.entries(registry.agents || {})) {
84
+ const ttl = entry.ttl || DEFAULT_TTL_MS;
85
+ const lastSeenTime = entry.lastSeen ? new Date(entry.lastSeen).getTime() : 0;
86
+ const lastSeen = isNaN(lastSeenTime) ? 0 : lastSeenTime;
87
+ if (now - lastSeen > ttl) {
88
+ delete registry.agents[id];
89
+ pruned = true;
90
+ }
91
+ }
92
+
93
+ if (pruned) {
94
+ saveAgentRegistry(root, registry);
95
+ }
96
+
97
+ return registry;
98
+ }
99
+
100
+ /**
101
+ * Save the agent registry atomically (write-then-rename).
102
+ * @param {string} root - Project root
103
+ * @param {object} registry - Registry object
104
+ */
105
+ function saveAgentRegistry(root, registry) {
106
+ const registryPath = path.join(root, AGENTS_REGISTRY);
107
+ const dir = path.dirname(registryPath);
108
+ if (!fs.existsSync(dir)) {
109
+ fs.mkdirSync(dir, { recursive: true });
110
+ }
111
+ const tmpPath = registryPath + '.tmp.' + process.pid;
112
+ fs.writeFileSync(tmpPath, JSON.stringify(registry, null, 2));
113
+ fs.renameSync(tmpPath, registryPath);
114
+ }
115
+
116
+ /**
117
+ * Register or heartbeat an agent session.
118
+ * Called by session-log hooks to keep entries fresh.
119
+ * @param {string} root - Project root
120
+ * @param {object} agent - Agent info
121
+ * @param {string} agent.sessionId - Unique session/conversation ID
122
+ * @param {string} agent.platform - 'claude-code' | 'cursor' | 'unknown'
123
+ * @param {string} [agent.model] - Model name if known
124
+ * @param {string} [agent.specId] - Active spec ID if known
125
+ * @param {number} [agent.ttl] - Custom TTL in ms (default 30 min)
126
+ */
127
+ function heartbeatAgent(root, agent) {
128
+ const registry = loadAgentRegistry(root);
129
+ const existing = registry.agents[agent.sessionId] || {};
130
+
131
+ registry.agents[agent.sessionId] = {
132
+ ...existing,
133
+ sessionId: agent.sessionId,
134
+ platform: agent.platform || existing.platform || 'unknown',
135
+ model: agent.model || existing.model || null,
136
+ specId: agent.specId || existing.specId || null,
137
+ ttl: agent.ttl || existing.ttl || DEFAULT_TTL_MS,
138
+ firstSeen: existing.firstSeen || new Date().toISOString(),
139
+ lastSeen: new Date().toISOString(),
140
+ };
141
+
142
+ saveAgentRegistry(root, registry);
143
+ }
144
+
145
+ /**
146
+ * Remove an agent session from the registry.
147
+ * Called on session stop.
148
+ * @param {string} root - Project root
149
+ * @param {string} sessionId - Session to remove
150
+ */
151
+ function removeAgent(root, sessionId) {
152
+ const registry = loadAgentRegistry(root);
153
+ delete registry.agents[sessionId];
154
+ saveAgentRegistry(root, registry);
155
+ }
156
+
157
+ /**
158
+ * Find the most recently active agent for this terminal/process.
159
+ * Prefers agents that match the current environment.
160
+ * @param {object} registry - Loaded registry
161
+ * @returns {object|null} Agent entry or null
162
+ */
163
+ function findActiveAgent(registry) {
164
+ const agents = Object.values(registry.agents || {});
165
+ if (agents.length === 0) return null;
166
+
167
+ // If we're in Cursor, prefer cursor agents
168
+ const isCursor = !!process.env.CURSOR_TRACE_ID;
169
+ const preferred = agents.filter(a => {
170
+ if (isCursor) return a.platform === 'cursor';
171
+ return a.platform === 'claude-code';
172
+ });
173
+
174
+ const pool = preferred.length > 0 ? preferred : agents;
175
+
176
+ // Return most recently seen
177
+ pool.sort((a, b) => new Date(b.lastSeen) - new Date(a.lastSeen));
178
+ return pool[0];
179
+ }
180
+
181
+ /**
182
+ * List all currently active (non-expired) agents.
183
+ * @param {string} root - Project root
184
+ * @returns {object[]} Array of agent entries
185
+ */
186
+ function listActiveAgents(root) {
187
+ const registry = loadAgentRegistry(root);
188
+ return Object.values(registry.agents || {});
189
+ }
190
+
191
+ module.exports = {
192
+ getAgentSessionId,
193
+ getAgentPlatform,
194
+ loadAgentRegistry,
195
+ saveAgentRegistry,
196
+ heartbeatAgent,
197
+ removeAgent,
198
+ findActiveAgent,
199
+ listActiveAgents,
200
+ AGENTS_REGISTRY,
201
+ DEFAULT_TTL_MS,
202
+ };
@@ -33,11 +33,17 @@ function findPackageRoot(startDir = __dirname) {
33
33
  * @returns {Object} Setup information
34
34
  */
35
35
  function detectCAWSSetup(cwd = process.cwd()) {
36
- // Skip logging for version/help commands
36
+ // Skip logging for version/help/quiet commands, or when CAWS_QUIET is set.
37
+ // Verbose detection output contributes to Claude Code context-window
38
+ // exhaustion when agents run many caws commands in a single session.
37
39
  const isQuietCommand =
38
40
  process.argv.includes('--version') ||
39
41
  process.argv.includes('-V') ||
40
- process.argv.includes('--help');
42
+ process.argv.includes('--help') ||
43
+ process.argv.includes('--json') ||
44
+ process.argv.includes('--quiet') ||
45
+ process.argv.includes('-q') ||
46
+ process.env.CAWS_QUIET === '1';
41
47
 
42
48
  if (!isQuietCommand) {
43
49
  console.log(chalk.blue('Detecting CAWS setup...'));