specweave 0.21.3 → 0.22.2

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 (235) hide show
  1. package/CLAUDE.md +409 -6
  2. package/README.md +38 -8
  3. package/bin/specweave.js +5 -8
  4. package/dist/plugins/specweave-github/lib/CodeValidator.d.ts +101 -0
  5. package/dist/plugins/specweave-github/lib/CodeValidator.d.ts.map +1 -0
  6. package/dist/plugins/specweave-github/lib/CodeValidator.js +219 -0
  7. package/dist/plugins/specweave-github/lib/CodeValidator.js.map +1 -0
  8. package/dist/plugins/specweave-github/lib/ThreeLayerSyncManager.d.ts +182 -0
  9. package/dist/plugins/specweave-github/lib/ThreeLayerSyncManager.d.ts.map +1 -0
  10. package/dist/plugins/specweave-github/lib/ThreeLayerSyncManager.js +603 -0
  11. package/dist/plugins/specweave-github/lib/ThreeLayerSyncManager.js.map +1 -0
  12. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +10 -0
  13. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
  14. package/dist/plugins/specweave-github/lib/github-client-v2.js +26 -0
  15. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  16. package/dist/plugins/specweave-github/lib/task-sync.d.ts.map +1 -1
  17. package/dist/plugins/specweave-github/lib/task-sync.js +7 -0
  18. package/dist/plugins/specweave-github/lib/task-sync.js.map +1 -1
  19. package/dist/plugins/specweave-github/lib/types.d.ts +34 -0
  20. package/dist/plugins/specweave-github/lib/types.d.ts.map +1 -1
  21. package/dist/src/cli/commands/init.d.ts.map +1 -1
  22. package/dist/src/cli/commands/init.js +60 -5
  23. package/dist/src/cli/commands/init.js.map +1 -1
  24. package/dist/src/cli/commands/migrate-to-profiles.d.ts +1 -0
  25. package/dist/src/cli/commands/migrate-to-profiles.d.ts.map +1 -1
  26. package/dist/src/cli/commands/migrate-to-profiles.js +12 -1
  27. package/dist/src/cli/commands/migrate-to-profiles.js.map +1 -1
  28. package/dist/src/cli/commands/next-command.d.ts +52 -0
  29. package/dist/src/cli/commands/next-command.d.ts.map +1 -0
  30. package/dist/src/cli/commands/next-command.js +204 -0
  31. package/dist/src/cli/commands/next-command.js.map +1 -0
  32. package/dist/src/cli/commands/sync-specs.d.ts +16 -0
  33. package/dist/src/cli/commands/sync-specs.d.ts.map +1 -0
  34. package/dist/src/cli/commands/sync-specs.js +130 -0
  35. package/dist/src/cli/commands/sync-specs.js.map +1 -0
  36. package/dist/src/cli/count-tasks.d.ts +20 -0
  37. package/dist/src/cli/count-tasks.d.ts.map +1 -0
  38. package/dist/src/cli/count-tasks.js +50 -0
  39. package/dist/src/cli/count-tasks.js.map +1 -0
  40. package/dist/src/config/ConfigManager.d.ts.map +1 -1
  41. package/dist/src/config/ConfigManager.js +2 -1
  42. package/dist/src/config/ConfigManager.js.map +1 -1
  43. package/dist/src/config/types.d.ts +58 -58
  44. package/dist/src/core/cicd/state-manager.d.ts +8 -0
  45. package/dist/src/core/cicd/state-manager.d.ts.map +1 -1
  46. package/dist/src/core/cicd/state-manager.js +60 -15
  47. package/dist/src/core/cicd/state-manager.js.map +1 -1
  48. package/dist/src/core/cost-tracker.d.ts.map +1 -1
  49. package/dist/src/core/cost-tracker.js +2 -1
  50. package/dist/src/core/cost-tracker.js.map +1 -1
  51. package/dist/src/core/iac/template-engine.d.ts.map +1 -1
  52. package/dist/src/core/iac/template-engine.js +28 -0
  53. package/dist/src/core/iac/template-engine.js.map +1 -1
  54. package/dist/src/core/iac/template-generator.d.ts +53 -0
  55. package/dist/src/core/iac/template-generator.d.ts.map +1 -0
  56. package/dist/src/core/iac/template-generator.js +125 -0
  57. package/dist/src/core/iac/template-generator.js.map +1 -0
  58. package/dist/src/core/increment/status-auto-transition.js +3 -3
  59. package/dist/src/core/increment/status-auto-transition.js.map +1 -1
  60. package/dist/src/core/living-docs/CodeValidator.js +1 -1
  61. package/dist/src/core/living-docs/CodeValidator.js.map +1 -1
  62. package/dist/src/core/living-docs/CompletionPropagator.d.ts.map +1 -1
  63. package/dist/src/core/living-docs/CompletionPropagator.js +4 -3
  64. package/dist/src/core/living-docs/CompletionPropagator.js.map +1 -1
  65. package/dist/src/core/living-docs/SpecDistributor.d.ts +5 -0
  66. package/dist/src/core/living-docs/SpecDistributor.d.ts.map +1 -1
  67. package/dist/src/core/living-docs/SpecDistributor.js +12 -0
  68. package/dist/src/core/living-docs/SpecDistributor.js.map +1 -1
  69. package/dist/src/core/living-docs/content-distributor.d.ts.map +1 -1
  70. package/dist/src/core/living-docs/content-distributor.js +11 -1
  71. package/dist/src/core/living-docs/content-distributor.js.map +1 -1
  72. package/dist/src/core/living-docs/living-docs-sync.d.ts +166 -0
  73. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -0
  74. package/dist/src/core/living-docs/living-docs-sync.js +726 -0
  75. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -0
  76. package/dist/src/core/living-docs/project-detector.d.ts.map +1 -1
  77. package/dist/src/core/living-docs/project-detector.js +38 -0
  78. package/dist/src/core/living-docs/project-detector.js.map +1 -1
  79. package/dist/src/core/living-docs/task-project-specific-generator.d.ts +7 -3
  80. package/dist/src/core/living-docs/task-project-specific-generator.d.ts.map +1 -1
  81. package/dist/src/core/living-docs/task-project-specific-generator.js +40 -24
  82. package/dist/src/core/living-docs/task-project-specific-generator.js.map +1 -1
  83. package/dist/src/core/plugin-loader.d.ts +7 -0
  84. package/dist/src/core/plugin-loader.d.ts.map +1 -1
  85. package/dist/src/core/plugin-loader.js +18 -1
  86. package/dist/src/core/plugin-loader.js.map +1 -1
  87. package/dist/src/core/serverless/platform-data-loader.d.ts +8 -0
  88. package/dist/src/core/serverless/platform-data-loader.d.ts.map +1 -1
  89. package/dist/src/core/serverless/platform-data-loader.js +14 -0
  90. package/dist/src/core/serverless/platform-data-loader.js.map +1 -1
  91. package/dist/src/core/serverless/types.d.ts +1 -1
  92. package/dist/src/core/serverless/types.d.ts.map +1 -1
  93. package/dist/src/core/status-line/status-line-manager.d.ts +6 -2
  94. package/dist/src/core/status-line/status-line-manager.d.ts.map +1 -1
  95. package/dist/src/core/status-line/status-line-manager.js +11 -5
  96. package/dist/src/core/status-line/status-line-manager.js.map +1 -1
  97. package/dist/src/core/status-line/task-counter.d.ts +69 -0
  98. package/dist/src/core/status-line/task-counter.d.ts.map +1 -0
  99. package/dist/src/core/status-line/task-counter.js +107 -0
  100. package/dist/src/core/status-line/task-counter.js.map +1 -0
  101. package/dist/src/core/types/config.d.ts +23 -0
  102. package/dist/src/core/types/config.d.ts.map +1 -1
  103. package/dist/src/core/types/config.js +10 -0
  104. package/dist/src/core/types/config.js.map +1 -1
  105. package/dist/src/core/workflow/autonomous-executor.d.ts +111 -0
  106. package/dist/src/core/workflow/autonomous-executor.d.ts.map +1 -0
  107. package/dist/src/core/workflow/autonomous-executor.js +275 -0
  108. package/dist/src/core/workflow/autonomous-executor.js.map +1 -0
  109. package/dist/src/core/workflow/backlog-scanner.d.ts +94 -0
  110. package/dist/src/core/workflow/backlog-scanner.d.ts.map +1 -0
  111. package/dist/src/core/workflow/backlog-scanner.js +170 -0
  112. package/dist/src/core/workflow/backlog-scanner.js.map +1 -0
  113. package/dist/src/core/workflow/command-invoker.d.ts +86 -0
  114. package/dist/src/core/workflow/command-invoker.d.ts.map +1 -0
  115. package/dist/src/core/workflow/command-invoker.js +131 -0
  116. package/dist/src/core/workflow/command-invoker.js.map +1 -0
  117. package/dist/src/core/workflow/cost-estimator.d.ts +120 -0
  118. package/dist/src/core/workflow/cost-estimator.d.ts.map +1 -0
  119. package/dist/src/core/workflow/cost-estimator.js +222 -0
  120. package/dist/src/core/workflow/cost-estimator.js.map +1 -0
  121. package/dist/src/core/workflow/index.d.ts +20 -0
  122. package/dist/src/core/workflow/index.d.ts.map +1 -0
  123. package/dist/src/core/workflow/index.js +24 -0
  124. package/dist/src/core/workflow/index.js.map +1 -0
  125. package/dist/src/core/workflow/state-manager.d.ts +107 -0
  126. package/dist/src/core/workflow/state-manager.d.ts.map +1 -0
  127. package/dist/src/core/workflow/state-manager.js +126 -0
  128. package/dist/src/core/workflow/state-manager.js.map +1 -0
  129. package/dist/src/core/workflow/workflow-orchestrator.d.ts +93 -0
  130. package/dist/src/core/workflow/workflow-orchestrator.d.ts.map +1 -0
  131. package/dist/src/core/workflow/workflow-orchestrator.js +195 -0
  132. package/dist/src/core/workflow/workflow-orchestrator.js.map +1 -0
  133. package/dist/src/init/ArchitecturePresenter.d.ts +47 -0
  134. package/dist/src/init/ArchitecturePresenter.d.ts.map +1 -0
  135. package/dist/src/init/ArchitecturePresenter.js +180 -0
  136. package/dist/src/init/ArchitecturePresenter.js.map +1 -0
  137. package/dist/src/init/InitFlow.d.ts.map +1 -1
  138. package/dist/src/init/InitFlow.js +30 -1
  139. package/dist/src/init/InitFlow.js.map +1 -1
  140. package/dist/src/init/architecture/CostEstimator.d.ts +52 -0
  141. package/dist/src/init/architecture/CostEstimator.d.ts.map +1 -0
  142. package/dist/src/init/architecture/CostEstimator.js +107 -0
  143. package/dist/src/init/architecture/CostEstimator.js.map +1 -0
  144. package/dist/src/init/architecture/InfrastructureMapper.d.ts +41 -0
  145. package/dist/src/init/architecture/InfrastructureMapper.d.ts.map +1 -0
  146. package/dist/src/init/architecture/InfrastructureMapper.js +140 -0
  147. package/dist/src/init/architecture/InfrastructureMapper.js.map +1 -0
  148. package/dist/src/init/architecture/ProjectGenerator.d.ts +44 -0
  149. package/dist/src/init/architecture/ProjectGenerator.d.ts.map +1 -0
  150. package/dist/src/init/architecture/ProjectGenerator.js +216 -0
  151. package/dist/src/init/architecture/ProjectGenerator.js.map +1 -0
  152. package/dist/src/init/architecture/types.d.ts +10 -10
  153. package/dist/src/init/research/src/config/types.d.ts +8 -8
  154. package/dist/src/metrics/dora-calculator.js +2 -2
  155. package/dist/src/metrics/dora-calculator.js.map +1 -1
  156. package/dist/src/utils/pricing-constants.d.ts +5 -2
  157. package/dist/src/utils/pricing-constants.d.ts.map +1 -1
  158. package/dist/src/utils/pricing-constants.js +3 -2
  159. package/dist/src/utils/pricing-constants.js.map +1 -1
  160. package/package.json +9 -8
  161. package/plugins/specweave/agents/infrastructure/AGENT.md +88 -46
  162. package/plugins/specweave/agents/pm/AGENT.md +58 -1
  163. package/plugins/specweave/commands/specweave-archive-features.md +1 -1
  164. package/plugins/specweave/commands/specweave-archive-increments.md +1 -1
  165. package/plugins/specweave/commands/specweave-check-hooks.md +5 -0
  166. package/plugins/specweave/commands/specweave-done.md +12 -0
  167. package/plugins/specweave/commands/specweave-plan.md +1 -1
  168. package/plugins/specweave/commands/specweave-progress.md +108 -379
  169. package/plugins/specweave/commands/specweave-reopen.md +1 -1
  170. package/plugins/specweave/commands/specweave-restore-feature.md +1 -1
  171. package/plugins/specweave/commands/specweave-sync-specs.md +20 -48
  172. package/plugins/specweave/hooks/lib/update-status-line.sh +44 -35
  173. package/plugins/specweave/hooks/lib/validate-spec-status.sh +163 -0
  174. package/plugins/specweave/hooks/user-prompt-submit.sh +17 -35
  175. package/plugins/specweave/lib/hooks/update-tasks-md.js +52 -9
  176. package/plugins/specweave/lib/hooks/update-tasks-md.ts +77 -16
  177. package/plugins/specweave/templates/iac/aws-lambda/defaults.json +24 -0
  178. package/plugins/specweave/templates/iac/aws-lambda/templates/README.md.hbs +260 -0
  179. package/plugins/specweave/templates/iac/aws-lambda/templates/environments/dev.tfvars.hbs +34 -0
  180. package/plugins/specweave/templates/iac/aws-lambda/templates/environments/prod.tfvars.hbs +37 -0
  181. package/plugins/specweave/templates/iac/aws-lambda/templates/environments/staging.tfvars.hbs +35 -0
  182. package/plugins/specweave/templates/iac/aws-lambda/templates/outputs.tf.hbs +77 -0
  183. package/plugins/specweave/templates/iac/aws-lambda/templates/providers.tf.hbs +36 -0
  184. package/plugins/specweave/templates/iac/aws-lambda/templates/variables.tf.hbs +115 -0
  185. package/plugins/specweave/templates/iac/azure-functions/defaults.json +25 -0
  186. package/plugins/specweave/templates/iac/azure-functions/templates/README.md.hbs +268 -0
  187. package/plugins/specweave/templates/iac/azure-functions/templates/environments/dev.tfvars.hbs +34 -0
  188. package/plugins/specweave/templates/iac/azure-functions/templates/environments/prod.tfvars.hbs +46 -0
  189. package/plugins/specweave/templates/iac/azure-functions/templates/environments/staging.tfvars.hbs +34 -0
  190. package/plugins/specweave/templates/iac/azure-functions/templates/main.tf.hbs +225 -0
  191. package/plugins/specweave/templates/iac/azure-functions/templates/outputs.tf.hbs +89 -0
  192. package/plugins/specweave/templates/iac/azure-functions/templates/provider.tf.hbs +27 -0
  193. package/plugins/specweave/templates/iac/azure-functions/templates/providers.tf.hbs +35 -0
  194. package/plugins/specweave/templates/iac/azure-functions/templates/variables.tf.hbs +124 -0
  195. package/plugins/specweave/templates/iac/firebase/defaults.json +29 -0
  196. package/plugins/specweave/templates/iac/firebase/templates/README.md.hbs +35 -0
  197. package/plugins/specweave/templates/iac/firebase/templates/environments/dev.tfvars.hbs +7 -0
  198. package/plugins/specweave/templates/iac/firebase/templates/environments/prod.tfvars.hbs +7 -0
  199. package/plugins/specweave/templates/iac/firebase/templates/environments/staging.tfvars.hbs +7 -0
  200. package/plugins/specweave/templates/iac/firebase/templates/main.tf.hbs +90 -0
  201. package/plugins/specweave/templates/iac/firebase/templates/outputs.tf.hbs +15 -0
  202. package/plugins/specweave/templates/iac/firebase/templates/providers.tf.hbs +23 -0
  203. package/plugins/specweave/templates/iac/firebase/templates/variables.tf.hbs +42 -0
  204. package/plugins/specweave/templates/iac/gcp-cloud-functions/defaults.json +26 -0
  205. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/README.md.hbs +299 -0
  206. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/environments/dev.tfvars.hbs +36 -0
  207. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/environments/prod.tfvars.hbs +48 -0
  208. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/environments/staging.tfvars.hbs +41 -0
  209. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/main.tf.hbs +192 -0
  210. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/outputs.tf.hbs +66 -0
  211. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/providers.tf.hbs +25 -0
  212. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/variables.tf.hbs +119 -0
  213. package/plugins/specweave/templates/iac/supabase/defaults.json +15 -0
  214. package/plugins/specweave/templates/iac/supabase/templates/README.md.hbs +46 -0
  215. package/plugins/specweave/templates/iac/supabase/templates/main.tf.hbs +50 -0
  216. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
  217. package/plugins/specweave-github/agents/github-manager/AGENT.md +39 -7
  218. package/plugins/specweave-github/commands/specweave-github-create-issue.md +5 -5
  219. package/plugins/specweave-github/lib/CodeValidator.js +195 -0
  220. package/plugins/specweave-github/lib/CodeValidator.ts +284 -0
  221. package/plugins/specweave-github/lib/ThreeLayerSyncManager.js +545 -0
  222. package/plugins/specweave-github/lib/ThreeLayerSyncManager.ts +809 -0
  223. package/plugins/specweave-github/lib/github-client-v2.js +29 -0
  224. package/plugins/specweave-github/lib/github-client-v2.ts +30 -0
  225. package/plugins/specweave-github/lib/task-sync.js +4 -0
  226. package/plugins/specweave-github/lib/task-sync.ts +7 -0
  227. package/plugins/specweave-github/lib/types.ts +38 -0
  228. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
  229. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +3222 -0
  230. package/src/templates/AGENTS.md.template +22 -1
  231. package/src/templates/CLAUDE.md.template +31 -0
  232. package/dist/src/core/living-docs/ThreeLayerSyncManager.d.ts +0 -116
  233. package/dist/src/core/living-docs/ThreeLayerSyncManager.d.ts.map +0 -1
  234. package/dist/src/core/living-docs/ThreeLayerSyncManager.js +0 -356
  235. package/dist/src/core/living-docs/ThreeLayerSyncManager.js.map +0 -1
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: specweave:sync-specs
3
- description: Sync only specs folder to living docs - distributes increment specs into hierarchical user stories without updating architecture, operations, or other docs
3
+ description: Sync increment specifications to living docs structure. Auto-generates feature IDs for greenfield increments (FS-XXX). Use after completing an increment to make it visible in living docs.
4
4
  ---
