specweave 0.29.2 → 0.30.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 (366) hide show
  1. package/README.md +65 -16
  2. package/bin/specweave.js +16 -0
  3. package/dist/plugins/specweave-ado/lib/ado-spec-commit-sync.d.ts +1 -1
  4. package/dist/plugins/specweave-ado/lib/ado-spec-commit-sync.d.ts.map +1 -1
  5. package/dist/plugins/specweave-ado/lib/ado-spec-commit-sync.js +1 -1
  6. package/dist/plugins/specweave-ado/lib/ado-spec-commit-sync.js.map +1 -1
  7. package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.d.ts +1 -1
  8. package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.d.ts.map +1 -1
  9. package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js +1 -1
  10. package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js.map +1 -1
  11. package/dist/plugins/specweave-ado/lib/conflict-resolver.d.ts +134 -0
  12. package/dist/plugins/specweave-ado/lib/conflict-resolver.d.ts.map +1 -0
  13. package/dist/plugins/specweave-ado/lib/conflict-resolver.js +423 -0
  14. package/dist/plugins/specweave-ado/lib/conflict-resolver.js.map +1 -0
  15. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +23 -1
  16. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
  17. package/dist/plugins/specweave-github/lib/github-client-v2.js +95 -0
  18. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  19. package/dist/plugins/specweave-github/lib/github-spec-commit-sync.d.ts +1 -1
  20. package/dist/plugins/specweave-github/lib/github-spec-commit-sync.d.ts.map +1 -1
  21. package/dist/plugins/specweave-github/lib/github-spec-commit-sync.js +1 -1
  22. package/dist/plugins/specweave-github/lib/github-spec-commit-sync.js.map +1 -1
  23. package/dist/plugins/specweave-github/lib/github-spec-content-sync.d.ts +1 -1
  24. package/dist/plugins/specweave-github/lib/github-spec-content-sync.d.ts.map +1 -1
  25. package/dist/plugins/specweave-github/lib/github-spec-content-sync.js +1 -1
  26. package/dist/plugins/specweave-github/lib/github-spec-content-sync.js.map +1 -1
  27. package/dist/plugins/specweave-github/lib/types.d.ts +20 -0
  28. package/dist/plugins/specweave-github/lib/types.d.ts.map +1 -1
  29. package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.d.ts +1 -1
  30. package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.d.ts.map +1 -1
  31. package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.js +1 -1
  32. package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.js.map +1 -1
  33. package/dist/plugins/specweave-jira/lib/jira-spec-content-sync.d.ts +1 -1
  34. package/dist/plugins/specweave-jira/lib/jira-spec-content-sync.d.ts.map +1 -1
  35. package/dist/plugins/specweave-jira/lib/jira-spec-content-sync.js +1 -1
  36. package/dist/plugins/specweave-jira/lib/jira-spec-content-sync.js.map +1 -1
  37. package/dist/plugins/specweave-jira/lib/setup-wizard.d.ts +2 -2
  38. package/dist/plugins/specweave-jira/lib/setup-wizard.d.ts.map +1 -1
  39. package/dist/plugins/specweave-jira/lib/setup-wizard.js +1 -1
  40. package/dist/plugins/specweave-jira/lib/setup-wizard.js.map +1 -1
  41. package/dist/src/adapters/claude/adapter.js +1 -1
  42. package/dist/src/adapters/claude/adapter.js.map +1 -1
  43. package/dist/src/cli/commands/detect-project.js +1 -1
  44. package/dist/src/cli/commands/detect-project.js.map +1 -1
  45. package/dist/src/cli/commands/detect-specs.js +1 -1
  46. package/dist/src/cli/commands/detect-specs.js.map +1 -1
  47. package/dist/src/cli/commands/import-docs.js +1 -1
  48. package/dist/src/cli/commands/import-docs.js.map +1 -1
  49. package/dist/src/cli/commands/init-multiproject.js +1 -1
  50. package/dist/src/cli/commands/init-multiproject.js.map +1 -1
  51. package/dist/src/cli/commands/init.d.ts.map +1 -1
  52. package/dist/src/cli/commands/init.js +31 -1
  53. package/dist/src/cli/commands/init.js.map +1 -1
  54. package/dist/src/cli/commands/jobs.js +15 -1
  55. package/dist/src/cli/commands/jobs.js.map +1 -1
  56. package/dist/src/cli/commands/migrate-to-multiproject.js +1 -1
  57. package/dist/src/cli/commands/migrate-to-multiproject.js.map +1 -1
  58. package/dist/src/cli/commands/switch-project.js +1 -1
  59. package/dist/src/cli/commands/switch-project.js.map +1 -1
  60. package/dist/src/cli/commands/sync-scheduled.d.ts +38 -0
  61. package/dist/src/cli/commands/sync-scheduled.d.ts.map +1 -0
  62. package/dist/src/cli/commands/sync-scheduled.js +170 -0
  63. package/dist/src/cli/commands/sync-scheduled.js.map +1 -0
  64. package/dist/src/cli/helpers/init/external-import-grouping.d.ts +53 -0
  65. package/dist/src/cli/helpers/init/external-import-grouping.d.ts.map +1 -0
  66. package/dist/src/cli/helpers/init/external-import-grouping.js +287 -0
  67. package/dist/src/cli/helpers/init/external-import-grouping.js.map +1 -0
  68. package/dist/src/cli/helpers/init/external-import.d.ts +1 -34
  69. package/dist/src/cli/helpers/init/external-import.d.ts.map +1 -1
  70. package/dist/src/cli/helpers/init/external-import.js +15 -486
  71. package/dist/src/cli/helpers/init/external-import.js.map +1 -1
  72. package/dist/src/cli/helpers/init/living-docs-preflight.d.ts +58 -0
  73. package/dist/src/cli/helpers/init/living-docs-preflight.d.ts.map +1 -0
  74. package/dist/src/cli/helpers/init/living-docs-preflight.js +290 -0
  75. package/dist/src/cli/helpers/init/living-docs-preflight.js.map +1 -0
  76. package/dist/src/cli/helpers/init/sync-profile-helpers.d.ts +48 -0
  77. package/dist/src/cli/helpers/init/sync-profile-helpers.d.ts.map +1 -0
  78. package/dist/src/cli/helpers/init/sync-profile-helpers.js +238 -0
  79. package/dist/src/cli/helpers/init/sync-profile-helpers.js.map +1 -0
  80. package/dist/src/cli/helpers/init/types.d.ts +1 -0
  81. package/dist/src/cli/helpers/init/types.d.ts.map +1 -1
  82. package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
  83. package/dist/src/cli/helpers/issue-tracker/ado.js +113 -3
  84. package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
  85. package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
  86. package/dist/src/cli/helpers/issue-tracker/index.js +2 -287
  87. package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
  88. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.d.ts +25 -0
  89. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.d.ts.map +1 -0
  90. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js +306 -0
  91. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js.map +1 -0
  92. package/dist/src/cli/helpers/issue-tracker/types.d.ts +27 -0
  93. package/dist/src/cli/helpers/issue-tracker/types.d.ts.map +1 -1
  94. package/dist/src/cli/helpers/issue-tracker/types.js.map +1 -1
  95. package/dist/src/cli/workers/living-docs-worker.d.ts +16 -0
  96. package/dist/src/cli/workers/living-docs-worker.d.ts.map +1 -0
  97. package/dist/src/cli/workers/living-docs-worker.js +395 -0
  98. package/dist/src/cli/workers/living-docs-worker.js.map +1 -0
  99. package/dist/src/core/background/job-dependency.d.ts +45 -0
  100. package/dist/src/core/background/job-dependency.d.ts.map +1 -0
  101. package/dist/src/core/background/job-dependency.js +144 -0
  102. package/dist/src/core/background/job-dependency.js.map +1 -0
  103. package/dist/src/core/background/job-launcher.d.ts +21 -1
  104. package/dist/src/core/background/job-launcher.d.ts.map +1 -1
  105. package/dist/src/core/background/job-launcher.js +93 -0
  106. package/dist/src/core/background/job-launcher.js.map +1 -1
  107. package/dist/src/core/background/types.d.ts +73 -2
  108. package/dist/src/core/background/types.d.ts.map +1 -1
  109. package/dist/src/core/brownfield/importer.js +1 -1
  110. package/dist/src/core/brownfield/importer.js.map +1 -1
  111. package/dist/src/core/comment-builder.d.ts +1 -1
  112. package/dist/src/core/comment-builder.d.ts.map +1 -1
  113. package/dist/src/core/{cost-tracker.d.ts → cost/cost-tracker.d.ts} +2 -2
  114. package/dist/src/core/cost/cost-tracker.d.ts.map +1 -0
  115. package/dist/src/core/{cost-tracker.js → cost/cost-tracker.js} +2 -2
  116. package/dist/src/core/cost/cost-tracker.js.map +1 -0
  117. package/dist/src/core/credentials/credentials-manager.d.ts.map +1 -0
  118. package/dist/src/core/credentials/credentials-manager.js.map +1 -0
  119. package/dist/src/core/increment/discipline-checker.js +1 -1
  120. package/dist/src/core/increment/discipline-checker.js.map +1 -1
  121. package/dist/src/core/increment/increment-archiver.d.ts +9 -8
  122. package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
  123. package/dist/src/core/increment/increment-archiver.js +40 -42
  124. package/dist/src/core/increment/increment-archiver.js.map +1 -1
  125. package/dist/src/core/increment/increment-status.d.ts.map +1 -0
  126. package/dist/src/core/{increment-status.js → increment/increment-status.js} +1 -1
  127. package/dist/src/core/increment/increment-status.js.map +1 -0
  128. package/dist/src/core/increment/increment-utils.d.ts.map +1 -0
  129. package/dist/src/core/increment/increment-utils.js.map +1 -0
  130. package/dist/src/core/increment/metadata-validator.js +1 -1
  131. package/dist/src/core/increment/metadata-validator.js.map +1 -1
  132. package/dist/src/core/living-docs/checkpoint-manager.d.ts +120 -0
  133. package/dist/src/core/living-docs/checkpoint-manager.d.ts.map +1 -0
  134. package/dist/src/core/living-docs/checkpoint-manager.js +231 -0
  135. package/dist/src/core/living-docs/checkpoint-manager.js.map +1 -0
  136. package/dist/src/core/living-docs/discovery.d.ts +91 -0
  137. package/dist/src/core/living-docs/discovery.d.ts.map +1 -0
  138. package/dist/src/core/living-docs/discovery.js +656 -0
  139. package/dist/src/core/living-docs/discovery.js.map +1 -0
  140. package/dist/src/core/living-docs/feature-archiver.d.ts +23 -4
  141. package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -1
  142. package/dist/src/core/living-docs/feature-archiver.js +133 -30
  143. package/dist/src/core/living-docs/feature-archiver.js.map +1 -1
  144. package/dist/src/core/living-docs/foundation-builder.d.ts +37 -0
  145. package/dist/src/core/living-docs/foundation-builder.d.ts.map +1 -0
  146. package/dist/src/core/living-docs/foundation-builder.js +357 -0
  147. package/dist/src/core/living-docs/foundation-builder.js.map +1 -0
  148. package/dist/src/core/living-docs/living-docs-sync.d.ts +58 -0
  149. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  150. package/dist/src/core/living-docs/living-docs-sync.js +204 -7
  151. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  152. package/dist/src/core/living-docs/module-analyzer.d.ts +97 -0
  153. package/dist/src/core/living-docs/module-analyzer.d.ts.map +1 -0
  154. package/dist/src/core/living-docs/module-analyzer.js +427 -0
  155. package/dist/src/core/living-docs/module-analyzer.js.map +1 -0
  156. package/dist/src/core/living-docs/suggestions-generator.d.ts +112 -0
  157. package/dist/src/core/living-docs/suggestions-generator.d.ts.map +1 -0
  158. package/dist/src/core/living-docs/suggestions-generator.js +362 -0
  159. package/dist/src/core/living-docs/suggestions-generator.js.map +1 -0
  160. package/dist/src/core/living-docs/workitem-matcher.d.ts +116 -0
  161. package/dist/src/core/living-docs/workitem-matcher.d.ts.map +1 -0
  162. package/dist/src/core/living-docs/workitem-matcher.js +356 -0
  163. package/dist/src/core/living-docs/workitem-matcher.js.map +1 -0
  164. package/dist/src/core/{plugin-loader.d.ts → plugins/plugin-loader.d.ts} +1 -1
  165. package/dist/src/core/plugins/plugin-loader.d.ts.map +1 -0
  166. package/dist/src/core/{plugin-loader.js → plugins/plugin-loader.js} +2 -2
  167. package/dist/src/core/plugins/plugin-loader.js.map +1 -0
  168. package/dist/src/core/{project-manager.d.ts → project/project-manager.d.ts} +1 -1
  169. package/dist/src/core/project/project-manager.d.ts.map +1 -0
  170. package/dist/src/core/{project-manager.js → project/project-manager.js} +3 -3
  171. package/dist/src/core/project/project-manager.js.map +1 -0
  172. package/dist/src/core/project/project-structure-detector.d.ts.map +1 -0
  173. package/dist/src/core/project/project-structure-detector.js.map +1 -0
  174. package/dist/src/core/{rfc-generator-v2.d.ts → rfc/rfc-generator-v2.d.ts} +1 -1
  175. package/dist/src/core/rfc/rfc-generator-v2.d.ts.map +1 -0
  176. package/dist/src/core/{rfc-generator-v2.js → rfc/rfc-generator-v2.js} +1 -1
  177. package/dist/src/core/rfc/rfc-generator-v2.js.map +1 -0
  178. package/dist/src/core/scheduler/index.d.ts +1 -0
  179. package/dist/src/core/scheduler/index.d.ts.map +1 -1
  180. package/dist/src/core/scheduler/index.js +1 -0
  181. package/dist/src/core/scheduler/index.js.map +1 -1
  182. package/dist/src/core/scheduler/scheduled-job.d.ts +1 -1
  183. package/dist/src/core/scheduler/scheduled-job.d.ts.map +1 -1
  184. package/dist/src/core/scheduler/scheduled-job.js.map +1 -1
  185. package/dist/src/core/scheduler/session-sync-executor.d.ts +138 -0
  186. package/dist/src/core/scheduler/session-sync-executor.d.ts.map +1 -0
  187. package/dist/src/core/scheduler/session-sync-executor.js +333 -0
  188. package/dist/src/core/scheduler/session-sync-executor.js.map +1 -0
  189. package/dist/src/core/{spec-content-sync.d.ts → specs/spec-content-sync.d.ts} +2 -2
  190. package/dist/src/core/specs/spec-content-sync.d.ts.map +1 -0
  191. package/dist/src/core/{spec-content-sync.js → specs/spec-content-sync.js} +1 -1
  192. package/dist/src/core/specs/spec-content-sync.js.map +1 -0
  193. package/dist/src/core/{spec-detector.d.ts → specs/spec-detector.d.ts} +1 -1
  194. package/dist/src/core/specs/spec-detector.d.ts.map +1 -0
  195. package/dist/src/core/{spec-detector.js → specs/spec-detector.js} +1 -1
  196. package/dist/src/core/specs/spec-detector.js.map +1 -0
  197. package/dist/src/core/{spec-identifier-detector.d.ts → specs/spec-identifier-detector.d.ts} +2 -2
  198. package/dist/src/core/specs/spec-identifier-detector.d.ts.map +1 -0
  199. package/dist/src/core/{spec-identifier-detector.js → specs/spec-identifier-detector.js} +1 -1
  200. package/dist/src/core/specs/spec-identifier-detector.js.map +1 -0
  201. package/dist/src/core/specs/spec-metadata-manager.js +1 -1
  202. package/dist/src/core/specs/spec-metadata-manager.js.map +1 -1
  203. package/dist/src/core/specs/spec-task-mapper.d.ts.map +1 -0
  204. package/dist/src/core/specs/spec-task-mapper.js.map +1 -0
  205. package/dist/src/core/sync/sync-audit-logger.d.ts +62 -0
  206. package/dist/src/core/sync/sync-audit-logger.d.ts.map +1 -1
  207. package/dist/src/core/sync/sync-audit-logger.js +59 -0
  208. package/dist/src/core/sync/sync-audit-logger.js.map +1 -1
  209. package/dist/src/importers/item-converter.d.ts +51 -27
  210. package/dist/src/importers/item-converter.d.ts.map +1 -1
  211. package/dist/src/importers/item-converter.js +219 -179
  212. package/dist/src/importers/item-converter.js.map +1 -1
  213. package/dist/src/importers/markdown-generator.d.ts +75 -0
  214. package/dist/src/importers/markdown-generator.d.ts.map +1 -0
  215. package/dist/src/importers/markdown-generator.js +250 -0
  216. package/dist/src/importers/markdown-generator.js.map +1 -0
  217. package/dist/src/integrations/ado/ado-client.d.ts +93 -0
  218. package/dist/src/integrations/ado/ado-client.d.ts.map +1 -1
  219. package/dist/src/integrations/ado/ado-client.js +172 -1
  220. package/dist/src/integrations/ado/ado-client.js.map +1 -1
  221. package/dist/src/integrations/jira/jira-client.d.ts +44 -0
  222. package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
  223. package/dist/src/integrations/jira/jira-client.js +119 -1
  224. package/dist/src/integrations/jira/jira-client.js.map +1 -1
  225. package/dist/src/integrations/jira/jira-incremental-mapper.js +2 -2
  226. package/dist/src/integrations/jira/jira-incremental-mapper.js.map +1 -1
  227. package/dist/src/integrations/jira/jira-mapper.js +1 -1
  228. package/dist/src/integrations/jira/jira-mapper.js.map +1 -1
  229. package/dist/src/living-docs/epic-id-allocator.d.ts +177 -0
  230. package/dist/src/living-docs/epic-id-allocator.d.ts.map +1 -0
  231. package/dist/src/living-docs/epic-id-allocator.js +363 -0
  232. package/dist/src/living-docs/epic-id-allocator.js.map +1 -0
  233. package/dist/src/sync/external-change-puller.d.ts +94 -0
  234. package/dist/src/sync/external-change-puller.d.ts.map +1 -0
  235. package/dist/src/sync/external-change-puller.js +181 -0
  236. package/dist/src/sync/external-change-puller.js.map +1 -0
  237. package/dist/src/sync/index.d.ts +4 -0
  238. package/dist/src/sync/index.d.ts.map +1 -1
  239. package/dist/src/sync/index.js +2 -0
  240. package/dist/src/sync/index.js.map +1 -1
  241. package/dist/src/sync/living-docs-updater.d.ts +93 -0
  242. package/dist/src/sync/living-docs-updater.d.ts.map +1 -0
  243. package/dist/src/sync/living-docs-updater.js +218 -0
  244. package/dist/src/sync/living-docs-updater.js.map +1 -0
  245. package/dist/src/testing/test-generator.js +1 -1
  246. package/dist/src/testing/test-generator.js.map +1 -1
  247. package/dist/src/utils/cost-reporter.d.ts +1 -1
  248. package/dist/src/utils/cost-reporter.d.ts.map +1 -1
  249. package/dist/src/utils/model-selection.js +4 -4
  250. package/dist/src/utils/model-selection.js.map +1 -1
  251. package/dist/src/utils/pricing-constants.d.ts +2 -2
  252. package/dist/src/utils/pricing-constants.js +2 -2
  253. package/package.json +1 -1
  254. package/plugins/specweave/.claude-plugin/plugin.json +1 -28
  255. package/plugins/specweave/agents/code-standards-detective/AGENT.md +3 -3
  256. package/plugins/specweave/agents/infrastructure/AGENT.md +4 -0
  257. package/plugins/specweave/agents/performance/AGENT.md +2 -2
  258. package/plugins/specweave/agents/reflective-reviewer/AGENT.md +3 -3
  259. package/plugins/specweave/agents/translator/AGENT.md +2 -2
  260. package/plugins/specweave/commands/specweave-costs.md +2 -2
  261. package/plugins/specweave/commands/specweave-do.md +2 -2
  262. package/plugins/specweave/hooks/lib/scheduler-startup.sh +59 -8
  263. package/plugins/specweave/skills/code-reviewer/SKILL.md +1 -1
  264. package/plugins/specweave/skills/role-orchestrator/README.md +1 -1
  265. package/plugins/specweave-ado/agents/ado-manager/AGENT.md +8 -2
  266. package/plugins/specweave-ado/agents/ado-multi-project-mapper/AGENT.md +2 -2
  267. package/plugins/specweave-ado/agents/ado-sync-judge/AGENT.md +2 -2
  268. package/plugins/specweave-ado/lib/ado-spec-commit-sync.d.ts +1 -1
  269. package/plugins/specweave-ado/lib/ado-spec-commit-sync.js +1 -1
  270. package/plugins/specweave-ado/lib/ado-spec-commit-sync.ts +1 -1
  271. package/plugins/specweave-ado/lib/ado-spec-content-sync.js +1 -1
  272. package/plugins/specweave-ado/lib/ado-spec-content-sync.ts +1 -1
  273. package/plugins/specweave-ado/lib/conflict-resolver.js +87 -0
  274. package/plugins/specweave-ado/lib/conflict-resolver.ts +124 -0
  275. package/plugins/specweave-ado/skills/specweave-ado-mapper/SKILL.md +1 -1
  276. package/plugins/specweave-backend/agents/database-optimizer/AGENT.md +3 -3
  277. package/plugins/specweave-backend/skills/dotnet-backend/SKILL.md +1 -1
  278. package/plugins/specweave-backend/skills/nodejs-backend/SKILL.md +1 -1
  279. package/plugins/specweave-backend/skills/python-backend/SKILL.md +1 -1
  280. package/plugins/specweave-confluent/agents/confluent-architect/AGENT.md +6 -1
  281. package/plugins/specweave-diagrams/agents/diagrams-architect/AGENT.md +2 -2
  282. package/plugins/specweave-diagrams/skills/diagrams-architect/SKILL.md +1 -1
  283. package/plugins/specweave-frontend/agents/frontend-architect/AGENT.md +4 -0
  284. package/plugins/specweave-github/agents/github-manager/AGENT.md +1 -1
  285. package/plugins/specweave-github/agents/github-task-splitter/AGENT.md +11 -1
  286. package/plugins/specweave-github/agents/user-story-updater/AGENT.md +6 -1
  287. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +90 -0
  288. package/plugins/specweave-github/lib/github-client-v2.js +97 -0
  289. package/plugins/specweave-github/lib/github-client-v2.ts +115 -1
  290. package/plugins/specweave-github/lib/github-spec-commit-sync.d.ts +1 -1
  291. package/plugins/specweave-github/lib/github-spec-commit-sync.js +1 -1
  292. package/plugins/specweave-github/lib/github-spec-commit-sync.ts +1 -1
  293. package/plugins/specweave-github/lib/github-spec-content-sync.js +1 -1
  294. package/plugins/specweave-github/lib/github-spec-content-sync.ts +1 -1
  295. package/plugins/specweave-github/lib/types.ts +21 -0
  296. package/plugins/specweave-infrastructure/agents/devops/AGENT.md +1 -1
  297. package/plugins/specweave-infrastructure/agents/network-engineer/AGENT.md +3 -3
  298. package/plugins/specweave-infrastructure/agents/observability-engineer/AGENT.md +2 -2
  299. package/plugins/specweave-infrastructure/agents/performance-engineer/AGENT.md +3 -3
  300. package/plugins/specweave-infrastructure/agents/sre/AGENT.md +2 -2
  301. package/plugins/specweave-jira/agents/jira-manager/AGENT.md +11 -1
  302. package/plugins/specweave-jira/commands/import-projects.js +1 -1
  303. package/plugins/specweave-jira/commands/import-projects.ts +1 -1
  304. package/plugins/specweave-jira/lib/jira-spec-commit-sync.d.ts +1 -1
  305. package/plugins/specweave-jira/lib/jira-spec-commit-sync.js +1 -1
  306. package/plugins/specweave-jira/lib/jira-spec-commit-sync.ts +1 -1
  307. package/plugins/specweave-jira/lib/jira-spec-content-sync.js +1 -1
  308. package/plugins/specweave-jira/lib/jira-spec-content-sync.ts +1 -1
  309. package/plugins/specweave-jira/lib/setup-wizard.js +2 -2
  310. package/plugins/specweave-jira/lib/setup-wizard.ts +2 -2
  311. package/plugins/specweave-jira/skills/specweave-jira-mapper/SKILL.md +1 -1
  312. package/plugins/specweave-kafka/agents/kafka-architect/AGENT.md +5 -1
  313. package/plugins/specweave-kafka/agents/kafka-devops/AGENT.md +6 -1
  314. package/plugins/specweave-kafka/agents/kafka-observability/AGENT.md +6 -1
  315. package/plugins/specweave-kubernetes/agents/kubernetes-architect/AGENT.md +3 -3
  316. package/plugins/specweave-ml/agents/data-scientist/AGENT.md +2 -1
  317. package/plugins/specweave-ml/agents/ml-engineer/AGENT.md +2 -1
  318. package/plugins/specweave-ml/agents/mlops-engineer/AGENT.md +3 -3
  319. package/plugins/specweave-mobile/agents/mobile-architect/AGENT.md +6 -1
  320. package/plugins/specweave-payments/agents/payment-integration/AGENT.md +3 -3
  321. package/plugins/specweave-release/agents/release-manager/AGENT.md +6 -1
  322. package/plugins/specweave-release/commands/specweave-release-npm.md +8 -0
  323. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +135 -0
  324. package/plugins/specweave-testing/agents/qa-engineer/AGENT.md +5 -0
  325. package/src/templates/AGENTS.md.template +97 -0
  326. package/dist/src/core/cost-tracker.d.ts.map +0 -1
  327. package/dist/src/core/cost-tracker.js.map +0 -1
  328. package/dist/src/core/credentials-manager.d.ts.map +0 -1
  329. package/dist/src/core/credentials-manager.js.map +0 -1
  330. package/dist/src/core/increment-status.d.ts.map +0 -1
  331. package/dist/src/core/increment-status.js.map +0 -1
  332. package/dist/src/core/increment-utils.d.ts.map +0 -1
  333. package/dist/src/core/increment-utils.js.map +0 -1
  334. package/dist/src/core/plugin-loader.d.ts.map +0 -1
  335. package/dist/src/core/plugin-loader.js.map +0 -1
  336. package/dist/src/core/project-manager.d.ts.map +0 -1
  337. package/dist/src/core/project-manager.js.map +0 -1
  338. package/dist/src/core/project-structure-detector.d.ts.map +0 -1
  339. package/dist/src/core/project-structure-detector.js.map +0 -1
  340. package/dist/src/core/rfc-generator-v2.d.ts.map +0 -1
  341. package/dist/src/core/rfc-generator-v2.js.map +0 -1
  342. package/dist/src/core/spec-content-sync.d.ts.map +0 -1
  343. package/dist/src/core/spec-content-sync.js.map +0 -1
  344. package/dist/src/core/spec-detector.d.ts.map +0 -1
  345. package/dist/src/core/spec-detector.js.map +0 -1
  346. package/dist/src/core/spec-identifier-detector.d.ts.map +0 -1
  347. package/dist/src/core/spec-identifier-detector.js.map +0 -1
  348. package/dist/src/core/spec-task-mapper.d.ts.map +0 -1
  349. package/dist/src/core/spec-task-mapper.js.map +0 -1
  350. package/dist/src/utils/spec-parser.d.ts +0 -145
  351. package/dist/src/utils/spec-parser.d.ts.map +0 -1
  352. package/dist/src/utils/spec-parser.js +0 -640
  353. package/dist/src/utils/spec-parser.js.map +0 -1
  354. package/plugins/specweave/agents/pm/AGENT.md.bak2 +0 -1754
  355. package/plugins/specweave/hooks/hooks.json.bak +0 -72
  356. package/plugins/specweave/hooks/hooks.json.v1-backup +0 -16
  357. package/plugins/specweave/hooks/v2/hooks.json +0 -16
  358. /package/dist/src/core/{credentials-manager.d.ts → credentials/credentials-manager.d.ts} +0 -0
  359. /package/dist/src/core/{credentials-manager.js → credentials/credentials-manager.js} +0 -0
  360. /package/dist/src/core/{increment-status.d.ts → increment/increment-status.d.ts} +0 -0
  361. /package/dist/src/core/{increment-utils.d.ts → increment/increment-utils.d.ts} +0 -0
  362. /package/dist/src/core/{increment-utils.js → increment/increment-utils.js} +0 -0
  363. /package/dist/src/core/{project-structure-detector.d.ts → project/project-structure-detector.d.ts} +0 -0
  364. /package/dist/src/core/{project-structure-detector.js → project/project-structure-detector.js} +0 -0
  365. /package/dist/src/core/{spec-task-mapper.d.ts → specs/spec-task-mapper.d.ts} +0 -0
  366. /package/dist/src/core/{spec-task-mapper.js → specs/spec-task-mapper.js} +0 -0
