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,6 +1,38 @@
1
1
  # GitHub Manager Agent
2
2
 
3
- **Role**: GitHub integration specialist for SpecWeave increments
3
+ ⚠️ **DEPRECATED: Use Living Docs Sync Instead** ⚠️
4
+
5
+ **CRITICAL**: This agent creates GitHub issues using the OLD `[Increment XXXX]` format,
6
+ which violates SpecWeave's data flow architecture.
7
+
8
+ **CORRECT DATA FLOW**:
9
+ ```
10
+ Increment → Living Docs → GitHub
11
+ (source of truth)
12
+ ```
13
+
14
+ **USE INSTEAD**:
15
+ - `/specweave:sync-docs update` - Generate living docs from increments
16
+ - `/specweave-github:sync` - Sync living docs to GitHub Issues
17
+ - Result: Issues use correct `US-XXX` or `FS-YY-MM-DD` format
18
+
19
+ **WHY THIS IS DEPRECATED**:
20
+ 1. Creates issues with `[Increment XXXX]` format (rejected by validation)
21
+ 2. Bypasses living docs (source of truth)
22
+ 3. No traceability to User Stories/Feature Specs
23
+ 4. Cannot sync bidirectionally with living docs
24
+
25
+ **IF YOU USE THIS AGENT**: GitHub client will REJECT issue creation with error:
26
+ ```
27
+ ❌ DEPRECATED FORMAT DETECTED: "[Increment 0043] Title"
28
+ GitHub issues MUST use living docs format:
29
+ ✅ CORRECT: "US-XXX: Title" (User Story)
30
+ ✅ CORRECT: "FS-YY-MM-DD: Title" (Feature Spec)
31
+ ```
32
+
33
+ ---
34
+
35
+ **Role**: GitHub integration specialist for SpecWeave increments (DEPRECATED)
4
36
 
5
37
  **Expertise**: GitHub CLI, GitHub API, issue management, project boards, automation, webhooks, Actions
6
38
 
@@ -143,9 +175,9 @@ Invoke the github-manager agent (via Task tool) for:
143
175
  ### Issue Management
144
176
 
145
177
  ```bash
146
- # Create issue
178
+ # ❌ DEPRECATED - Create issue (OLD FORMAT - DO NOT USE)
147
179
  gh issue create \
148
- --title "[Increment 0004] Plugin Architecture" \
180
+ --title "[Increment 0004] Plugin Architecture" \ # ❌ DEPRECATED
149
181
  --body "$(cat issue-body.md)" \
150
182
  --label "specweave,increment,P1" \
151
183
  --milestone "v0.4.0"
@@ -384,10 +416,10 @@ When a new increment is created:
384
416
  - Parse `tasks.md` for task checklist
385
417
  - Format using issue template
386
418
 
387
- 4. **Create GitHub Issue**
419
+ 4. **Create GitHub Issue** (❌ DEPRECATED - DO NOT USE)
388
420
  ```bash
389
421
  gh issue create \
390
- --title "[Increment 0004] Plugin Architecture" \
422
+ --title "[Increment 0004] Plugin Architecture" \ # ❌ DEPRECATED
391
423
  --body "$(cat /tmp/issue-body.md)" \
392
424
  --label "specweave,increment,P1" \
393
425
  --milestone "v0.4.0"
@@ -680,9 +712,9 @@ GitHub Manager Agent:
680
712
  5. Updating metadata...
681
713
  ✓ Saved to .metadata.yaml
682
714
 
683
- ✅ GitHub Issue Created!
715
+ ✅ GitHub Issue Created! (❌ DEPRECATED FORMAT)
684
716
 
685
- Issue #130: [Increment 0004] Plugin Architecture
717
+ Issue #130: [Increment 0004] Plugin Architecture # ❌ DEPRECATED
686
718
  URL: https://github.com/owner/repo/issues/130
687
719
 
688
720
  You can now:
@@ -175,9 +175,9 @@ Creating issue...
175
175
  ✓ Milestone set: v0.4.0
176
176
  ✓ Metadata updated
177
177
 
178
- ✅ GitHub Issue Created!
178
+ ✅ GitHub Issue Created! (❌ DEPRECATED FORMAT)
179
179
 
180
- Issue #130: [Increment 0004] Plugin Architecture
180
+ Issue #130: [Increment 0004] Plugin Architecture # ❌ DEPRECATED
181
181
  URL: https://github.com/owner/repo/issues/130
182
182
 
183
183
  Auto-sync enabled: progress will update automatically after each task.
@@ -274,15 +274,15 @@ Preview issue body before creating:
274
274
 
275
275
  Output:
276
276
  ```
277
- 📄 Preview: Issue body for increment 0004
277
+ 📄 Preview: Issue body for increment 0004 (❌ DEPRECATED FORMAT)
278
278
 
279
- Title: [Increment 0004] Plugin Architecture
279
+ Title: [Increment 0004] Plugin Architecture # ❌ DEPRECATED
280
280
  Labels: specweave, increment, P1
281
281
  Milestone: v0.4.0
282
282
 
283
283
  Body:
284
284
  ---
285
- # [Increment 0004] Plugin Architecture
285
+ # [Increment 0004] Plugin Architecture # ❌ DEPRECATED
286
286
 
287
287
  **Status**: Planning
288
288
  **Priority**: P1
@@ -0,0 +1,195 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ class CodeValidator {
4
+ constructor(options = {}) {
5
+ this.options = {
6
+ minLines: options.minLines ?? 3,
7
+ minChars: options.minChars ?? 50,
8
+ projectRoot: options.projectRoot ?? process.cwd()
9
+ };
10
+ }
11
+ /**
12
+ * Validate that code exists for a task
13
+ *
14
+ * Extracts file paths from task description and verifies:
15
+ * 1. Files exist
16
+ * 2. Files have meaningful content
17
+ * 3. Files are not just stubs
18
+ *
19
+ * @param taskDescription - Task description with file paths
20
+ * @param taskId - Task ID for error messages
21
+ * @returns Validation result
22
+ */
23
+ async validateTask(taskDescription, taskId) {
24
+ const filePaths = this.extractFilePaths(taskDescription);
25
+ if (filePaths.length === 0) {
26
+ return {
27
+ taskId,
28
+ valid: true,
29
+ files: [],
30
+ reason: "No file paths specified in task description"
31
+ };
32
+ }
33
+ const fileResults = [];
34
+ let allValid = true;
35
+ const reasons = [];
36
+ for (const filePath of filePaths) {
37
+ const result = await this.validateFile(filePath);
38
+ fileResults.push(result);
39
+ if (!result.exists) {
40
+ allValid = false;
41
+ reasons.push(`File not found: ${filePath}`);
42
+ } else if (!result.hasContent) {
43
+ allValid = false;
44
+ reasons.push(`File has no meaningful content: ${filePath} (${result.reason})`);
45
+ }
46
+ }
47
+ return {
48
+ taskId,
49
+ valid: allValid,
50
+ files: fileResults,
51
+ reason: reasons.length > 0 ? reasons.join("; ") : void 0
52
+ };
53
+ }
54
+ /**
55
+ * Validate a single file
56
+ *
57
+ * @param filePath - Path to file (relative or absolute)
58
+ * @returns File validation result
59
+ */
60
+ async validateFile(filePath) {
61
+ const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(this.options.projectRoot, filePath);
62
+ const exists = await fs.pathExists(absolutePath);
63
+ if (!exists) {
64
+ return {
65
+ path: filePath,
66
+ exists: false,
67
+ hasContent: false,
68
+ lineCount: 0,
69
+ reason: "File does not exist"
70
+ };
71
+ }
72
+ const content = await fs.readFile(absolutePath, "utf-8");
73
+ const lines = content.split("\n");
74
+ const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
75
+ if (nonEmptyLines.length < this.options.minLines) {
76
+ return {
77
+ path: filePath,
78
+ exists: true,
79
+ hasContent: false,
80
+ lineCount: nonEmptyLines.length,
81
+ reason: `Only ${nonEmptyLines.length} non-empty lines (minimum: ${this.options.minLines})`
82
+ };
83
+ }
84
+ const trimmedContent = content.trim();
85
+ if (trimmedContent.length < this.options.minChars) {
86
+ return {
87
+ path: filePath,
88
+ exists: true,
89
+ hasContent: false,
90
+ lineCount: nonEmptyLines.length,
91
+ reason: `Only ${trimmedContent.length} characters (minimum: ${this.options.minChars})`
92
+ };
93
+ }
94
+ const stubPatterns = [
95
+ /^\/\/\s*TODO:/i,
96
+ /^#\s*TODO:/i,
97
+ /^\s*throw new Error\(['"]Not implemented['"]\)/i,
98
+ /^\s*return null;?\s*$/m,
99
+ /^\s*pass\s*$/m,
100
+ // Python
101
+ /^\s*\.\.\.$/m
102
+ // TypeScript
103
+ ];
104
+ const isStub = stubPatterns.some((pattern) => pattern.test(trimmedContent));
105
+ if (isStub) {
106
+ return {
107
+ path: filePath,
108
+ exists: true,
109
+ hasContent: false,
110
+ lineCount: nonEmptyLines.length,
111
+ reason: "File contains stub/placeholder code"
112
+ };
113
+ }
114
+ return {
115
+ path: filePath,
116
+ exists: true,
117
+ hasContent: true,
118
+ lineCount: nonEmptyLines.length
119
+ };
120
+ }
121
+ /**
122
+ * Extract file paths from task description
123
+ *
124
+ * Supports multiple formats:
125
+ * - **Files**: src/foo.ts, src/bar.ts
126
+ * - **Files to create**: src/foo.ts
127
+ * - **Files to modify**: src/bar.ts
128
+ * - Inline code blocks with file paths
129
+ *
130
+ * @param description - Task description text
131
+ * @returns Array of file paths
132
+ */
133
+ extractFilePaths(description) {
134
+ const paths = /* @__PURE__ */ new Set();
135
+ const filesMatch = description.match(/\*\*Files\*\*:\s*([^\n]+)/i);
136
+ if (filesMatch) {
137
+ const filePaths = filesMatch[1].split(",").map((p) => p.trim());
138
+ filePaths.forEach((p) => paths.add(p));
139
+ }
140
+ const createMatch = description.match(/\*\*Files to create\*\*:\s*([^\n]+)/i);
141
+ if (createMatch) {
142
+ const filePaths = createMatch[1].split(",").map((p) => p.trim());
143
+ filePaths.forEach((p) => paths.add(p));
144
+ }
145
+ const modifyMatch = description.match(/\*\*Files to modify\*\*:\s*([^\n]+)/i);
146
+ if (modifyMatch) {
147
+ const filePaths = modifyMatch[1].split(",").map((p) => p.trim());
148
+ filePaths.forEach((p) => paths.add(p));
149
+ }
150
+ const inlineMatches = description.matchAll(/`([a-zA-Z0-9_\-./]+\.(ts|js|tsx|jsx|py|java|go|rs|cpp|c|h))`/g);
151
+ for (const match of inlineMatches) {
152
+ paths.add(match[1]);
153
+ }
154
+ const listMatches = description.matchAll(/^[-*]\s+([a-zA-Z0-9_\-./]+\.(ts|js|tsx|jsx|py|java|go|rs|cpp|c|h))/gm);
155
+ for (const match of listMatches) {
156
+ paths.add(match[1]);
157
+ }
158
+ return Array.from(paths);
159
+ }
160
+ /**
161
+ * Batch validate multiple tasks
162
+ *
163
+ * @param tasks - Array of {taskId, description}
164
+ * @returns Array of validation results
165
+ */
166
+ async validateTasks(tasks) {
167
+ const validationPromises = tasks.map(
168
+ (task) => this.validateTask(task.description, task.taskId)
169
+ );
170
+ return Promise.all(validationPromises);
171
+ }
172
+ /**
173
+ * Get summary of validation results
174
+ *
175
+ * @param results - Array of task validation results
176
+ * @returns Summary statistics
177
+ */
178
+ summarizeResults(results) {
179
+ const total = results.length;
180
+ const valid = results.filter((r) => r.valid).length;
181
+ const invalid = results.filter((r) => !r.valid).length;
182
+ const noFiles = results.filter((r) => r.files.length === 0).length;
183
+ const invalidTasks = results.filter((r) => !r.valid).map((r) => r.taskId);
184
+ return {
185
+ total,
186
+ valid,
187
+ invalid,
188
+ noFiles,
189
+ invalidTasks
190
+ };
191
+ }
192
+ }
193
+ export {
194
+ CodeValidator
195
+ };
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Code Validator
3
+ *
4
+ * Validates that completed tasks have actual code implementation.
5
+ * Prevents marking tasks as complete when:
6
+ * - Files don't exist
7
+ * - Files are empty or have trivial content
8
+ * - Implementation is incomplete
9
+ *
10
+ * Used by GitHub sync to enforce code-completion discipline.
11
+ *
12
+ * @module CodeValidator
13
+ */
14
+
15
+ import fs from 'fs-extra';
16
+ import path from 'path';
17
+
18
+ /**
19
+ * File validation result
20
+ */
21
+ export interface FileValidationResult {
22
+ path: string;
23
+ exists: boolean;
24
+ hasContent: boolean;
25
+ lineCount: number;
26
+ reason?: string; // Why validation failed
27
+ }
28
+
29
+ /**
30
+ * Task validation result
31
+ */
32
+ export interface TaskValidationResult {
33
+ taskId: string;
34
+ valid: boolean;
35
+ files: FileValidationResult[];
36
+ reason?: string; // Summary of why task validation failed
37
+ }
38
+
39
+ /**
40
+ * CodeValidator options
41
+ */
42
+ export interface CodeValidatorOptions {
43
+ minLines?: number; // Minimum lines for a file to be considered non-empty (default: 3)
44
+ minChars?: number; // Minimum characters for meaningful content (default: 50)
45
+ projectRoot?: string; // Project root for resolving relative paths
46
+ }
47
+
48
+ export class CodeValidator {
49
+ private options: Required<CodeValidatorOptions>;
50
+
51
+ constructor(options: CodeValidatorOptions = {}) {
52
+ this.options = {
53
+ minLines: options.minLines ?? 3,
54
+ minChars: options.minChars ?? 50,
55
+ projectRoot: options.projectRoot ?? process.cwd()
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Validate that code exists for a task
61
+ *
62
+ * Extracts file paths from task description and verifies:
63
+ * 1. Files exist
64
+ * 2. Files have meaningful content
65
+ * 3. Files are not just stubs
66
+ *
67
+ * @param taskDescription - Task description with file paths
68
+ * @param taskId - Task ID for error messages
69
+ * @returns Validation result
70
+ */
71
+ async validateTask(taskDescription: string, taskId: string): Promise<TaskValidationResult> {
72
+ const filePaths = this.extractFilePaths(taskDescription);
73
+
74
+ if (filePaths.length === 0) {
75
+ // No file paths specified - consider it valid (task might be non-code)
76
+ return {
77
+ taskId,
78
+ valid: true,
79
+ files: [],
80
+ reason: 'No file paths specified in task description'
81
+ };
82
+ }
83
+
84
+ const fileResults: FileValidationResult[] = [];
85
+ let allValid = true;
86
+ const reasons: string[] = [];
87
+
88
+ for (const filePath of filePaths) {
89
+ const result = await this.validateFile(filePath);
90
+ fileResults.push(result);
91
+
92
+ if (!result.exists) {
93
+ allValid = false;
94
+ reasons.push(`File not found: ${filePath}`);
95
+ } else if (!result.hasContent) {
96
+ allValid = false;
97
+ reasons.push(`File has no meaningful content: ${filePath} (${result.reason})`);
98
+ }
99
+ }
100
+
101
+ return {
102
+ taskId,
103
+ valid: allValid,
104
+ files: fileResults,
105
+ reason: reasons.length > 0 ? reasons.join('; ') : undefined
106
+ };
107
+ }
108
+
109
+ /**
110
+ * Validate a single file
111
+ *
112
+ * @param filePath - Path to file (relative or absolute)
113
+ * @returns File validation result
114
+ */
115
+ async validateFile(filePath: string): Promise<FileValidationResult> {
116
+ // Resolve relative paths
117
+ const absolutePath = path.isAbsolute(filePath)
118
+ ? filePath
119
+ : path.join(this.options.projectRoot, filePath);
120
+
121
+ // Check if file exists
122
+ const exists = await fs.pathExists(absolutePath);
123
+ if (!exists) {
124
+ return {
125
+ path: filePath,
126
+ exists: false,
127
+ hasContent: false,
128
+ lineCount: 0,
129
+ reason: 'File does not exist'
130
+ };
131
+ }
132
+
133
+ // Read file content
134
+ const content = await fs.readFile(absolutePath, 'utf-8');
135
+ const lines = content.split('\n');
136
+ const nonEmptyLines = lines.filter(line => line.trim().length > 0);
137
+
138
+ // Check line count
139
+ if (nonEmptyLines.length < this.options.minLines) {
140
+ return {
141
+ path: filePath,
142
+ exists: true,
143
+ hasContent: false,
144
+ lineCount: nonEmptyLines.length,
145
+ reason: `Only ${nonEmptyLines.length} non-empty lines (minimum: ${this.options.minLines})`
146
+ };
147
+ }
148
+
149
+ // Check character count
150
+ const trimmedContent = content.trim();
151
+ if (trimmedContent.length < this.options.minChars) {
152
+ return {
153
+ path: filePath,
154
+ exists: true,
155
+ hasContent: false,
156
+ lineCount: nonEmptyLines.length,
157
+ reason: `Only ${trimmedContent.length} characters (minimum: ${this.options.minChars})`
158
+ };
159
+ }
160
+
161
+ // Check for stub patterns (common placeholder patterns)
162
+ const stubPatterns = [
163
+ /^\/\/\s*TODO:/i,
164
+ /^#\s*TODO:/i,
165
+ /^\s*throw new Error\(['"]Not implemented['"]\)/i,
166
+ /^\s*return null;?\s*$/m,
167
+ /^\s*pass\s*$/m, // Python
168
+ /^\s*\.\.\.$/m // TypeScript
169
+ ];
170
+
171
+ const isStub = stubPatterns.some(pattern => pattern.test(trimmedContent));
172
+ if (isStub) {
173
+ return {
174
+ path: filePath,
175
+ exists: true,
176
+ hasContent: false,
177
+ lineCount: nonEmptyLines.length,
178
+ reason: 'File contains stub/placeholder code'
179
+ };
180
+ }
181
+
182
+ // All checks passed
183
+ return {
184
+ path: filePath,
185
+ exists: true,
186
+ hasContent: true,
187
+ lineCount: nonEmptyLines.length
188
+ };
189
+ }
190
+
191
+ /**
192
+ * Extract file paths from task description
193
+ *
194
+ * Supports multiple formats:
195
+ * - **Files**: src/foo.ts, src/bar.ts
196
+ * - **Files to create**: src/foo.ts
197
+ * - **Files to modify**: src/bar.ts
198
+ * - Inline code blocks with file paths
199
+ *
200
+ * @param description - Task description text
201
+ * @returns Array of file paths
202
+ */
203
+ extractFilePaths(description: string): string[] {
204
+ const paths: Set<string> = new Set();
205
+
206
+ // Pattern 1: **Files**: path1, path2, path3
207
+ const filesMatch = description.match(/\*\*Files\*\*:\s*([^\n]+)/i);
208
+ if (filesMatch) {
209
+ const filePaths = filesMatch[1].split(',').map(p => p.trim());
210
+ filePaths.forEach(p => paths.add(p));
211
+ }
212
+
213
+ // Pattern 2: **Files to create**: path1, path2
214
+ const createMatch = description.match(/\*\*Files to create\*\*:\s*([^\n]+)/i);
215
+ if (createMatch) {
216
+ const filePaths = createMatch[1].split(',').map(p => p.trim());
217
+ filePaths.forEach(p => paths.add(p));
218
+ }
219
+
220
+ // Pattern 3: **Files to modify**: path1, path2
221
+ const modifyMatch = description.match(/\*\*Files to modify\*\*:\s*([^\n]+)/i);
222
+ if (modifyMatch) {
223
+ const filePaths = modifyMatch[1].split(',').map(p => p.trim());
224
+ filePaths.forEach(p => paths.add(p));
225
+ }
226
+
227
+ // Pattern 4: Inline file references (e.g., `src/foo/bar.ts`)
228
+ const inlineMatches = description.matchAll(/`([a-zA-Z0-9_\-./]+\.(ts|js|tsx|jsx|py|java|go|rs|cpp|c|h))`/g);
229
+ for (const match of inlineMatches) {
230
+ paths.add(match[1]);
231
+ }
232
+
233
+ // Pattern 5: Markdown list items with file paths
234
+ const listMatches = description.matchAll(/^[-*]\s+([a-zA-Z0-9_\-./]+\.(ts|js|tsx|jsx|py|java|go|rs|cpp|c|h))/gm);
235
+ for (const match of listMatches) {
236
+ paths.add(match[1]);
237
+ }
238
+
239
+ return Array.from(paths);
240
+ }
241
+
242
+ /**
243
+ * Batch validate multiple tasks
244
+ *
245
+ * @param tasks - Array of {taskId, description}
246
+ * @returns Array of validation results
247
+ */
248
+ async validateTasks(tasks: Array<{ taskId: string; description: string }>): Promise<TaskValidationResult[]> {
249
+ // Use parallel validation for performance
250
+ const validationPromises = tasks.map(task =>
251
+ this.validateTask(task.description, task.taskId)
252
+ );
253
+
254
+ return Promise.all(validationPromises);
255
+ }
256
+
257
+ /**
258
+ * Get summary of validation results
259
+ *
260
+ * @param results - Array of task validation results
261
+ * @returns Summary statistics
262
+ */
263
+ summarizeResults(results: TaskValidationResult[]): {
264
+ total: number;
265
+ valid: number;
266
+ invalid: number;
267
+ noFiles: number;
268
+ invalidTasks: string[];
269
+ } {
270
+ const total = results.length;
271
+ const valid = results.filter(r => r.valid).length;
272
+ const invalid = results.filter(r => !r.valid).length;
273
+ const noFiles = results.filter(r => r.files.length === 0).length;
274
+ const invalidTasks = results.filter(r => !r.valid).map(r => r.taskId);
275
+
276
+ return {
277
+ total,
278
+ valid,
279
+ invalid,
280
+ noFiles,
281
+ invalidTasks
282
+ };
283
+ }
284
+ }