5
5
 
6
- # Sync Specs Only
6
+ # Sync Increment Specifications to Living Docs
7
7
 
8
- You are executing the SpecWeave specs-only sync command. This distributes increment specs specifically into the living docs specs folder using the Universal Hierarchy (Epic Feature → User Story → Task), without touching other documentation areas.
8
+ Syncs increment specs to living docs structure for stakeholder visibility. Auto-generates feature IDs for greenfield increments.
9
9
 
10
10
  ---
11
11
 
@@ -85,58 +85,30 @@ fi
85
85
 
86
86
  ---
87
87
 
88
- ## STEP 3: Execute Spec Distribution
88
+ ## STEP 3: Execute Spec Sync
89
89
 
90
- ### 3.1 Run SpecDistributor
90
+ ### 3.1 Run Sync Command
91
91
 
92
- **Execute the distribution using Node.js**:
92
+ **Execute the sync using the CLI command**:
93
93
 
94
- ```javascript
95
- // Direct execution via Node.js
96
- node -e "
97
- import('./dist/src/core/living-docs/spec-distributor.js').then(async ({ SpecDistributor }) => {
98
- const distributor = new SpecDistributor(process.cwd());
94
+ ```typescript
95
+ import { syncSpecs } from './dist/src/cli/commands/sync-specs.js';
99
96
 