@@ -7,8 +7,11 @@
7
7
  import * as fs from '../utils/fs-native.js';
8
8
  import path from 'path';
9
9
  import { FSIdAllocator } from '../living-docs/fs-id-allocator.js';
10
+ import { EpicIdAllocator } from '../living-docs/epic-id-allocator.js';
11
+ import { createExternalMetadata } from '../core/types/origin-metadata.js';
10
12
  import { DuplicateDetector } from './duplicate-detector.js';
11
13
  import { normalizeToProjectId } from '../utils/project-id-generator.js';
14
+ import { MarkdownGenerator } from './markdown-generator.js';
12
15
  /**
13
16
  * Convert external items to living docs User Stories
14
17
  *
@@ -19,8 +22,13 @@ export class ItemConverter {
19
22
  constructor(options) {
20
23
  this.duplicateDetector = null;
21
24
  this.fsIdAllocator = null;
25
+ this.epicIdAllocator = null;
22
26
  /** Cache of created feature folders to avoid duplicates */
23
27
  this.createdFeatures = new Map();
28
+ /** Cache of created epic folders to avoid duplicates */
29
+ this.createdEpics = new Map();
30
+ /** Markdown generator for living docs content */
31
+ this.markdownGen = new MarkdownGenerator();
24
32
  this.options = {
25
33
  enableDuplicateDetection: true,
26
34
  // NOTE: projectId is intentionally NOT defaulted to 'default'
@@ -42,6 +50,10 @@ export class ItemConverter {
42
50
  // Use projectId if provided, otherwise undefined (direct to specs/)
43
51
  // Enable global collision detection for umbrella/multi-repo setups
44
52
  this.fsIdAllocator = new FSIdAllocator(this.options.projectRoot, this.options.projectId, { globalCollisionDetection: this.options.enableGlobalCollisionDetection });
53
+ // Initialize Epic ID allocator for ADO Capabilities (v0.29.3+)
54
+ // Capabilities are the top-level items in ADO SAFe/Enterprise setups
55
+ // They go to _epics/EP-XXXE/ instead of FS-XXXE/
56
+ this.epicIdAllocator = new EpicIdAllocator(this.options.projectRoot, { globalCollisionDetection: this.options.enableGlobalCollisionDetection });
45
57
  }
46
58
  }