100
- console.log('🚀 Starting spec distribution...');
101
- console.log(' 📄 Reading spec.md from increment ${INCREMENT_ID}');
97
+ // Parse arguments
98
+ const args = process.argv.slice(2); // e.g., ['0040', '--dry-run']
102
99
 
103
- try {
104
- const result = await distributor.distribute('${INCREMENT_ID}');
105
-
106
- console.log('');
107
- console.log('✅ Distribution successful!');
108
- console.log(' 📊 Total stories: ' + result.totalStories);
109
- console.log(' 📁 Total files created: ' + result.totalFiles);
110
- console.log(' 🎯 Feature ID: ' + result.specId);
111
-
112
- if (result.epicPath) {
113
- console.log(' 📂 Feature path: ' + result.epicPath);
114
- }
115
-
116
- if (result.userStoryPaths && result.userStoryPaths.length > 0) {
117
- console.log(' 📝 User stories created:');
118
- result.userStoryPaths.forEach(p => console.log(' • ' + p));
119
- }
120
-
121
- if (result.warnings && result.warnings.length > 0) {
122
- console.log('');
123
- console.log('⚠️ Warnings:');
124
- result.warnings.forEach(w => console.log(' • ' + w));
125
- }
126
-
127
- // Update acceptance criteria status based on completed tasks
128
- console.log('');
129
- console.log('📊 Updating acceptance criteria status...');
130
- await distributor.updateAcceptanceCriteriaStatus('${INCREMENT_ID}');
131
-
132
- } catch (error) {
133
- console.error('❌ Distribution failed:', error.message);
134
- process.exit(1);
135
- }
136
- });
137
- "
100
+ // Execute sync
101
+ await syncSpecs(args);
138
102
  ```
139
103
 
104
+ **This will**:
105
+ 1. Auto-generate feature ID for greenfield increments (FS-040, FS-041, etc.)
106
+ 2. Parse spec.md for user stories and acceptance criteria
107
+ 3. Create living docs structure:
108
+ - `.specweave/docs/internal/specs/_features/FS-XXX/FEATURE.md`
109
+ - `.specweave/docs/internal/specs/specweave/FS-XXX/README.md`
110
+ - `.specweave/docs/internal/specs/specweave/FS-XXX/us-*.md`
111
+
140
112
  ---
141
113
 
142
114
  ## STEP 4: Report Distribution Results
@@ -6,9 +6,9 @@
6
6
  # Shows: [increment-name] ████░░░░ X/Y tasks (Z open)
7
7
  #
8
8
  # Logic:
9
- # 1. Scan all metadata.json for status=active/in-progress/planning
9
+ # 1. Scan all spec.md for status=active/planning (SOURCE OF TRUTH!)
10
10
  # 2. Take first (oldest) as current increment
11
- # 3. Count all active/in-progress/planning as openCount
11
+ # 3. Count all active/planning as openCount
12
12
  # 4. Parse current increment's tasks.md for progress
13
13
  # 5. Write to cache
14
14
  #
@@ -37,19 +37,24 @@ TMP_FILE="$PROJECT_ROOT/.specweave/state/.status-line-tmp.txt"
37
37
  # Ensure state directory exists
38
38
  mkdir -p "$PROJECT_ROOT/.specweave/state"
39
39
 
40
- # Step 1: Find all open increments (active/in-progress/planning)
40
+ # Step 1: Find all open increments (active/planning)
41
+ # Read from spec.md (source of truth), not metadata.json
42
+ # ONLY accepts official IncrementStatus enum values
41
43
  # Write to temp file: "timestamp increment_id"
42
44
  > "$TMP_FILE"
43
45
 
44
46
  if [[ -d "$INCREMENTS_DIR" ]]; then
45
- for metadata in "$INCREMENTS_DIR"/*/metadata.json; do
46
- if [[ -f "$metadata" ]]; then
47
- status=$(jq -r '.status // ""' "$metadata" 2>/dev/null || echo "")
48
-
49
- # Check if increment is open (active, in-progress, or planning)
50
- if [[ "$status" == "active" ]] || [[ "$status" == "in-progress" ]] || [[ "$status" == "planning" ]]; then
51
- increment_id=$(basename "$(dirname "$metadata")")
52
- created=$(jq -r '.created // ""' "$metadata" 2>/dev/null || echo "1970-01-01T00:00:00Z")
47
+ for spec_file in "$INCREMENTS_DIR"/*/spec.md; do
48
+ if [[ -f "$spec_file" ]]; then
49
+ # Parse YAML frontmatter for status (source of truth)
50
+ status=$(grep -m1 "^status:" "$spec_file" 2>/dev/null | cut -d: -f2 | tr -d ' ' || echo "")
51
+
52
+ # Check if increment is open (active, planning, or in-progress)
53
+ # ONLY accepts official IncrementStatus enum values
54
+ if [[ "$status" == "active" ]] || [[ "$status" == "planning" ]] || [[ "$status" == "in-progress" ]]; then
55
+ increment_id=$(basename "$(dirname "$spec_file")")
56
+ # Parse created date from spec.md YAML frontmatter
57
+ created=$(grep -m1 "^created:" "$spec_file" 2>/dev/null | cut -d: -f2- | tr -d ' ' || echo "1970-01-01")
53
58
 
54
59
  # Write to temp file
55
60
  echo "$created $increment_id" >> "$TMP_FILE"
@@ -85,33 +90,37 @@ COMPLETED_TASKS=0
85
90
  PERCENTAGE=0
86
91
 
87
92
  if [[ -f "$TASKS_FILE" ]]; then
88
- # Count total tasks (## T- or ### T- headings)
89
- TOTAL_TASKS=$(grep -cE '^##+ T-' "$TASKS_FILE" 2>/dev/null || echo 0)
90
- TOTAL_TASKS=$(echo "$TOTAL_TASKS" | tr -d '\n\r ' || echo 0)
91
-
92
- # Count completed tasks (multiple formats)
93
- # Format 1: [x] at line start (legacy)
94
- COMPLETED_STANDARD=$(grep -c '^\[x\]' "$TASKS_FILE" 2>/dev/null || echo 0)
95
- COMPLETED_STANDARD=$(echo "$COMPLETED_STANDARD" | tr -d '\n\r ' || echo 0)
96
-
97
- # Format 2: **Status**: [x] inline (legacy)
98
- COMPLETED_INLINE=$(grep -c '\*\*Status\*\*: \[x\]' "$TASKS_FILE" 2>/dev/null || echo 0)
99
- COMPLETED_INLINE=$(echo "$COMPLETED_INLINE" | tr -d '\n\r ' || echo 0)
100
-
101
- # Format 3: **Completed**: <date> (current format)
102
- COMPLETED_DATE=$(grep -c '\*\*Completed\*\*:' "$TASKS_FILE" 2>/dev/null || echo 0)
103
- COMPLETED_DATE=$(echo "$COMPLETED_DATE" | tr -d '\n\r ' || echo 0)
104
-
105
- COMPLETED_TASKS=$((COMPLETED_STANDARD + COMPLETED_INLINE + COMPLETED_DATE))
106
-
107
- # Calculate percentage
108
- if [[ $TOTAL_TASKS -gt 0 ]]; then
109
- PERCENTAGE=$((COMPLETED_TASKS * 100 / TOTAL_TASKS))
93
+ # Use TaskCounter CLI for accurate counting (fixes overcounting bug)
94
+ COUNT_TASKS_CLI="$PROJECT_ROOT/dist/src/cli/count-tasks.js"
95
+
96
+ if [[ -f "$COUNT_TASKS_CLI" ]]; then
97
+ # Call CLI and parse JSON output
98
+ TASK_COUNTS=$(node "$COUNT_TASKS_CLI" "$TASKS_FILE" 2>/dev/null || echo '{"total":0,"completed":0,"percentage":0}')
99
+ TOTAL_TASKS=$(echo "$TASK_COUNTS" | jq -r '.total' 2>/dev/null || echo 0)
100
+ COMPLETED_TASKS=$(echo "$TASK_COUNTS" | jq -r '.completed' 2>/dev/null || echo 0)
101
+ PERCENTAGE=$(echo "$TASK_COUNTS" | jq -r '.percentage' 2>/dev/null || echo 0)
102
+ else
103
+ # Fallback to legacy counting if CLI not available (graceful degradation)
104
+ # Count total tasks (## T- or ### T- headings)
105
+ TOTAL_TASKS=$(grep -cE '^##+ T-' "$TASKS_FILE" 2>/dev/null || echo 0)
106
+ TOTAL_TASKS=$(echo "$TOTAL_TASKS" | tr -d '\n\r ' || echo 0)
107
+
108
+ # Count completed tasks - use most reliable single marker (**Completed**: format)
109
+ COMPLETED_TASKS=$(grep -c '\*\*Completed\*\*:' "$TASKS_FILE" 2>/dev/null || echo 0)
110
+ COMPLETED_TASKS=$(echo "$COMPLETED_TASKS" | tr -d '\n\r ' || echo 0)
111
+
112
+ # Calculate percentage
113
+ if [[ $TOTAL_TASKS -gt 0 ]]; then
114
+ PERCENTAGE=$((COMPLETED_TASKS * 100 / TOTAL_TASKS))
115
+ fi
110
116
  fi
111
117
  fi
112
118
 
113
- # Step 5: Extract increment name (remove 4-digit prefix)
114
- INCREMENT_NAME=$(echo "$CURRENT_INCREMENT" | sed 's/^[0-9]\{4\}-//')
119
+ # Step 5: Extract increment ID and name
120
+ # Format: XXXX-name where XXXX is 4-digit prefix (brackets added by manager)
121
+ INCREMENT_ID=$(echo "$CURRENT_INCREMENT" | grep -oE '^[0-9]{4}')
122
+ INCREMENT_NAME_ONLY=$(echo "$CURRENT_INCREMENT" | sed 's/^[0-9]\{4\}-//')
123
+ INCREMENT_NAME="$INCREMENT_ID-$INCREMENT_NAME_ONLY"
115
124
 
116
125
  # Step 6: Write cache
117
126
  jq -n \
@@ -0,0 +1,163 @@
1
+ #!/bin/bash
2
+ #
3
+ # validate-spec-status.sh
4
+ #
5
+ # Validates that spec.md status values match IncrementStatus enum.
6
+ # Prevents vocabulary drift and ensures status line hook compatibility.
7
+ #
8
+ # Usage:
9
+ # bash validate-spec-status.sh <increment-id>
10
+ # bash validate-spec-status.sh --all # Validate all increments
11
+ #
12
+ # Exit codes:
13
+ # 0 = Valid
14
+ # 1 = Invalid status found
15
+ # 2 = Error (file not found, etc.)
16
+ #
17
+ # Requires: bash 4.0+ (for associative arrays)
18
+
19
+ set -euo pipefail
20
+
21
+ # Find project root
22
+ find_project_root() {
23
+ local dir="$PWD"
24
+ while [[ "$dir" != "/" ]]; do
25
+ if [[ -d "$dir/.specweave" ]]; then
26
+ echo "$dir"
27
+ return 0
28
+ fi
29
+ dir=$(dirname "$dir")
30
+ done
31
+ echo "$PWD"
32
+ }
33
+
34
+ PROJECT_ROOT=$(find_project_root)
35
+ INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
36
+
37
+ # Valid IncrementStatus enum values (from src/core/types/increment-metadata.ts)
38
+ VALID_STATUSES=("planning" "active" "backlog" "paused" "completed" "abandoned")
39
+
40
+ # Get suggested correction for invalid status
41
+ get_correction() {
42
+ local status="$1"
43
+ case "$status" in
44
+ "planned") echo "planning" ;;
45
+ "in-progress"|"in_progress") echo "active" ;;
46
+ "done") echo "completed" ;;
47
+ "cancelled"|"canceled") echo "abandoned" ;;
48
+ *) echo "" ;;
49
+ esac
50
+ }
51
+
52
+ # Validate a single spec file
53
+ validate_spec() {
54
+ local spec_file="$1"
55
+ local increment_id=$(basename "$(dirname "$spec_file")")
56
+
57
+ if [[ ! -f "$spec_file" ]]; then
58
+ echo "❌ Error: spec.md not found for increment $increment_id"
59
+ return 2
60
+ fi
61
+
62
+ # Extract status from YAML frontmatter
63
+ local status=$(grep -m1 "^status:" "$spec_file" 2>/dev/null | cut -d: -f2 | tr -d ' "' || echo "")
64
+
65
+ if [[ -z "$status" ]]; then
66
+ echo "⚠️ Warning: No status field in $increment_id/spec.md"
67
+ return 0 # Not an error, just a warning
68
+ fi
69
+
70
+ # Check if status is valid
71
+ local is_valid=0
72
+ for valid_status in "${VALID_STATUSES[@]}"; do
73
+ if [[ "$status" == "$valid_status" ]]; then
74
+ is_valid=1
75
+ break
76
+ fi
77
+ done
78
+
79
+ if [[ $is_valid -eq 1 ]]; then
80
+ echo "✅ $increment_id: status '$status' is valid"
81
+ return 0
82
+ else
83
+ # Invalid status found - suggest correction
84
+ echo ""
85
+ echo "❌ Invalid status in $increment_id/spec.md"
86
+ echo " Found: '$status'"
87
+ echo ""
88
+
89
+ # Check if we have a suggested correction
90
+ local correction=$(get_correction "$status")
91
+ if [[ -n "$correction" ]]; then
92
+ echo " 💡 Did you mean: '$correction'?"
93
+ echo ""
94
+ echo " Fix:"
95
+ echo " sed -i '' 's/^status: $status/status: $correction/' $spec_file"
96
+ else
97
+ echo " Valid statuses: ${VALID_STATUSES[*]}"
98
+ fi
99
+ echo ""
100
+
101
+ return 1
102
+ fi
103
+ }
104
+
105
+ # Validate all increments
106
+ validate_all() {
107
+ local exit_code=0
108
+ local total=0
109
+ local valid=0
110
+ local invalid=0
111
+
112
+ echo "Validating all increments in $INCREMENTS_DIR"
113
+ echo ""
114
+
115
+ for spec_file in "$INCREMENTS_DIR"/*/spec.md; do
116
+ if [[ -f "$spec_file" ]]; then
117
+ total=$((total + 1))
118
+ if validate_spec "$spec_file"; then
119
+ valid=$((valid + 1))
120
+ else
121
+ invalid=$((invalid + 1))
122
+ exit_code=1
123
+ fi
124
+ fi
125
+ done
126
+
127
+ echo ""
128
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
129
+ echo "Summary: $total increments checked"
130
+ echo " ✅ Valid: $valid"
131
+ echo " ❌ Invalid: $invalid"
132
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
133
+
134
+ if [[ $invalid -gt 0 ]]; then
135
+ echo ""
136
+ echo "⚠️ Please fix invalid status values to use official IncrementStatus enum."
137
+ echo ""
138
+ echo "Valid statuses:"
139
+ for status in "${VALID_STATUSES[@]}"; do
140
+ echo " - $status"
141
+ done
142
+ fi
143
+
144
+ return $exit_code
145
+ }
146
+
147
+ # Main logic
148
+ if [[ $# -eq 0 ]]; then
149
+ echo "Usage: $0 <increment-id> | --all"
150
+ echo ""
151
+ echo "Examples:"
152
+ echo " $0 0042-test-infrastructure-cleanup"
153
+ echo " $0 --all"
154
+ exit 2
155
+ fi
156
+
157
+ if [[ "$1" == "--all" ]]; then
158
+ validate_all
159
+ else
160
+ INCREMENT_ID="$1"
161
+ SPEC_FILE="$INCREMENTS_DIR/$INCREMENT_ID/spec.md"
162
+ validate_spec "$SPEC_FILE"
163
+ fi
@@ -303,44 +303,26 @@ if [[ -d ".specweave/increments" ]]; then
303
303
  done)
304
304
 
305
305
  if [[ -n "$ACTIVE_INCREMENT" ]]; then
306
- # Use status line cache for consistent display
307
- STATUS_LINE_CACHE=".specweave/state/status-line.json"
308
- if [[ -f "$STATUS_LINE_CACHE" ]]; then
309
- # Get status line from cache (ultra-fast)
310
- STATUS_LINE=$(node -e "
311
- try {
312
- const { StatusLineManager } = require('./dist/src/core/status-line/status-line-manager.js');
313
- const manager = new StatusLineManager(process.cwd());
314
- const result = manager.render();
315
- console.log(result || '');
316
- } catch (e) {
317
- // Fallback: show increment name only
318
- console.log('');
319
- }
320
- " 2>/dev/null || echo "")
321
-
322
- if [[ -n "$STATUS_LINE" ]]; then
323
- CONTEXT="✓ $STATUS_LINE"
306
+ # Simple status: parse tasks.md for completion
307
+ TASKS_FILE=".specweave/increments/$ACTIVE_INCREMENT/tasks.md"
308
+ if [[ -f "$TASKS_FILE" ]]; then
309
+ # Count tasks (headers with T-NNN format - both ### and ####)
310
+ TOTAL_TASKS=$(grep -cE '^#{3,4}\s*T-[0-9]' "$TASKS_FILE" 2>/dev/null | tr -d '\n' || echo "0")
311
+ # Count completed (various formats)
312
+ COMPLETED_TASKS=$(grep -cE '(✅ COMPLETE|\[COMPLETED\]|\[x\] Completed)' "$TASKS_FILE" 2>/dev/null | tr -d '\n' || echo "0")
313
+
314
+ # Ensure valid numbers
315
+ TOTAL_TASKS=${TOTAL_TASKS:-0}
316
+ COMPLETED_TASKS=${COMPLETED_TASKS:-0}
317
+
318
+ if [[ "$TOTAL_TASKS" -gt 0 ]] 2>/dev/null; then
319
+ PERCENTAGE=$(( COMPLETED_TASKS * 100 / TOTAL_TASKS ))
320
+ CONTEXT=" Active: $ACTIVE_INCREMENT ($COMPLETED_TASKS/$TOTAL_TASKS tasks, $PERCENTAGE%)"
324
321
  else
325
- # Fallback: parse tasks.md manually
326
- TASKS_FILE=".specweave/increments/$ACTIVE_INCREMENT/tasks.md"
327
- if [[ -f "$TASKS_FILE" ]]; then
328
- TASK_STATS=$(grep -E "^\s*-\s*\[[ x]\]" "$TASKS_FILE" 2>/dev/null | wc -l | xargs || echo "0")
329
- COMPLETED_TASKS=$(grep -E "^\s*-\s*\[x\]" "$TASKS_FILE" 2>/dev/null | wc -l | xargs || echo "0")
330
-
331
- if [[ "$TASK_STATS" -gt 0 ]]; then
332
- PERCENTAGE=$(( COMPLETED_TASKS * 100 / TASK_STATS ))
333
- CONTEXT="✓ Active Increment: $ACTIVE_INCREMENT ($PERCENTAGE% complete, $COMPLETED_TASKS/$TASK_STATS tasks)"
334
- else
335
- CONTEXT="✓ Active Increment: $ACTIVE_INCREMENT"
336
- fi
337
- else
338
- CONTEXT="✓ Active Increment: $ACTIVE_INCREMENT"
339
- fi
322
+ CONTEXT="✓ Active: $ACTIVE_INCREMENT"
340
323
  fi
341
324
  else
342
- # No cache, fall back to increment name only
343
- CONTEXT="✓ Active Increment: $ACTIVE_INCREMENT"
325
+ CONTEXT="✓ Active: $ACTIVE_INCREMENT"
344
326
  fi
345
327
  fi
346
328
  fi
@@ -20,13 +20,22 @@ async function updateTasksMd(incrementId) {
20
20
  console.log(`\u{1F4D6} Read tasks.md (${lines.length} lines)`);
21
21
  const tasks = parseTaskStatus(lines);
22
22
  console.log(`\u{1F4CB} Found ${tasks.length} tasks`);
23
- const completedTasks = detectCompletedTasks(lines);
24
- if (completedTasks.length === 0) {
25
- console.log("\u2705 No new task completions detected");
23
+ const { completedTasks, fixes } = detectCompletedTasks(lines);
24
+ let updatedContent = originalContent;
25
+ let autoFixedCount = 0;
26
+ if (fixes.length > 0) {
27
+ console.log(`\u{1F527} Auto-fixing ${fixes.length} task consistency issue(s)...`);
28
+ updatedContent = applyConsistencyFixes(originalContent, fixes);
29
+ autoFixedCount = fixes.length;
30
+ console.log("\u2705 Task consistency auto-fixed");
31
+ }
32
+ if (completedTasks.length === 0 && fixes.length === 0) {
33
+ console.log("\u2705 No new task completions or consistency fixes needed");
26
34
  return;
27
35
  }
28
- console.log(`\u{1F3AF} Detected ${completedTasks.length} completed task(s):`, completedTasks);
29
- let updatedContent = originalContent;
36
+ if (completedTasks.length > 0) {
37
+ console.log(`\u{1F3AF} Detected ${completedTasks.length} completed task(s):`, completedTasks);
38
+ }
30
39
  for (const taskId of completedTasks) {
31
40
  updatedContent = markTaskComplete(updatedContent, taskId);
32
41
  }
@@ -38,6 +47,9 @@ async function updateTasksMd(incrementId) {
38
47
  updatedContent = updateProgressHeader(updatedContent, completedCount, totalTasks, progress);
39
48
  await fs.writeFile(tasksPath, updatedContent, "utf-8");
40
49
  console.log(`\u2705 Updated ${tasksPath}`);
50
+ if (autoFixedCount > 0) {
51
+ console.log(`\u{1F527} Auto-fixed ${autoFixedCount} consistency issue(s)`);
52
+ }
41
53
  console.log(` Completed: ${completedCount}/${totalTasks}`);
42
54
  console.log(` Progress: ${progress}%
43
55
  `);
@@ -78,6 +90,7 @@ function parseTaskStatus(lines) {
78
90
  }
79
91
  function detectCompletedTasks(lines) {
80
92
  const completedTasks = [];
93
+ const fixes = [];
81
94
  const warnings = [];
82
95
  const taskPattern = /^###\s+(T-\d+[-A-Z]*):?\s+(.+)/;
83
96
  for (let i = 0; i < lines.length; i++) {
@@ -99,17 +112,33 @@ function detectCompletedTasks(lines) {
99
112
  completedTasks.push(taskId);
100
113
  }
101
114
  } else {
115
+ fixes.push({
116
+ taskId,
117
+ lineNumber: i,
118
+ action: "remove-complete-marker",
119
+ currentLine: line
120
+ });
102
121
  warnings.push(`${taskId}: Header has \u2705 COMPLETE but not all checkboxes checked`);
103
122
  }
104
123
  continue;
105
124
  }
106
125
  if (hasCompleteMarker && !implementationSection) {
107
- if (!completedTasks.includes(taskId)) {
108
- completedTasks.push(taskId);
109
- }
126
+ fixes.push({
127
+ taskId,
128
+ lineNumber: i,
129
+ action: "remove-complete-marker",
130
+ currentLine: line
131
+ });
132
+ warnings.push(`${taskId}: Header has \u2705 COMPLETE but no implementation section to verify`);
110
133
  continue;
111
134
  }
112
135
  if (!hasCompleteMarker && implementationSection && allCheckboxesComplete) {
136
+ fixes.push({
137
+ taskId,
138
+ lineNumber: i,
139
+ action: "add-complete-marker",
140
+ currentLine: line
141
+ });
113
142
  warnings.push(`${taskId}: All checkboxes checked but header missing \u2705 COMPLETE`);
114
143
  if (!completedTasks.includes(taskId)) {
115
144
  completedTasks.push(taskId);
@@ -131,7 +160,21 @@ function detectCompletedTasks(lines) {
131
160
  warnings.forEach((w) => console.warn(` ${w}`));
132
161
  console.warn("");
133
162
  }
134
- return completedTasks;
163
+ return { completedTasks, fixes };
164
+ }
165
+ function applyConsistencyFixes(content, fixes) {
166
+ const lines = content.split("\n");
167
+ for (const fix of fixes.reverse()) {
168
+ const line = lines[fix.lineNumber];
169
+ if (fix.action === "remove-complete-marker") {
170
+ const fixed = line.replace(/\s*✅\s*COMPLETE\s*/g, "");
171
+ lines[fix.lineNumber] = fixed;
172
+ } else if (fix.action === "add-complete-marker") {
173
+ const fixed = line.trim() + " \u2705 COMPLETE";
174
+ lines[fix.lineNumber] = fixed;
175
+ }
176
+ }
177
+ return lines.join("\n");
135
178
  }
136
179
  function findNextTaskStart(lines, startIndex) {
137
180
  const taskPattern = /^###\s+T-\d+/;