47
59
  /**
@@ -55,14 +67,14 @@ export class ItemConverter {
55
67
  // Generate US-ID with E suffix
56
68
  const id = `US-${String(usId).padStart(3, '0')}E`;
57
69
  // Map external status to SpecWeave status
58
- const status = this.mapStatus(item.status);
70
+ const status = this.markdownGen.mapStatus(item.status);
59
71
  // Extract acceptance criteria
60
72
  const acceptanceCriteria = item.acceptanceCriteria || [];
61
73
  // Generate origin badge
62
- const originBadge = this.generateOriginBadge(item);
74
+ const originBadge = this.markdownGen.generateOriginBadge(item);
63
75
  // Generate markdown content for living docs
64
76
  // CRITICAL (2025-12-01): Include parent info for future re-import parent change detection
65
- const markdown = this.generateMarkdown({
77
+ const markdown = this.markdownGen.generateUserStoryMarkdown({
66
78
  id,
67
79
  title: item.title,
68
80
  description: item.description,
@@ -88,7 +100,7 @@ export class ItemConverter {
88
100
  }
89
101
  });
90
102
  // Generate file path (with feature folder if allocated)
91
- const fileName = this.generateFileName(id, item.title);
103
+ const fileName = this.markdownGen.generateFileName(id, item.title);
92
104
  let filePath;
93
105
  // Check if item should be auto-archived (older than threshold)
94
106
  const shouldArchive = this.shouldAutoArchive(item.createdAt);
@@ -169,7 +181,9 @@ export class ItemConverter {
169
181
  let featureId;
170
182
  if (this.fsIdAllocator && this.options.enableFeatureAllocation && groupItems.length > 0) {
171
183
  const firstItem = groupItems[0];
172
- featureId = await this.allocateFeatureForGroup(firstItem, groupKey);
184
+ // CRITICAL FIX (2025-12-01): Pass ALL group items to check if entire group should be archived
185
+ // This prevents duplicate folders when all items are old
186
+ featureId = await this.allocateFeatureForGroup(firstItem, groupKey, groupItems);
173
187
  }
174
188
  // Convert each item in the group
175
189
  for (let i = 0; i < groupItems.length; i++) {
@@ -210,44 +224,81 @@ export class ItemConverter {
210
224
  }
211
225
  /**
212
226
  * Group items by feature (based on source repo, parent hierarchy, or labels)
213
- * CRITICAL FIX (2025-12-01): For ADO, group by parent Capability/Epic to preserve hierarchy
214
- * CRITICAL FIX (2025-12-01): ADO orphans (no parent) get INDIVIDUAL folders, not 'default' group
215
227
  *
216
- * ADO Hierarchy: Capability Epic Feature → User Story → Task
217
- * - Capability/Epic items become the feature (group leader)
218
- * - User Stories are grouped under their parent Epic/Capability
219
- * - Items without parent (true orphans) get INDIVIDUAL FS-XXXE folders
228
+ * CRITICAL FIX (2025-12-02): Intelligent ADO Hierarchy Mapping for SAFe/Enterprise
229
+ *
230
+ * ADO Hierarchy (5-level SAFe): Capability Epic Feature User Story → Task
231
+ *
232
+ * SpecWeave Mapping:
233
+ * - Capability (ADO 5th level) → SpecWeave Epic (_epics/EP-XXXE/)
234
+ * - Epic (ADO 4th level, child of Capability) → SpecWeave Feature (FS-XXXE/) with parent ref
235
+ * - Epic (ADO 4th level, standalone) → SpecWeave Epic (_epics/EP-XXXE/)
236
+ * - Feature (ADO 3rd level) → SpecWeave Feature (FS-XXXE/)
237
+ * - User Story → us-xxxe.md in parent Feature folder
238
+ *
239
+ * Group Key Prefixes:
240
+ * - "epic:" → Item goes to _epics/EP-XXXE/ (Capability or standalone Epic)
241
+ * - "feature:" → Item goes to FS-XXXE/ (Epic under Capability, or Feature)
242
+ * - "orphan:" → Individual FS-XXXE/ folder (no parent)
243
+ * - "missing-parent:" → Grouped by missing parent ID
220
244
  */
221
245
  groupItemsByFeature(items) {
222
246
  const groups = new Map();
223
- // CRITICAL FIX (2025-12-01): Detect ADO items even without hierarchy info
224
- // ADO items have adoProjectName set by the importer
225
- // Previous bug: Orphan ADO items (no parentId, not epics) fell through to 'default' group
247
+ // Detect ADO items
226
248
  const hasAdoItems = items.some(item => item.platform === 'ado' && item.adoProjectName);
227
- const hasAdoHierarchy = items.some(item => item.platform === 'ado' && (item.parentId || item.type === 'epic'));
228
- // Process ADO items: with hierarchy (group by parent) OR orphans (individual folders)
249
+ // Process ADO items with intelligent hierarchy mapping
229
250
  if (hasAdoItems) {
230
251
  // Build a map of all items by ID for lookup
231
252
  const itemById = new Map();
232
253
  for (const item of items) {
233
254
  itemById.set(item.id, item);
234
255
  }
235
- // Find all "feature-level" items (Capability, Epic, Feature)
236
- const featureLevelTypes = new Set(['capability', 'epic', 'feature']);
237
- const featureItems = items.filter(item => {
238
- const witType = item.adoWorkItemType?.toLowerCase() || item.type;
239
- return featureLevelTypes.has(witType);
240
- });
241
- // Create groups for each feature-level item
256
+ // CRITICAL: Separate Capability (top-level) from other feature-level items
257
+ // Capabilities go to _epics/, everything else goes to FS-XXXE/
258
+ const capabilityItems = items.filter(item => item.adoWorkItemType?.toLowerCase() === 'capability');
259
+ // Epics and Features are "feature-level" items
260
+ // But Epics with Capability parents become Features (FS-XXXE)
261
+ // Epics without Capability parents become Epics (_epics/EP-XXXE)
262
+ const epicItems = items.filter(item => item.adoWorkItemType?.toLowerCase() === 'epic');
263
+ const featureItems = items.filter(item => item.adoWorkItemType?.toLowerCase() === 'feature');
264
+ // Create groups for Capabilities → _epics/EP-XXXE/
265
+ for (const capItem of capabilityItems) {
266
+ const groupKey = `epic:${capItem.id}`;
267
+ if (!groups.has(groupKey)) {
268
+ groups.set(groupKey, []);
269
+ }
270
+ groups.get(groupKey).push(capItem);
271
+ }
272
+ // Process Epics: determine if they're children of Capabilities or standalone
273
+ for (const epicItem of epicItems) {
274
+ let groupKey;
275
+ // Check if Epic has a Capability parent
276
+ const hasCapabilityParent = epicItem.parentId &&
277
+ capabilityItems.some(cap => cap.id === epicItem.parentId);
278
+ if (hasCapabilityParent) {
279
+ // Epic with Capability parent → FS-XXXE/ (Feature level)
280
+ // The Epic becomes a Feature folder, with reference to parent Epic
281
+ groupKey = `feature:${epicItem.id}`;
282
+ }
283
+ else {
284
+ // Standalone Epic (no Capability parent) → _epics/EP-XXXE/
285
+ groupKey = `epic:${epicItem.id}`;
286
+ }
287
+ if (!groups.has(groupKey)) {
288
+ groups.set(groupKey, []);
289
+ }
290
+ groups.get(groupKey).push(epicItem);
291
+ }
292
+ // Create groups for Features → FS-XXXE/
242
293
  for (const featureItem of featureItems) {
243
294
  const groupKey = `feature:${featureItem.id}`;
244
295
  if (!groups.has(groupKey)) {
245
296
  groups.set(groupKey, []);
246
297
  }
247
- // The feature item itself goes first (used to generate FEATURE.md)
248
298
  groups.get(groupKey).push(featureItem);
249
299
  }
250
300
  // Assign child items (User Stories, Tasks, Bugs) to their parent groups
301
+ const featureLevelTypes = new Set(['capability', 'epic', 'feature']);
251
302
  for (const item of items) {
252
303
  const witType = item.adoWorkItemType?.toLowerCase() || item.type;
253
304
  if (featureLevelTypes.has(witType)) {
@@ -259,29 +310,40 @@ export class ItemConverter {
259
310
  // Try to find parent in our items
260
311
  const parent = itemById.get(item.parentId);
261
312
  if (parent) {
262
- // Find the feature-level ancestor
313
+ // Find the appropriate ancestor group
263
314
  let current = parent;
264
315
  while (current) {
265
316
  const currentType = current.adoWorkItemType?.toLowerCase() || current.type;
266
- if (featureLevelTypes.has(currentType)) {
317
+ // Check if this ancestor is a Capability (→ epic group)
318
+ if (currentType === 'capability') {
319
+ groupKey = `epic:${current.id}`;
320
+ break;
321
+ }
322
+ // Check if this ancestor is an Epic
323
+ if (currentType === 'epic') {
324
+ // Check if Epic has Capability parent (→ feature group)
325
+ const epicHasCapParent = current.parentId &&
326
+ capabilityItems.some(cap => cap.id === current.parentId);
327
+ groupKey = epicHasCapParent
328
+ ? `feature:${current.id}`
329
+ : `epic:${current.id}`;
330
+ break;
331
+ }
332
+ // Check if this ancestor is a Feature (→ feature group)
333
+ if (currentType === 'feature') {
267
334
  groupKey = `feature:${current.id}`;
268
335
  break;
269
336
  }
270
337
  current = current.parentId ? itemById.get(current.parentId) : undefined;
271
338
  }
272
339
  }
273
- // NOTE: If parent NOT in dataset, fall through to orphan handling below
274
- // DO NOT group by parentId - we want individual FS-XXXE folders per item
340
+ else {
341
+ // Parent NOT in dataset - group by parentId so siblings stay together
342
+ groupKey = `missing-parent:${item.parentId}`;
343
+ }
275
344
  }
276
- // CRITICAL FIX (2025-12-01): For orphan items, create INDIVIDUAL FS-XXXE
277
- // folders per item. This applies when:
278
- // - Item has no parentId (true orphan)
279
- // - Item has parentId but parent NOT in dataset (orphan with missing parent)
280
- // Each orphan gets its own feature folder with US title as FEATURE.md header
281
- // UNIVERSAL: Applies to ALL platforms (ADO, JIRA, GitHub) for consistent handling
345
+ // For TRUE orphan items (no parentId at all), create INDIVIDUAL folders
282
346
  if (!groupKey) {
283
- // Each orphan gets its own feature folder with unique group key
284
- // This ensures consistent behavior across all platforms
285
347
  groupKey = `orphan:${item.id}`;
286
348
  }
287
349
  if (!groups.has(groupKey)) {
@@ -303,23 +365,79 @@ export class ItemConverter {
303
365
  }
304
366
  /**
305
367
  * Allocate a feature ID for a group of items
306
- * CRITICAL FIX (2025-12-01): Use feature-level item (Capability/Epic) info for FEATURE.md
368
+ *
369
+ * CRITICAL FIX (2025-12-02): Handle "epic:" groups using EpicIdAllocator
370
+ *
371
+ * Group Key Handling:
372
+ * - "epic:..." → Use EpicIdAllocator, create in _epics/EP-XXXE/
373
+ * - "feature:..." → Use FSIdAllocator, create in FS-XXXE/
374
+ * - "orphan:..." → Use FSIdAllocator, create in FS-XXXE/
375
+ * - Other → Use FSIdAllocator
376
+ *
377
+ * @returns Feature ID (FS-XXXE) or Epic ID (EP-XXXE)
307
378
  */
308
- async allocateFeatureForGroup(firstItem, groupKey) {
309
- // Check if we already created a feature for this group
310
- if (this.createdFeatures.has(groupKey)) {
311
- return this.createdFeatures.get(groupKey);
379
+ async allocateFeatureForGroup(firstItem, groupKey, allGroupItems) {
380
+ // CRITICAL: Detect if this is an "epic:" group (Capability or standalone Epic)
381
+ const isEpicGroup = groupKey.startsWith('epic:');
382
+ // Check cache based on group type
383
+ if (isEpicGroup) {
384
+ if (this.createdEpics.has(groupKey)) {
385
+ return this.createdEpics.get(groupKey);
386
+ }
387
+ }
388
+ else {
389
+ if (this.createdFeatures.has(groupKey)) {
390
+ return this.createdFeatures.get(groupKey);
391
+ }
392
+ }
393
+ // For "epic:" groups, use EpicIdAllocator
394
+ if (isEpicGroup) {
395
+ if (!this.epicIdAllocator) {
396
+ throw new Error('EpicIdAllocator not initialized');
397
+ }
398
+ // Create external epic item for allocation
399
+ const epicItem = {
400
+ externalId: firstItem.id,
401
+ title: firstItem.title,
402
+ createdAt: firstItem.createdAt.toISOString(),
403
+ externalUrl: firstItem.url,
404
+ workItemType: firstItem.adoWorkItemType || 'Capability'
405
+ };
406
+ // Allocate Epic ID
407
+ const allocation = await this.epicIdAllocator.allocateId(epicItem);
408
+ const epicId = allocation.id;
409
+ // Create Epic folder in _epics/
410
+ const metadata = createExternalMetadata({
411
+ id: epicId,
412
+ source: firstItem.platform,
413
+ externalId: firstItem.id,
414
+ externalUrl: firstItem.url,
415
+ externalTitle: firstItem.title
416
+ });
417
+ await this.epicIdAllocator.createEpicFolder(epicId, epicItem, metadata);
418
+ // Cache for reuse
419
+ this.createdEpics.set(groupKey, epicId);
420
+ // Notify callback
421
+ if (this.options.onFeatureCreated) {
422
+ this.options.onFeatureCreated(epicId, path.join(this.options.specsDir, '_epics', epicId));
423
+ }
424
+ return epicId;
312
425
  }
426
+ // For "feature:" and other groups, use FSIdAllocator
313
427
  if (!this.fsIdAllocator) {
314
428
  throw new Error('FSIdAllocator not initialized');
315
429
  }
316
- // For ADO hierarchy groups (feature:ADO-XXX), the first item IS the feature-level item
430
+ // For ADO feature groups, check if it's an Epic with Capability parent
431
+ // If so, include parent reference in the feature folder
317
432
  const isAdoFeatureGroup = groupKey.startsWith('feature:');
318
433
  const featureLevelTypes = new Set(['capability', 'epic', 'feature']);
319
434
  const isFeatureLevelItem = isAdoFeatureGroup &&
320
435
  featureLevelTypes.has(firstItem.adoWorkItemType?.toLowerCase() || firstItem.type);
436
+ // Check if this Epic has a Capability parent
437
+ const isEpicWithCapParent = firstItem.adoWorkItemType?.toLowerCase() === 'epic' &&
438
+ firstItem.parentId &&
439
+ this.createdEpics.has(`epic:${firstItem.parentId}`);
321
440
  // Create external work item for allocation
322
- // For ADO feature groups, use the Capability/Epic's title as the feature name
323
441
  const workItem = {
324
442
  externalId: firstItem.id,
325
443
  title: isFeatureLevelItem ? firstItem.title : (firstItem.sourceRepo || firstItem.title),
@@ -329,8 +447,15 @@ export class ItemConverter {
329
447
  // Allocate feature ID
330
448
  const allocation = await this.fsIdAllocator.allocateId(workItem);
331
449
  const featureId = allocation.id;
450
+ // Check if ALL items in the group should be archived
451
+ const shouldArchiveFeature = this.shouldArchiveEntireGroup(allGroupItems || [firstItem]);
332
452
  // Create feature folder with FEATURE.md
333
- const featurePath = await this.createFeatureFolder(featureId, firstItem, groupKey);
453
+ // If Epic with Capability parent, include parent reference
454
+ const parentEpicId = isEpicWithCapParent
455
+ ? this.createdEpics.get(`epic:${firstItem.parentId}`)
456
+ : undefined;
457
+ const featurePath = await this.createFeatureFolder(featureId, firstItem, groupKey, shouldArchiveFeature, parentEpicId // Pass parent Epic ID for reference
458
+ );
334
459
  // Cache for reuse
335
460
  this.createdFeatures.set(groupKey, featureId);
336
461
  // Notify callback
@@ -339,6 +464,29 @@ export class ItemConverter {
339
464
  }
340
465
  return featureId;
341
466
  }
467
+ /**
468
+ * Check if ALL items in a group should be archived
469
+ * CRITICAL FIX (2025-12-01): Prevents duplicate folder creation
470
+ *
471
+ * If ALL items are old (should be archived), the feature folder itself
472
+ * should be created in _archive/ to avoid having an empty FS-XXX/ folder
473
+ * in the main location while all content is in _archive/FS-XXX/
474
+ *
475
+ * @param items - All items in the feature group
476
+ * @returns True if ALL items should be archived
477
+ */
478
+ shouldArchiveEntireGroup(items) {
479
+ if (items.length === 0) {
480
+ return false;
481
+ }
482
+ // Check if auto-archiving is disabled
483
+ const threshold = this.options.autoArchiveAfterDays;
484
+ if (!threshold || threshold <= 0) {
485
+ return false;
486
+ }
487
+ // ALL items must be old enough to archive the entire group
488
+ return items.every(item => this.shouldAutoArchive(item.createdAt));
489
+ }
342
490
  /**
343
491
  * Get base directory for specs, handling both 1-level and 2-level structures
344
492
  *
@@ -374,12 +522,23 @@ export class ItemConverter {
374
522
  }
375
523
  /**
376
524
  * Create feature folder with FEATURE.md
377
- * CRITICAL FIX (2025-12-01): Generate proper FEATURE.md for ADO Capability/Epic hierarchy
525
+ *
526
+ * CRITICAL FIX (2025-12-02): Support parent Epic reference for ADO Epic→Feature mapping
527
+ *
528
+ * @param featureId - Feature ID (e.g., "FS-001E")
529
+ * @param firstItem - First item in the group (used for metadata)
530
+ * @param groupKey - Group key for caching
531
+ * @param shouldArchive - If true, create feature folder in _archive/ directory
532
+ * @param parentEpicId - Optional parent Epic ID (EP-XXXE) for hierarchy reference
378
533
  */
379
- async createFeatureFolder(featureId, firstItem, groupKey) {
534
+ async createFeatureFolder(featureId, firstItem, groupKey, shouldArchive = false, parentEpicId) {
380
535
  // Use 2-level or 1-level structure based on externalContainer
381
536
  const baseDir = this.getBaseDirectory();
382
- const featurePath = path.join(baseDir, featureId);
537
+ // CRITICAL FIX (2025-12-01): Create in _archive if all items in group are old
538
+ // This prevents duplicate folders: FS-XXX/ (empty) + _archive/FS-XXX/ (with content)
539
+ const featurePath = shouldArchive
540
+ ? path.join(baseDir, '_archive', featureId)
541
+ : path.join(baseDir, featureId);
383
542
  // Create directory
384
543
  fs.mkdirSync(featurePath, { recursive: true });
385
544
  // Check if this is a feature-level item or orphan
@@ -443,6 +602,11 @@ export class ItemConverter {
443
602
  }
444
603
  // Include external metadata for feature-level items and all orphans
445
604
  const includeExternalMetadata = isAdoFeatureLevelItem || isOrphanGroup;
605
+ // Build parent Epic reference if available (for ADO Epic → Feature mapping)
606
+ const hasParentEpic = parentEpicId !== undefined;
607
+ const parentEpicLink = hasParentEpic
608
+ ? `[${parentEpicId}](../_epics/${parentEpicId}/EPIC.md)`
609
+ : '';
446
610
  const featureContent = `---
447
611
  id: ${featureId}
448
612
  title: ${featureTitle}
@@ -453,6 +617,7 @@ ${firstItem.adoProjectName ? `ado_project: ${firstItem.adoProjectName}` : ''}
453
617
  ${firstItem.adoAreaPath ? `ado_area_path: ${firstItem.adoAreaPath}` : ''}
454
618
  ${includeExternalMetadata ? `work_item_type: ${witTypeLabel}` : ''}
455
619
  ${includeExternalMetadata ? `external_id: ${firstItem.id}` : ''}
620
+ ${hasParentEpic ? `parent_epic: ${parentEpicId}` : ''}
456
621
  ${isOrphanGroup ? `orphan: true` : ''}
457
622
  created: ${new Date().toISOString()}
458
623
  ---
@@ -460,6 +625,7 @@ created: ${new Date().toISOString()}
460
625
  # ${featureTitle}
461
626
 
462
627
  **Origin**: 🔗 ${externalLink || `Imported from ${platformLabel}`}
628
+ ${hasParentEpic ? `\n**Parent Epic**: ${parentEpicLink}` : ''}
463
629
 
464
630
  ## Description
465
631
 
@@ -467,6 +633,7 @@ ${description}
467
633
 
468
634
  ${firstItem.sourceRepo ? `**Source Repository**: ${firstItem.sourceRepo}` : ''}
469
635
  ${firstItem.adoAreaPath ? `**Area Path**: ${firstItem.adoAreaPath}` : ''}
636
+ ${hasParentEpic ? `\n> **Hierarchy**: This feature belongs to Epic ${parentEpicId} (Capability in Azure DevOps).` : ''}
470
637
  ${isOrphanGroup ? `\n> **Note**: This feature was created from an orphan item (no parent Epic/Feature in ${platformLabel} or parent not imported).` : ''}
471
638
 
472
639
  ## User Stories
@@ -478,6 +645,7 @@ User stories in this ${witTypeLabel.toLowerCase()} will be listed here.
478
645
  - **Created**: ${new Date().toISOString()}
479
646
  - **Source**: ${platformLabel}
480
647
  ${includeExternalMetadata ? `- **External ID**: ${firstItem.id}` : ''}
648
+ ${hasParentEpic ? `- **Parent Epic**: ${parentEpicId}` : ''}
481
649
  ${isOrphanGroup ? `- **Type**: Orphan (no parent Epic)` : ''}
482
650
  `;
483
651
  // Write FEATURE.md
@@ -485,18 +653,7 @@ ${isOrphanGroup ? `- **Type**: Orphan (no parent Epic)` : ''}
485
653
  fs.writeFileSync(featureFile, featureContent, 'utf-8');
486
654
  return featurePath;
487
655
  }
488
- /**
489
- * Map external status to SpecWeave status
490
- */
491
- mapStatus(externalStatus) {
492
- const statusMap = {
493
- 'open': 'Open',
494
- 'in-progress': 'In Progress',
495
- 'completed': 'Completed',
496
- 'closed': 'Completed'
497
- };
498
- return statusMap[externalStatus] || 'Open';
499
- }
656
+ // mapStatus moved to MarkdownGenerator
500
657
  /**
501
658
  * Check if an item should be auto-archived based on creation date
502
659
  *
@@ -643,126 +800,9 @@ ${isOrphanGroup ? `- **Type**: Orphan (no parent Epic)` : ''}
643
800
  // Folder cleanup is best-effort, don't fail on errors
644
801
  }
645
802
  }
646
- /**
647
- * Generate origin badge for living docs
648
- */
649
- generateOriginBadge(item) {
650
- const platformEmoji = {
651
- 'github': '🔗',
652
- 'jira': '🔗',
653
- 'ado': '🔗'
654
- };
655
- const platformName = {
656
- 'github': 'GitHub',
657
- 'jira': 'JIRA',
658
- 'ado': 'Azure DevOps'
659
- };
660
- const emoji = platformEmoji[item.platform] || '🔗';
661
- const name = platformName[item.platform] || item.platform;
662
- // Extract issue/ticket number from external ID
663
- // v0.29+ format: github#owner/repo#123 → extract "123"
664
- // Legacy format: github#123 → extract "123"
665
- let issueNumber = item.id;
666
- const newFormatMatch = item.id.match(/#(\d+)$/);
667
- if (newFormatMatch) {
668
- issueNumber = newFormatMatch[1];
669
- }
670
- else {
671
- issueNumber = item.id.replace(/^(GITHUB|JIRA|ADO)-/, '');
672
- }
673
- return `${emoji} [${name} #${issueNumber}](${item.url})`;
674
- }
675
- /**
676
- * Generate markdown content for living docs User Story
677
- * CRITICAL (2025-12-01): Includes parent tracking info for re-import hierarchy updates
678
- */
679
- generateMarkdown(data) {
680
- const parts = [];
681
- // Title
682
- parts.push(`# ${data.id}: ${data.title}`);
683
- parts.push('');
684
- // Origin badge
685
- parts.push(`**Origin**: ${data.originBadge}`);
686
- parts.push('');
687
- // Status and Priority
688
- parts.push(`**Status**: ${data.status}`);
689
- if (data.priority) {
690
- parts.push(`**Priority**: ${data.priority}`);
691
- }
692
- parts.push('');
693
- // Description
694
- parts.push('## Description');
695
- parts.push('');
696
- parts.push(data.description || 'No description provided.');
697
- parts.push('');
698
- // Acceptance Criteria
699
- if (data.acceptanceCriteria.length > 0) {
700
- parts.push('## Acceptance Criteria');
701
- parts.push('');
702
- data.acceptanceCriteria.forEach((ac, index) => {
703
- const acId = `AC-${data.id.replace('E', '')}-${String(index + 1).padStart(2, '0')}`;
704
- parts.push(`- [ ] **${acId}**: ${ac}`);
705
- });
706
- parts.push('');
707
- }
708
- // Tasks
709
- parts.push('## Tasks');
710
- parts.push('');
711
- parts.push('> **Note**: This User Story was imported from an external tool.');
712
- parts.push('> Create tasks manually when ready to implement.');
713
- parts.push('');
714
- // Metadata (frontmatter-style at bottom)
715
- parts.push('---');
716
- parts.push('');
717
- parts.push('## External Metadata');
718
- parts.push('');
719
- parts.push(`- **External ID**: ${data.metadata.externalId}`);
720
- parts.push(`- **External URL**: ${data.metadata.externalUrl}`);
721
- parts.push(`- **Platform**: ${data.metadata.externalPlatform}`);
722
- parts.push(`- **Imported At**: ${data.metadata.importedAt}`);
723
- parts.push(`- **Created At**: ${data.metadata.createdAt}`);
724
- parts.push(`- **Updated At**: ${data.metadata.updatedAt}`);
725
- if (data.metadata.labels.length > 0) {
726
- parts.push(`- **Labels**: ${data.metadata.labels.join(', ')}`);
727
- }
728
- if (data.metadata.sourceRepo) {
729
- parts.push(`- **Source Repository**: ${data.metadata.sourceRepo}`);
730
- }
731
- // Parent tracking for re-import hierarchy updates (CRITICAL for parent change detection)
732
- if (data.metadata.featureId) {
733
- parts.push(`- **Feature ID**: ${data.metadata.featureId}`);
734
- }
735
- if (data.metadata.parentId) {
736
- parts.push(`- **Parent ID**: ${data.metadata.parentId}`);
737
- }
738
- if (data.metadata.isOrphan) {
739
- parts.push(`- **Orphan**: true (no parent in external tool)`);
740
- }
741
- if (data.metadata.adoWorkItemType) {
742
- parts.push(`- **ADO Work Item Type**: ${data.metadata.adoWorkItemType}`);
743
- }
744
- if (data.metadata.adoAreaPath) {
745
- parts.push(`- **ADO Area Path**: ${data.metadata.adoAreaPath}`);
746
- }
747
- return parts.join('\n');
748
- }
749
- /**
750
- * Generate file name for living docs User Story
751
- *
752
- * Format: us-001e-title-here.md
753
- */
754
- generateFileName(usId, title) {
755
- // Convert US-001E to us-001e
756
- const idPart = usId.toLowerCase();
757
- // Convert title to kebab-case
758
- const titlePart = title
759
- .toLowerCase()
760
- .replace(/[^a-z0-9\s-]/g, '') // Remove special chars
761
- .replace(/\s+/g, '-') // Replace spaces with hyphens
762
- .replace(/-+/g, '-') // Remove duplicate hyphens
763
- .slice(0, 50); // Limit to 50 chars
764
- return `${idPart}-${titlePart}.md`;
765
- }
803
+ // generateOriginBadge moved to MarkdownGenerator
804
+ // generateMarkdown moved to MarkdownGenerator
805
+ // generateFileName moved to MarkdownGenerator
766
806
  /**
767
807
  * Validate that no increments were created
768
808
  *