specweave 0.18.1 → 0.20.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 (384) hide show
  1. package/CLAUDE.md +229 -1817
  2. package/README.md +68 -0
  3. package/bin/specweave.js +62 -6
  4. package/dist/plugins/specweave/lib/hooks/sync-living-docs.d.ts.map +1 -1
  5. package/dist/plugins/specweave/lib/hooks/sync-living-docs.js +3 -0
  6. package/dist/plugins/specweave/lib/hooks/sync-living-docs.js.map +1 -1
  7. package/dist/plugins/specweave/lib/hooks/update-ac-status.d.ts +21 -0
  8. package/dist/plugins/specweave/lib/hooks/update-ac-status.d.ts.map +1 -0
  9. package/dist/plugins/specweave/lib/hooks/update-ac-status.js +162 -0
  10. package/dist/plugins/specweave/lib/hooks/update-ac-status.js.map +1 -0
  11. package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.d.ts.map +1 -1
  12. package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js +65 -6
  13. package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js.map +1 -1
  14. package/dist/plugins/specweave-github/lib/completion-calculator.d.ts +112 -0
  15. package/dist/plugins/specweave-github/lib/completion-calculator.d.ts.map +1 -0
  16. package/dist/plugins/specweave-github/lib/completion-calculator.js +301 -0
  17. package/dist/plugins/specweave-github/lib/completion-calculator.js.map +1 -0
  18. package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts +3 -3
  19. package/dist/plugins/specweave-github/lib/duplicate-detector.js +3 -3
  20. package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts +7 -0
  21. package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts.map +1 -1
  22. package/dist/plugins/specweave-github/lib/epic-content-builder.js +42 -0
  23. package/dist/plugins/specweave-github/lib/epic-content-builder.js.map +1 -1
  24. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +14 -0
  25. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
  26. package/dist/plugins/specweave-github/lib/github-client-v2.js +51 -0
  27. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  28. package/dist/plugins/specweave-github/lib/github-epic-sync.js +1 -1
  29. package/dist/plugins/specweave-github/lib/github-epic-sync.js.map +1 -1
  30. package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts +87 -0
  31. package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -0
  32. package/dist/plugins/specweave-github/lib/github-feature-sync.js +412 -0
  33. package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -0
  34. package/dist/plugins/specweave-github/lib/github-spec-content-sync.d.ts.map +1 -1
  35. package/dist/plugins/specweave-github/lib/github-spec-content-sync.js +64 -13
  36. package/dist/plugins/specweave-github/lib/github-spec-content-sync.js.map +1 -1
  37. package/dist/plugins/specweave-github/lib/progress-comment-builder.d.ts +78 -0
  38. package/dist/plugins/specweave-github/lib/progress-comment-builder.d.ts.map +1 -0
  39. package/dist/plugins/specweave-github/lib/progress-comment-builder.js +237 -0
  40. package/dist/plugins/specweave-github/lib/progress-comment-builder.js.map +1 -0
  41. package/dist/plugins/specweave-github/lib/user-story-content-builder.d.ts +97 -0
  42. package/dist/plugins/specweave-github/lib/user-story-content-builder.d.ts.map +1 -0
  43. package/dist/plugins/specweave-github/lib/user-story-content-builder.js +301 -0
  44. package/dist/plugins/specweave-github/lib/user-story-content-builder.js.map +1 -0
  45. package/dist/plugins/specweave-github/lib/user-story-issue-builder.d.ts +83 -0
  46. package/dist/plugins/specweave-github/lib/user-story-issue-builder.d.ts.map +1 -0
  47. package/dist/plugins/specweave-github/lib/user-story-issue-builder.js +386 -0
  48. package/dist/plugins/specweave-github/lib/user-story-issue-builder.js.map +1 -0
  49. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts +8 -6
  50. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts.map +1 -1
  51. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js +78 -117
  52. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js.map +1 -1
  53. package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.d.ts +57 -0
  54. package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.d.ts.map +1 -0
  55. package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.js +248 -0
  56. package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.js.map +1 -0
  57. package/dist/plugins/specweave-kafka/lib/cli/types.d.ts +82 -0
  58. package/dist/plugins/specweave-kafka/lib/cli/types.d.ts.map +1 -0
  59. package/dist/plugins/specweave-kafka/lib/cli/types.js +13 -0
  60. package/dist/plugins/specweave-kafka/lib/cli/types.js.map +1 -0
  61. package/dist/plugins/specweave-kafka/lib/mcp/detector.d.ts +49 -0
  62. package/dist/plugins/specweave-kafka/lib/mcp/detector.d.ts.map +1 -0
  63. package/dist/plugins/specweave-kafka/lib/mcp/detector.js +316 -0
  64. package/dist/plugins/specweave-kafka/lib/mcp/detector.js.map +1 -0
  65. package/dist/plugins/specweave-kafka/lib/mcp/types.d.ts +70 -0
  66. package/dist/plugins/specweave-kafka/lib/mcp/types.d.ts.map +1 -0
  67. package/dist/plugins/specweave-kafka/lib/mcp/types.js +23 -0
  68. package/dist/plugins/specweave-kafka/lib/mcp/types.js.map +1 -0
  69. package/dist/plugins/specweave-kafka/lib/utils/partitioning.d.ts +85 -0
  70. package/dist/plugins/specweave-kafka/lib/utils/partitioning.d.ts.map +1 -0
  71. package/dist/plugins/specweave-kafka/lib/utils/partitioning.js +281 -0
  72. package/dist/plugins/specweave-kafka/lib/utils/partitioning.js.map +1 -0
  73. package/dist/plugins/specweave-kafka/lib/utils/sizing.d.ts +75 -0
  74. package/dist/plugins/specweave-kafka/lib/utils/sizing.d.ts.map +1 -0
  75. package/dist/plugins/specweave-kafka/lib/utils/sizing.js +238 -0
  76. package/dist/plugins/specweave-kafka/lib/utils/sizing.js.map +1 -0
  77. package/dist/src/cli/commands/import-docs.js +4 -4
  78. package/dist/src/cli/commands/import-docs.js.map +1 -1
  79. package/dist/src/cli/commands/init-multiproject.d.ts.map +1 -1
  80. package/dist/src/cli/commands/init-multiproject.js +17 -18
  81. package/dist/src/cli/commands/init-multiproject.js.map +1 -1
  82. package/dist/src/cli/commands/migrate-to-multiproject.d.ts.map +1 -1
  83. package/dist/src/cli/commands/migrate-to-multiproject.js +8 -4
  84. package/dist/src/cli/commands/migrate-to-multiproject.js.map +1 -1
  85. package/dist/src/cli/commands/switch-project.d.ts.map +1 -1
  86. package/dist/src/cli/commands/switch-project.js +9 -26
  87. package/dist/src/cli/commands/switch-project.js.map +1 -1
  88. package/dist/src/cli/commands/sync-spec-content.js +3 -0
  89. package/dist/src/cli/commands/sync-spec-content.js.map +1 -1
  90. package/dist/src/core/deduplication/command-deduplicator.d.ts +166 -0
  91. package/dist/src/core/deduplication/command-deduplicator.d.ts.map +1 -0
  92. package/dist/src/core/deduplication/command-deduplicator.js +254 -0
  93. package/dist/src/core/deduplication/command-deduplicator.js.map +1 -0
  94. package/dist/src/core/increment/active-increment-manager.d.ts +42 -15
  95. package/dist/src/core/increment/active-increment-manager.d.ts.map +1 -1
  96. package/dist/src/core/increment/active-increment-manager.js +113 -46
  97. package/dist/src/core/increment/active-increment-manager.js.map +1 -1
  98. package/dist/src/core/increment/conflict-resolver.d.ts +40 -0
  99. package/dist/src/core/increment/conflict-resolver.d.ts.map +1 -0
  100. package/dist/src/core/increment/conflict-resolver.js +219 -0
  101. package/dist/src/core/increment/conflict-resolver.js.map +1 -0
  102. package/dist/src/core/increment/discipline-checker.d.ts.map +1 -1
  103. package/dist/src/core/increment/discipline-checker.js +7 -1
  104. package/dist/src/core/increment/discipline-checker.js.map +1 -1
  105. package/dist/src/core/increment/duplicate-detector.d.ts +52 -0
  106. package/dist/src/core/increment/duplicate-detector.d.ts.map +1 -0
  107. package/dist/src/core/increment/duplicate-detector.js +276 -0
  108. package/dist/src/core/increment/duplicate-detector.js.map +1 -0
  109. package/dist/src/core/increment/increment-archiver.d.ts +90 -0
  110. package/dist/src/core/increment/increment-archiver.d.ts.map +1 -0
  111. package/dist/src/core/increment/increment-archiver.js +368 -0
  112. package/dist/src/core/increment/increment-archiver.js.map +1 -0
  113. package/dist/src/core/increment/increment-reopener.d.ts +165 -0
  114. package/dist/src/core/increment/increment-reopener.d.ts.map +1 -0
  115. package/dist/src/core/increment/increment-reopener.js +390 -0
  116. package/dist/src/core/increment/increment-reopener.js.map +1 -0
  117. package/dist/src/core/increment/metadata-manager.d.ts +26 -1
  118. package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
  119. package/dist/src/core/increment/metadata-manager.js +143 -5
  120. package/dist/src/core/increment/metadata-manager.js.map +1 -1
  121. package/dist/src/core/increment/recent-work-scanner.d.ts +121 -0
  122. package/dist/src/core/increment/recent-work-scanner.d.ts.map +1 -0
  123. package/dist/src/core/increment/recent-work-scanner.js +303 -0
  124. package/dist/src/core/increment/recent-work-scanner.js.map +1 -0
  125. package/dist/src/core/increment/types.d.ts +1 -0
  126. package/dist/src/core/increment/types.d.ts.map +1 -1
  127. package/dist/src/core/increment-utils.d.ts +112 -0
  128. package/dist/src/core/increment-utils.d.ts.map +1 -0
  129. package/dist/src/core/increment-utils.js +210 -0
  130. package/dist/src/core/increment-utils.js.map +1 -0
  131. package/dist/src/core/living-docs/ac-project-specific-generator.d.ts +65 -0
  132. package/dist/src/core/living-docs/ac-project-specific-generator.d.ts.map +1 -0
  133. package/dist/src/core/living-docs/ac-project-specific-generator.js +175 -0
  134. package/dist/src/core/living-docs/ac-project-specific-generator.js.map +1 -0
  135. package/dist/src/core/living-docs/feature-archiver.d.ts +130 -0
  136. package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -0
  137. package/dist/src/core/living-docs/feature-archiver.js +549 -0
  138. package/dist/src/core/living-docs/feature-archiver.js.map +1 -0
  139. package/dist/src/core/living-docs/feature-id-manager.d.ts +81 -0
  140. package/dist/src/core/living-docs/feature-id-manager.d.ts.map +1 -0
  141. package/dist/src/core/living-docs/feature-id-manager.js +339 -0
  142. package/dist/src/core/living-docs/feature-id-manager.js.map +1 -0
  143. package/dist/src/core/living-docs/hierarchy-mapper.d.ts +144 -83
  144. package/dist/src/core/living-docs/hierarchy-mapper.d.ts.map +1 -1
  145. package/dist/src/core/living-docs/hierarchy-mapper.js +488 -270
  146. package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
  147. package/dist/src/core/living-docs/index.d.ts +6 -0
  148. package/dist/src/core/living-docs/index.d.ts.map +1 -1
  149. package/dist/src/core/living-docs/index.js +6 -0
  150. package/dist/src/core/living-docs/index.js.map +1 -1
  151. package/dist/src/core/living-docs/project-detector.d.ts +6 -0
  152. package/dist/src/core/living-docs/project-detector.d.ts.map +1 -1
  153. package/dist/src/core/living-docs/project-detector.js +35 -1
  154. package/dist/src/core/living-docs/project-detector.js.map +1 -1
  155. package/dist/src/core/living-docs/spec-distributor.d.ts +100 -26
  156. package/dist/src/core/living-docs/spec-distributor.d.ts.map +1 -1
  157. package/dist/src/core/living-docs/spec-distributor.js +1275 -258
  158. package/dist/src/core/living-docs/spec-distributor.js.map +1 -1
  159. package/dist/src/core/living-docs/task-project-specific-generator.d.ts +109 -0
  160. package/dist/src/core/living-docs/task-project-specific-generator.d.ts.map +1 -0
  161. package/dist/src/core/living-docs/task-project-specific-generator.js +221 -0
  162. package/dist/src/core/living-docs/task-project-specific-generator.js.map +1 -0
  163. package/dist/src/core/living-docs/types.d.ts +143 -0
  164. package/dist/src/core/living-docs/types.d.ts.map +1 -1
  165. package/dist/src/core/project-manager.d.ts +2 -17
  166. package/dist/src/core/project-manager.d.ts.map +1 -1
  167. package/dist/src/core/project-manager.js +68 -48
  168. package/dist/src/core/project-manager.js.map +1 -1
  169. package/dist/src/core/spec-content-sync.d.ts +1 -1
  170. package/dist/src/core/spec-content-sync.d.ts.map +1 -1
  171. package/dist/src/core/sync/enhanced-content-builder.d.ts.map +1 -1
  172. package/dist/src/core/sync/enhanced-content-builder.js +2 -1
  173. package/dist/src/core/sync/enhanced-content-builder.js.map +1 -1
  174. package/dist/src/core/sync/performance-optimizer.d.ts +153 -0
  175. package/dist/src/core/sync/performance-optimizer.d.ts.map +1 -0
  176. package/dist/src/core/sync/performance-optimizer.js +220 -0
  177. package/dist/src/core/sync/performance-optimizer.js.map +1 -0
  178. package/dist/src/core/sync/retry-handler.d.ts +98 -0
  179. package/dist/src/core/sync/retry-handler.d.ts.map +1 -0
  180. package/dist/src/core/sync/retry-handler.js +196 -0
  181. package/dist/src/core/sync/retry-handler.js.map +1 -0
  182. package/dist/src/core/types/config.d.ts +94 -0
  183. package/dist/src/core/types/config.d.ts.map +1 -1
  184. package/dist/src/core/types/config.js +16 -0
  185. package/dist/src/core/types/config.js.map +1 -1
  186. package/dist/src/core/types/increment-metadata.d.ts +6 -0
  187. package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
  188. package/dist/src/core/types/increment-metadata.js +10 -1
  189. package/dist/src/core/types/increment-metadata.js.map +1 -1
  190. package/dist/src/integrations/jira/jira-incremental-mapper.d.ts.map +1 -1
  191. package/dist/src/integrations/jira/jira-incremental-mapper.js +4 -8
  192. package/dist/src/integrations/jira/jira-incremental-mapper.js.map +1 -1
  193. package/dist/src/integrations/jira/jira-mapper.d.ts.map +1 -1
  194. package/dist/src/integrations/jira/jira-mapper.js +4 -8
  195. package/dist/src/integrations/jira/jira-mapper.js.map +1 -1
  196. package/package.json +1 -1
  197. package/plugins/specweave/COMMANDS.md +13 -4
  198. package/plugins/specweave/commands/specweave-abandon.md +22 -20
  199. package/plugins/specweave/commands/specweave-archive-features.md +121 -0
  200. package/plugins/specweave/commands/specweave-archive-increments.md +82 -0
  201. package/plugins/specweave/commands/specweave-archive.md +363 -0
  202. package/plugins/specweave/commands/specweave-backlog.md +211 -0
  203. package/plugins/specweave/commands/specweave-fix-duplicates.md +517 -0
  204. package/plugins/specweave/commands/specweave-increment.md +4 -3
  205. package/plugins/specweave/commands/specweave-progress.md +176 -27
  206. package/plugins/specweave/commands/specweave-reopen.md +391 -0
  207. package/plugins/specweave/commands/specweave-restore-feature.md +90 -0
  208. package/plugins/specweave/commands/specweave-restore.md +309 -0
  209. package/plugins/specweave/commands/specweave-resume.md +51 -23
  210. package/plugins/specweave/commands/specweave-status.md +41 -7
  211. package/plugins/specweave/commands/specweave-sync-specs.md +425 -0
  212. package/plugins/specweave/hooks/hooks.json +4 -0
  213. package/plugins/specweave/hooks/lib/sync-spec-content.sh +2 -2
  214. package/plugins/specweave/hooks/post-task-completion.sh +39 -0
  215. package/plugins/specweave/hooks/pre-command-deduplication.sh +83 -0
  216. package/plugins/specweave/hooks/user-prompt-submit.sh +1 -1
  217. package/plugins/specweave/lib/hooks/sync-living-docs.js +2 -0
  218. package/plugins/specweave/lib/hooks/sync-living-docs.ts +4 -0
  219. package/plugins/specweave/lib/hooks/update-ac-status.js +102 -0
  220. package/plugins/specweave/lib/hooks/update-ac-status.ts +192 -0
  221. package/plugins/specweave/skills/archive-increments/SKILL.md +198 -0
  222. package/plugins/specweave/skills/increment-planner/scripts/feature-utils.js +14 -0
  223. package/plugins/specweave/skills/smart-reopen-detector/SKILL.md +244 -0
  224. package/plugins/specweave-ado/lib/ado-spec-content-sync.js +49 -5
  225. package/plugins/specweave-ado/lib/ado-spec-content-sync.ts +72 -6
  226. package/plugins/specweave-confluent/.claude-plugin/plugin.json +23 -0
  227. package/plugins/specweave-confluent/README.md +375 -0
  228. package/plugins/specweave-confluent/agents/confluent-architect/AGENT.md +306 -0
  229. package/plugins/specweave-confluent/skills/confluent-kafka-connect/SKILL.md +453 -0
  230. package/plugins/specweave-confluent/skills/confluent-ksqldb/SKILL.md +470 -0
  231. package/plugins/specweave-confluent/skills/confluent-schema-registry/SKILL.md +316 -0
  232. package/plugins/specweave-github/agents/github-task-splitter/AGENT.md +2 -2
  233. package/plugins/specweave-github/agents/user-story-updater/AGENT.md +148 -0
  234. package/plugins/specweave-github/commands/specweave-github-cleanup-duplicates.md +1 -1
  235. package/plugins/specweave-github/commands/specweave-github-update-user-story.md +156 -0
  236. package/plugins/specweave-github/hooks/post-task-completion.sh +10 -9
  237. package/plugins/specweave-github/lib/completion-calculator.js +262 -0
  238. package/plugins/specweave-github/lib/completion-calculator.ts +434 -0
  239. package/plugins/specweave-github/lib/duplicate-detector.js +3 -3
  240. package/plugins/specweave-github/lib/duplicate-detector.ts +4 -4
  241. package/plugins/specweave-github/lib/epic-content-builder.js +38 -0
  242. package/plugins/specweave-github/lib/epic-content-builder.ts +59 -0
  243. package/plugins/specweave-github/lib/github-client-v2.js +49 -0
  244. package/plugins/specweave-github/lib/github-client-v2.ts +59 -0
  245. package/plugins/specweave-github/lib/github-epic-sync.ts +1 -1
  246. package/plugins/specweave-github/lib/github-feature-sync.js +381 -0
  247. package/plugins/specweave-github/lib/github-feature-sync.ts +568 -0
  248. package/plugins/specweave-github/lib/github-spec-content-sync.js +40 -10
  249. package/plugins/specweave-github/lib/github-spec-content-sync.ts +82 -14
  250. package/plugins/specweave-github/lib/progress-comment-builder.js +229 -0
  251. package/plugins/specweave-github/lib/progress-comment-builder.ts +324 -0
  252. package/plugins/specweave-github/lib/user-story-content-builder.js +299 -0
  253. package/plugins/specweave-github/lib/user-story-content-builder.ts +413 -0
  254. package/plugins/specweave-github/lib/user-story-issue-builder.js +344 -0
  255. package/plugins/specweave-github/lib/user-story-issue-builder.ts +543 -0
  256. package/plugins/specweave-github/skills/github-issue-standard/SKILL.md +189 -0
  257. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
  258. package/plugins/specweave-jira/lib/{enhanced-jira-sync.ts.disabled → enhanced-jira-sync.ts} +26 -52
  259. package/plugins/specweave-kafka/.claude-plugin/plugin.json +26 -0
  260. package/plugins/specweave-kafka/IMPLEMENTATION-COMPLETE.md +483 -0
  261. package/plugins/specweave-kafka/README.md +242 -0
  262. package/plugins/specweave-kafka/agents/kafka-architect/AGENT.md +235 -0
  263. package/plugins/specweave-kafka/agents/kafka-devops/AGENT.md +209 -0
  264. package/plugins/specweave-kafka/agents/kafka-observability/AGENT.md +266 -0
  265. package/plugins/specweave-kafka/commands/deploy.md +99 -0
  266. package/plugins/specweave-kafka/commands/dev-env.md +176 -0
  267. package/plugins/specweave-kafka/commands/mcp-configure.md +101 -0
  268. package/plugins/specweave-kafka/commands/monitor-setup.md +96 -0
  269. package/plugins/specweave-kafka/docker/kafka-local/docker-compose.yml +187 -0
  270. package/plugins/specweave-kafka/docker/redpanda/docker-compose.yml +199 -0
  271. package/plugins/specweave-kafka/docker/templates/consumer-nodejs.js +225 -0
  272. package/plugins/specweave-kafka/docker/templates/consumer-python.py +220 -0
  273. package/plugins/specweave-kafka/docker/templates/producer-nodejs.js +168 -0
  274. package/plugins/specweave-kafka/docker/templates/producer-python.py +167 -0
  275. package/plugins/specweave-kafka/lib/adapters/apache-kafka-adapter.js +438 -0
  276. package/plugins/specweave-kafka/lib/adapters/apache-kafka-adapter.ts +541 -0
  277. package/plugins/specweave-kafka/lib/adapters/platform-adapter.js +47 -0
  278. package/plugins/specweave-kafka/lib/adapters/platform-adapter.ts +343 -0
  279. package/plugins/specweave-kafka/lib/cli/kcat-wrapper.js +258 -0
  280. package/plugins/specweave-kafka/lib/cli/kcat-wrapper.ts +298 -0
  281. package/plugins/specweave-kafka/lib/cli/types.js +10 -0
  282. package/plugins/specweave-kafka/lib/cli/types.ts +92 -0
  283. package/plugins/specweave-kafka/lib/connectors/connector-catalog.js +305 -0
  284. package/plugins/specweave-kafka/lib/connectors/connector-catalog.ts +528 -0
  285. package/plugins/specweave-kafka/lib/documentation/diagram-generator.js +114 -0
  286. package/plugins/specweave-kafka/lib/documentation/diagram-generator.ts +195 -0
  287. package/plugins/specweave-kafka/lib/documentation/exporter.js +210 -0
  288. package/plugins/specweave-kafka/lib/documentation/exporter.ts +338 -0
  289. package/plugins/specweave-kafka/lib/documentation/schema-catalog-generator.js +60 -0
  290. package/plugins/specweave-kafka/lib/documentation/schema-catalog-generator.ts +130 -0
  291. package/plugins/specweave-kafka/lib/documentation/topology-generator.js +143 -0
  292. package/plugins/specweave-kafka/lib/documentation/topology-generator.ts +290 -0
  293. package/plugins/specweave-kafka/lib/mcp/detector.js +298 -0
  294. package/plugins/specweave-kafka/lib/mcp/detector.ts +352 -0
  295. package/plugins/specweave-kafka/lib/mcp/types.js +21 -0
  296. package/plugins/specweave-kafka/lib/mcp/types.ts +77 -0
  297. package/plugins/specweave-kafka/lib/multi-cluster/cluster-config-manager.js +193 -0
  298. package/plugins/specweave-kafka/lib/multi-cluster/cluster-config-manager.ts +362 -0
  299. package/plugins/specweave-kafka/lib/multi-cluster/cluster-switcher.js +188 -0
  300. package/plugins/specweave-kafka/lib/multi-cluster/cluster-switcher.ts +359 -0
  301. package/plugins/specweave-kafka/lib/multi-cluster/health-aggregator.js +195 -0
  302. package/plugins/specweave-kafka/lib/multi-cluster/health-aggregator.ts +380 -0
  303. package/plugins/specweave-kafka/lib/observability/opentelemetry-kafka.js +209 -0
  304. package/plugins/specweave-kafka/lib/observability/opentelemetry-kafka.ts +358 -0
  305. package/plugins/specweave-kafka/lib/patterns/advanced-ksqldb-patterns.js +354 -0
  306. package/plugins/specweave-kafka/lib/patterns/advanced-ksqldb-patterns.ts +563 -0
  307. package/plugins/specweave-kafka/lib/patterns/circuit-breaker-resilience.js +259 -0
  308. package/plugins/specweave-kafka/lib/patterns/circuit-breaker-resilience.ts +516 -0
  309. package/plugins/specweave-kafka/lib/patterns/dead-letter-queue.js +233 -0
  310. package/plugins/specweave-kafka/lib/patterns/dead-letter-queue.ts +423 -0
  311. package/plugins/specweave-kafka/lib/patterns/exactly-once-semantics.js +266 -0
  312. package/plugins/specweave-kafka/lib/patterns/exactly-once-semantics.ts +445 -0
  313. package/plugins/specweave-kafka/lib/patterns/flink-kafka-integration.js +312 -0
  314. package/plugins/specweave-kafka/lib/patterns/flink-kafka-integration.ts +561 -0
  315. package/plugins/specweave-kafka/lib/patterns/multi-dc-replication.js +289 -0
  316. package/plugins/specweave-kafka/lib/patterns/multi-dc-replication.ts +607 -0
  317. package/plugins/specweave-kafka/lib/patterns/rate-limiting-backpressure.js +264 -0
  318. package/plugins/specweave-kafka/lib/patterns/rate-limiting-backpressure.ts +498 -0
  319. package/plugins/specweave-kafka/lib/patterns/stream-processing-optimization.js +263 -0
  320. package/plugins/specweave-kafka/lib/patterns/stream-processing-optimization.ts +549 -0
  321. package/plugins/specweave-kafka/lib/patterns/tiered-storage-compaction.js +205 -0
  322. package/plugins/specweave-kafka/lib/patterns/tiered-storage-compaction.ts +399 -0
  323. package/plugins/specweave-kafka/lib/performance/performance-optimizer.js +249 -0
  324. package/plugins/specweave-kafka/lib/performance/performance-optimizer.ts +427 -0
  325. package/plugins/specweave-kafka/lib/security/kafka-security.js +252 -0
  326. package/plugins/specweave-kafka/lib/security/kafka-security.ts +494 -0
  327. package/plugins/specweave-kafka/lib/utils/capacity-planner.js +203 -0
  328. package/plugins/specweave-kafka/lib/utils/capacity-planner.ts +469 -0
  329. package/plugins/specweave-kafka/lib/utils/config-validator.js +419 -0
  330. package/plugins/specweave-kafka/lib/utils/config-validator.ts +564 -0
  331. package/plugins/specweave-kafka/lib/utils/partitioning.js +329 -0
  332. package/plugins/specweave-kafka/lib/utils/partitioning.ts +473 -0
  333. package/plugins/specweave-kafka/lib/utils/sizing.js +221 -0
  334. package/plugins/specweave-kafka/lib/utils/sizing.ts +374 -0
  335. package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-broker-metrics.json +628 -0
  336. package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-cluster-overview.json +564 -0
  337. package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-consumer-lag.json +509 -0
  338. package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-jvm-metrics.json +674 -0
  339. package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-topic-metrics.json +578 -0
  340. package/plugins/specweave-kafka/monitoring/grafana/provisioning/dashboards/kafka.yml +17 -0
  341. package/plugins/specweave-kafka/monitoring/grafana/provisioning/datasources/prometheus.yml +17 -0
  342. package/plugins/specweave-kafka/monitoring/prometheus/kafka-alerts.yml +415 -0
  343. package/plugins/specweave-kafka/monitoring/prometheus/kafka-jmx-exporter.yml +256 -0
  344. package/plugins/specweave-kafka/package.json +41 -0
  345. package/plugins/specweave-kafka/skills/kafka-architecture/SKILL.md +647 -0
  346. package/plugins/specweave-kafka/skills/kafka-cli-tools/SKILL.md +433 -0
  347. package/plugins/specweave-kafka/skills/kafka-iac-deployment/SKILL.md +449 -0
  348. package/plugins/specweave-kafka/skills/kafka-kubernetes/SKILL.md +667 -0
  349. package/plugins/specweave-kafka/skills/kafka-mcp-integration/SKILL.md +273 -0
  350. package/plugins/specweave-kafka/skills/kafka-observability/SKILL.md +576 -0
  351. package/plugins/specweave-kafka/templates/config/broker-production.properties +254 -0
  352. package/plugins/specweave-kafka/templates/config/consumer-low-latency.properties +112 -0
  353. package/plugins/specweave-kafka/templates/config/producer-high-throughput.properties +120 -0
  354. package/plugins/specweave-kafka/templates/migration/mirrormaker2-config.properties +234 -0
  355. package/plugins/specweave-kafka/templates/monitoring/grafana/multi-cluster-dashboard.json +686 -0
  356. package/plugins/specweave-kafka/terraform/apache-kafka/main.tf +347 -0
  357. package/plugins/specweave-kafka/terraform/apache-kafka/outputs.tf +107 -0
  358. package/plugins/specweave-kafka/terraform/apache-kafka/templates/kafka-broker-init.sh.tpl +216 -0
  359. package/plugins/specweave-kafka/terraform/apache-kafka/variables.tf +156 -0
  360. package/plugins/specweave-kafka/terraform/aws-msk/main.tf +362 -0
  361. package/plugins/specweave-kafka/terraform/aws-msk/outputs.tf +93 -0
  362. package/plugins/specweave-kafka/terraform/aws-msk/templates/server.properties.tpl +32 -0
  363. package/plugins/specweave-kafka/terraform/aws-msk/variables.tf +235 -0
  364. package/plugins/specweave-kafka/terraform/azure-event-hubs/main.tf +281 -0
  365. package/plugins/specweave-kafka/terraform/azure-event-hubs/outputs.tf +118 -0
  366. package/plugins/specweave-kafka/terraform/azure-event-hubs/variables.tf +148 -0
  367. package/plugins/specweave-kafka/tsconfig.json +21 -0
  368. package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +23 -0
  369. package/plugins/specweave-kafka-streams/README.md +310 -0
  370. package/plugins/specweave-kafka-streams/skills/kafka-streams-topology/SKILL.md +539 -0
  371. package/plugins/specweave-n8n/.claude-plugin/plugin.json +22 -0
  372. package/plugins/specweave-n8n/README.md +354 -0
  373. package/plugins/specweave-n8n/skills/n8n-kafka-workflows/SKILL.md +504 -0
  374. package/plugins/specweave-release/commands/specweave-release-platform.md +1 -1
  375. package/plugins/specweave-release/hooks/post-task-completion.sh +2 -2
  376. package/src/templates/AGENTS.md.template +601 -7
  377. package/src/templates/CLAUDE.md.template +188 -88
  378. package/plugins/specweave-ado/commands/specweave-ado-sync-spec.md +0 -255
  379. package/plugins/specweave-github/commands/specweave-github-sync-epic.md +0 -248
  380. package/plugins/specweave-github/commands/specweave-github-sync-from.md +0 -147
  381. package/plugins/specweave-github/commands/specweave-github-sync-spec.md +0 -208
  382. package/plugins/specweave-github/commands/specweave-github-sync-tasks.md +0 -530
  383. package/plugins/specweave-jira/commands/specweave-jira-sync-epic.md +0 -267
  384. package/plugins/specweave-jira/commands/specweave-jira-sync-spec.md +0 -240
@@ -1,238 +1,515 @@
1
1
  /**
2
- * SpecWeave Hierarchy Mapper
2
+ * SpecWeave Hierarchy Mapper (v4.0.0 - Universal Hierarchy with FS-XXX)
3
3
  *
4
- * Maps increments to Feature Spec (FS-*) folders following Universal Hierarchy architecture.
4
+ * Maps increments to universal hierarchy following work item type matrix:
5
+ * - Epic (EPIC-{id}) -> Cross-project strategic themes (_epics/)
6
+ * - Feature (FS-XXX) -> Cross-project features (_features/ + project folders)
7
+ * - User Story (us-{id}) -> Project-specific requirements (project/FS-XXX/us-{id}.md)
8
+ * - Task (T-{id}) -> Increment-specific implementation (increments/{id}/tasks.md)
5
9
  *
6
- * Supports three complexity levels:
7
- * 1. Simple: Increment Issue/Epic/Feature (flat)
8
- * 2. Standard: FS-* (Epic) US-* (User Stories) → T-* (Tasks)
9
- * 3. Enterprise: Domain → FS-* (Epic) → US-* → T-*
10
+ * Key Principles:
11
+ * - NO HARDCODED PROJECT NAMES (backend, frontend are examples)
12
+ * - Projects are DYNAMIC from config.json -> multiProject.projects
13
+ * - Single-project mode: ['default'] (always)
14
+ * - Multi-project mode: User-configured project names
15
+ * - Feature IDs assigned by creation date (FS-001, FS-002, etc.)
16
+ * - NO DUPLICATE FEATURE IDS (enforced by FeatureIDManager)
10
17
  *
11
18
  * @author SpecWeave Team
12
- * @version 3.0.0
19
+ * @version 4.0.0 (Universal Hierarchy with FS-XXX)
13
20
  */
14
21
  import fs from 'fs-extra';
15
22
  import path from 'path';
23
+ import { ConfigManager } from '../config-manager.js';
24
+ import { FeatureIDManager } from './feature-id-manager.js';
16
25
  /**
17
- * HierarchyMapper - Maps increments to feature folders (named by concept, not increment number)
26
+ * HierarchyMapper - Maps increments to universal hierarchy
18
27
  *
19
- * NEW ARCHITECTURE (v0.18.0+):
20
- * - Features are PERMANENT (named by concept: "release-management", "external-tool-sync")
21
- * - Increments are TEMPORARY (named by execution: "0023-release-management-enhancements")
22
- * - Multiple increments can contribute to same feature
23
- * - No FS-### prefix (features aren't numbered)
28
+ * Universal Hierarchy (v4.0.0):
29
+ * 1. Epic (EPIC-YYYY-QN-{name}) -> _epics/EPIC-{id}/EPIC.md
30
+ * 2. Feature (FS-XXX) -> _features/FS-XXX/FEATURE.md + {project}/FS-XXX/
31
+ * 3. User Story (us-NNN-{name}) -> {project}/FS-XXX/us-{id}.md
32
+ * 4. Task (T-NNN) -> increments/{id}/tasks.md
24
33
  */
25
34
  export class HierarchyMapper {
26
35
  constructor(projectRoot, config) {
36
+ this.specweaveConfig = null;
27
37
  this.projectRoot = projectRoot;
28
- const projectId = config?.projectId || 'default';
38
+ this.configManager = new ConfigManager(projectRoot);
39
+ this.featureIdManager = new FeatureIDManager(projectRoot);
29
40
  this.config = {
30
- level: 'standard', // SpecWeave uses Standard level by default
31
- specsBaseDir: path.join(projectRoot, '.specweave', 'docs', 'internal', 'specs', projectId),
32
- projectId,
33
- featureFolderPattern: '{name}', // Feature-based naming (no FS- prefix)
34
- userStoriesSubdir: '', // User stories go directly in feature folder, not subfolder
41
+ level: 'standard',
42
+ specsBaseDir: path.join(projectRoot, '.specweave', 'docs', 'internal', 'specs'),
43
+ detectEpicFrom: ['frontmatter', 'config'],
35
44
  detectFeatureFrom: ['frontmatter', 'increment-name', 'config'],
36
- fallbackFeature: undefined,
45
+ detectProjectFrom: ['frontmatter', 'increment-name', 'config'],
37
46
  ...config,
38
47
  };
39
48
  }
40
49
  /**
41
- * Detect which feature folder this increment belongs to
50
+ * Load SpecWeave config (cached)
51
+ */
52
+ async getSpecweaveConfig() {
53
+ if (!this.specweaveConfig) {
54
+ this.specweaveConfig = await this.configManager.loadAsync();
55
+ }
56
+ return this.specweaveConfig;
57
+ }
58
+ /**
59
+ * Get list of configured projects
42
60
  *
43
- * NEW: Features are named by CONCEPT (release-management), not INCREMENT NUMBER (FS-023)
61
+ * Returns:
62
+ * - Single-project mode: ['default']
63
+ * - Multi-project mode: User-configured project names (dynamic, no hardcodes)
64
+ */
65
+ async getConfiguredProjects() {
66
+ const config = await this.getSpecweaveConfig();
67
+ // Check if multi-project mode is enabled
68
+ if (config.multiProject?.enabled && config.multiProject.projects) {
69
+ const projects = Object.keys(config.multiProject.projects);
70
+ // If projects object is empty, fallback to default
71
+ if (projects.length > 0) {
72
+ return projects;
73
+ }
74
+ }
75
+ // Default: single-project mode
76
+ return ['default'];
77
+ }
78
+ /**
79
+ * Check if a feature is archived
80
+ */
81
+ async isFeatureArchived(featureId) {
82
+ const archivePaths = [
83
+ path.join(this.config.specsBaseDir, '_features', '_archive', featureId),
84
+ // Check project-specific archives
85
+ ...(await this.getConfiguredProjects()).map(project => path.join(this.config.specsBaseDir, project, '_archive', featureId))
86
+ ];
87
+ for (const archivePath of archivePaths) {
88
+ if (await fs.pathExists(archivePath)) {
89
+ return true;
90
+ }
91
+ }
92
+ return false;
93
+ }
94
+ /**
95
+ * Check if an epic is archived
96
+ */
97
+ async isEpicArchived(epicId) {
98
+ const archivePath = path.join(this.config.specsBaseDir, '_epics', '_archive', epicId);
99
+ return await fs.pathExists(archivePath);
100
+ }
101
+ /**
102
+ * Filter out archived items from mappings
103
+ */
104
+ async filterArchivedItems(items, type) {
105
+ const filtered = [];
106
+ for (const item of items) {
107
+ const isArchived = type === 'feature'
108
+ ? await this.isFeatureArchived(item.id)
109
+ : await this.isEpicArchived(item.id);
110
+ if (!isArchived) {
111
+ filtered.push(item);
112
+ }
113
+ }
114
+ return filtered;
115
+ }
116
+ /**
117
+ * Get project context for a specific project ID
118
+ */
119
+ async getProjectContext(projectId) {
120
+ const config = await this.getSpecweaveConfig();
121
+ // Single-project mode
122
+ if (projectId === 'default') {
123
+ return {
124
+ projectId: 'default',
125
+ projectName: config.project?.name || 'Default Project',
126
+ projectPath: path.join(this.config.specsBaseDir, 'default'),
127
+ keywords: [],
128
+ techStack: config.project?.techStack || [],
129
+ };
130
+ }
131
+ // Multi-project mode
132
+ const projectConfig = config.multiProject?.projects?.[projectId];
133
+ if (!projectConfig) {
134
+ return null;
135
+ }
136
+ return {
137
+ projectId,
138
+ projectName: projectConfig.name,
139
+ projectPath: path.join(this.config.specsBaseDir, projectId),
140
+ keywords: projectConfig.keywords || [],
141
+ techStack: projectConfig.techStack || [],
142
+ };
143
+ }
144
+ /**
145
+ * Detect which epic this increment belongs to (OPTIONAL)
146
+ *
147
+ * Epic Format: EPIC-YYYY-QN-{name}
148
+ * Example: EPIC-2025-Q4-platform
149
+ *
150
+ * Detection Methods:
151
+ * 1. Frontmatter: epic: EPIC-2025-Q4-platform
152
+ * 2. Config: livingDocs.hierarchyMapping.incrementToEpic
153
+ * 3. Fallback: null (no epic required)
154
+ */
155
+ async detectEpicMapping(incrementId) {
156
+ const specPath = path.join(this.projectRoot, '.specweave', 'increments', incrementId, 'spec.md');
157
+ if (!fs.existsSync(specPath)) {
158
+ return null;
159
+ }
160
+ const content = await fs.readFile(specPath, 'utf-8');
161
+ // Try each detection method
162
+ for (const method of this.config.detectEpicFrom) {
163
+ let mapping = null;
164
+ switch (method) {
165
+ case 'frontmatter':
166
+ mapping = await this.detectEpicFromFrontmatter(content, incrementId);
167
+ break;
168
+ case 'config':
169
+ mapping = await this.detectEpicFromConfig(incrementId);
170
+ break;
171
+ }
172
+ if (mapping && mapping.confidence >= 80) {
173
+ return mapping;
174
+ }
175
+ }
176
+ // No epic found (OK - epics are optional)
177
+ return null;
178
+ }
179
+ /**
180
+ * Detect which feature this increment belongs to (REQUIRED)
181
+ *
182
+ * Feature Format (Greenfield): FS-XXX (matches increment number)
183
+ * Feature Format (Brownfield): FS-YY-MM-DD-{feature-name} (date-based)
184
+ * Examples:
185
+ * - Greenfield: 0031-external-tool-sync → FS-031
186
+ * - Brownfield: Imported from JIRA → FS-25-11-14-external-tool-sync
187
+ *
188
+ * Detection Methods:
189
+ * 1. Frontmatter: feature: FS-031 (greenfield) or feature: FS-25-11-14-name (brownfield)
190
+ * 2. Increment Name: 0031-external-tool-status-sync → FS-031 (auto-extract number)
191
+ * 3. Config: livingDocs.hierarchyMapping.incrementToFeature
192
+ * 4. Fallback: Auto-create feature from increment number (FS-XXX format)
44
193
  */
45
194
  async detectFeatureMapping(incrementId) {
195
+ // Load feature registry first
196
+ await this.featureIdManager.loadRegistry();
46
197
  const specPath = path.join(this.projectRoot, '.specweave', 'increments', incrementId, 'spec.md');
47
198
  if (!fs.existsSync(specPath)) {
48
199
  throw new Error(`Increment spec not found: ${specPath}`);
49
200
  }
50
201
  const content = await fs.readFile(specPath, 'utf-8');
51
- // Try each detection method in order
202
+ // Try each detection method
52
203
  for (const method of this.config.detectFeatureFrom) {
53
204
  let mapping = null;
54
205
  switch (method) {
55
206
  case 'frontmatter':
56
- mapping = await this.detectFromFrontmatter(content, incrementId);
207
+ mapping = await this.detectFeatureFromFrontmatter(content, incrementId);
57
208
  break;
58
209
  case 'increment-name':
59
- mapping = await this.detectFromIncrementName(incrementId);
210
+ mapping = await this.detectFeatureFromIncrementName(incrementId);
60
211
  break;
61
212
  case 'config':
62
- mapping = await this.detectFromConfig(incrementId);
213
+ mapping = await this.detectFeatureFromConfig(incrementId);
63
214
  break;
64
215
  }
65
216
  if (mapping && mapping.confidence >= 80) {
66
217
  return mapping;
67
218
  }
68
219
  }
69
- // Fallback: Extract feature name from increment ID
70
- return await this.createFallbackMapping(incrementId);
220
+ // Fallback: Create feature from increment name + date
221
+ return await this.createFallbackFeatureMapping(incrementId);
71
222
  }
72
223
  /**
73
- * Legacy alias for backward compatibility
224
+ * Detect which projects this increment/feature affects (REQUIRED)
225
+ *
226
+ * Returns array of project IDs (dynamic, from config)
227
+ *
228
+ * Detection Methods:
229
+ * 1. Frontmatter: project: backend OR projects: [backend, frontend]
230
+ * 2. Increment Name: Contains project keyword (0031-backend-api-sync)
231
+ * 3. Config: livingDocs.hierarchyMapping.incrementToProjects
232
+ * 4. Fallback: ['default'] (single-project mode)
74
233
  */
75
- async detectEpicMapping(incrementId) {
76
- return this.detectFeatureMapping(incrementId);
234
+ async detectProjects(incrementId) {
235
+ const specPath = path.join(this.projectRoot, '.specweave', 'increments', incrementId, 'spec.md');
236
+ if (!fs.existsSync(specPath)) {
237
+ // Fallback to configured projects when spec missing
238
+ return await this.getConfiguredProjects();
239
+ }
240
+ const content = await fs.readFile(specPath, 'utf-8');
241
+ const config = await this.getSpecweaveConfig();
242
+ const configuredProjects = await this.getConfiguredProjects();
243
+ // Method 1: Frontmatter (explicit)
244
+ const frontmatterProjects = await this.detectProjectsFromFrontmatter(content);
245
+ if (frontmatterProjects.length > 0) {
246
+ // Validate projects exist in config
247
+ const validProjects = frontmatterProjects.filter(p => configuredProjects.includes(p));
248
+ if (validProjects.length > 0) {
249
+ return validProjects;
250
+ }
251
+ }
252
+ // Method 2: Increment name (contains project keyword)
253
+ const nameProjects = this.detectProjectsFromIncrementName(incrementId, configuredProjects, config);
254
+ if (nameProjects.length > 0) {
255
+ return nameProjects;
256
+ }
257
+ // Method 3: Config mapping
258
+ const configProjects = await this.detectProjectsFromConfig(incrementId);
259
+ if (configProjects.length > 0) {
260
+ return configProjects;
261
+ }
262
+ // Fallback: Use configured projects (single-project mode uses repo name, not 'default')
263
+ return configuredProjects;
77
264
  }
78
265
  /**
79
- * Detect feature from frontmatter (epic: feature-name)
80
- *
81
- * FORMAT (v0.18.1+): Accepts simple feature names (external-tool-status-sync)
82
- * LEGACY: Also accepts old FS-### format (FS-001) - extracts from increment name instead
266
+ * Detect epic from frontmatter
83
267
  */
84
- async detectFromFrontmatter(content, incrementId) {
268
+ async detectEpicFromFrontmatter(content, incrementId) {
85
269
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
86
270
  if (!frontmatterMatch)
87
271
  return null;
88
272
  try {
89
273
  const yaml = await import('yaml');
90
274
  const frontmatter = yaml.parse(frontmatterMatch[1]);
91
- // Check for explicit epic field
92
275
  if (frontmatter.epic && typeof frontmatter.epic === 'string') {
93
- const epicValue = frontmatter.epic;
94
- // Handle LEGACY numeric format (FS-001, FS-031) → extract from increment name
95
- if (epicValue.match(/^FS-\d+$/)) {
96
- console.log(` ⚠️ Legacy FS-### format detected (${epicValue}), extracting feature name from increment...`);
97
- return await this.detectFromIncrementName(incrementId);
98
- }
99
- // NEW FORMAT: FS-25-11-14-feature-name (date-based) OR plain feature-name
100
- const featureFolder = await this.findFeatureFolder(epicValue);
101
- if (featureFolder) {
102
- return {
103
- featureId: featureFolder, // ID = folder name
104
- featureFolder,
105
- featurePath: path.join(this.config.specsBaseDir, featureFolder),
106
- userStoriesPath: path.join(this.config.specsBaseDir, featureFolder, this.config.userStoriesSubdir),
107
- confidence: 100,
108
- detectionMethod: 'frontmatter',
109
- };
276
+ const epicId = frontmatter.epic; // EPIC-2025-Q4-platform
277
+ const epicFolder = epicId;
278
+ const epicPath = path.join(this.config.specsBaseDir, '_epics', epicFolder);
279
+ // Try to detect which features belong to this epic (from existing EPIC.md)
280
+ const epicFilePath = path.join(epicPath, 'EPIC.md');
281
+ let features = [];
282
+ if (fs.existsSync(epicFilePath)) {
283
+ const epicContent = await fs.readFile(epicFilePath, 'utf-8');
284
+ // Extract feature IDs from epic file (simple pattern matching)
285
+ const featureMatches = epicContent.matchAll(/FS-\d{2}-\d{2}-\d{2}-[a-z0-9-]+/g);
286
+ features = Array.from(featureMatches, m => m[0]);
110
287
  }
288
+ return {
289
+ epicId,
290
+ epicFolder,
291
+ epicPath,
292
+ features,
293
+ confidence: 100,
294
+ detectionMethod: 'frontmatter',
295
+ };
111
296
  }
112
297
  }
113
298
  catch (error) {
114
- console.warn(` ⚠️ Failed to parse frontmatter: ${error}`);
299
+ console.warn(` ⚠️ Failed to parse frontmatter for epic detection: ${error}`);
115
300
  }
116
301
  return null;
117
302
  }
118
303
  /**
119
- * Detect feature from increment NAME
304
+ * Detect epic from config
305
+ */
306
+ async detectEpicFromConfig(incrementId) {
307
+ const config = await this.getSpecweaveConfig();
308
+ const epicMapping = config.livingDocs?.hierarchyMapping?.incrementToEpic?.[incrementId];
309
+ if (epicMapping && typeof epicMapping === 'string') {
310
+ const epicId = epicMapping;
311
+ const epicFolder = epicId;
312
+ const epicPath = path.join(this.config.specsBaseDir, '_epics', epicFolder);
313
+ return {
314
+ epicId,
315
+ epicFolder,
316
+ epicPath,
317
+ features: [],
318
+ confidence: 100,
319
+ detectionMethod: 'config',
320
+ };
321
+ }
322
+ return null;
323
+ }
324
+ /**
325
+ * Detect feature from frontmatter
120
326
  *
121
- * FORMAT (v0.18.1+): Simple descriptive names (no date prefixes)
327
+ * CRITICAL: For greenfield projects, ALWAYS use increment number (FS-XXX)
328
+ * even if frontmatter says FS-YY-MM-DD-name (date-based format)
329
+ */
330
+ async detectFeatureFromFrontmatter(content, incrementId) {
331
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
332
+ if (!frontmatterMatch)
333
+ return null;
334
+ try {
335
+ const yaml = await import('yaml');
336
+ const frontmatter = yaml.parse(frontmatterMatch[1]);
337
+ if (frontmatter.feature && typeof frontmatter.feature === 'string') {
338
+ let featureId = frontmatter.feature;
339
+ // Check if this is a brownfield project (imported from external tool)
340
+ const isBrownfield = frontmatter.source === 'external' || frontmatter.imported === true;
341
+ if (!isBrownfield) {
342
+ // Greenfield: ALWAYS use increment number, ignore frontmatter's date-based ID
343
+ const numMatch = incrementId.match(/^(\d{4})-/);
344
+ if (numMatch) {
345
+ const num = parseInt(numMatch[1], 10);
346
+ featureId = `FS-${String(num).padStart(3, '0')}`;
347
+ }
348
+ }
349
+ // For brownfield, keep the date-based ID from frontmatter
350
+ const projects = await this.detectProjects(incrementId);
351
+ const epic = frontmatter.epic || undefined;
352
+ return this.buildFeatureMapping(featureId, projects, epic, 100, 'frontmatter');
353
+ }
354
+ }
355
+ catch (error) {
356
+ console.warn(` ⚠️ Failed to parse frontmatter for feature detection: ${error}`);
357
+ }
358
+ return null;
359
+ }
360
+ /**
361
+ * Detect feature from increment name
122
362
  *
123
- * Extracts the descriptive part of increment ID and normalizes it to feature name
124
363
  * Examples:
125
- * 0023-release-management-enhancementsrelease-management (new or existing)
126
- * 0031-external-tool-status-syncexternal-tool-status-sync
364
+ * - 0031-external-tool-status-syncFS-031
365
+ * - 0032-user-authenticationFS-032
366
+ * - 0001-core-framework → FS-001
127
367
  */
128
- async detectFromIncrementName(incrementId) {
129
- // Extract name part (after number: 0023-release-management-enhancements → release-management-enhancements)
130
- const nameMatch = incrementId.match(/^\d+-(.+)/);
131
- if (!nameMatch)
368
+ async detectFeatureFromIncrementName(incrementId) {
369
+ // Extract increment number (first 4 digits)
370
+ const numMatch = incrementId.match(/^(\d{4})-/);
371
+ if (!numMatch)
132
372
  return null;
133
- const fullName = nameMatch[1]; // release-management-enhancements
134
- // Extract core feature name (remove suffixes like -enhancements, -improvements, -fixes)
135
- const suffixesToRemove = ['-enhancements', '-improvements', '-fixes', '-updates', '-v2', '-v3'];
136
- let featureName = fullName;
137
- for (const suffix of suffixesToRemove) {
138
- if (featureName.endsWith(suffix)) {
139
- featureName = featureName.slice(0, -suffix.length);
140
- break;
141
- }
373
+ const num = parseInt(numMatch[1], 10);
374
+ // Build feature ID: FS-XXX (using last 3 digits, zero-padded)
375
+ const featureId = `FS-${String(num).padStart(3, '0')}`;
376
+ // Detect projects
377
+ const projects = await this.detectProjects(incrementId);
378
+ // Check if feature folder already exists
379
+ const existingFeature = await this.findExistingFeatureFolder(featureId);
380
+ if (existingFeature) {
381
+ return this.buildFeatureMapping(existingFeature, projects, undefined, 90, 'increment-name');
142
382
  }
143
- // Try to find existing feature folder (legacy or new format)
144
- let featureFolder = await this.findFeatureFolder(featureName);
145
- // If not found, try with full name
146
- if (!featureFolder) {
147
- featureFolder = await this.findFeatureFolder(fullName);
383
+ return this.buildFeatureMapping(featureId, projects, undefined, 90, 'increment-name');
384
+ }
385
+ /**
386
+ * Detect feature from config
387
+ */
388
+ async detectFeatureFromConfig(incrementId) {
389
+ const config = await this.getSpecweaveConfig();
390
+ const featureMapping = config.livingDocs?.hierarchyMapping?.incrementToFeature?.[incrementId];
391
+ if (featureMapping && typeof featureMapping === 'string') {
392
+ const featureId = featureMapping;
393
+ const projects = await this.detectProjects(incrementId);
394
+ return this.buildFeatureMapping(featureId, projects, undefined, 100, 'config');
395
+ }
396
+ return null;
397
+ }
398
+ /**
399
+ * Create fallback feature mapping
400
+ */
401
+ async createFallbackFeatureMapping(incrementId) {
402
+ // Extract increment number (first 4 digits)
403
+ const numMatch = incrementId.match(/^(\d{4})-/);
404
+ if (!numMatch) {
405
+ throw new Error(`Invalid increment ID format: ${incrementId}`);
148
406
  }
149
- // If not found, use simple feature name (no date prefix, no FS- prefix)
150
- if (!featureFolder) {
151
- featureFolder = featureName; // Simple: external-tool-status-sync
152
- console.log(` ✨ Creating new feature folder: ${featureFolder}`);
407
+ const num = parseInt(numMatch[1], 10);
408
+ // Build feature ID: FS-XXX (using last 3 digits, zero-padded)
409
+ const featureId = `FS-${String(num).padStart(3, '0')}`;
410
+ // Detect projects
411
+ const projects = await this.detectProjects(incrementId);
412
+ console.log(` 📁 Creating new feature: ${featureId}`);
413
+ return this.buildFeatureMapping(featureId, projects, undefined, 80, 'fallback');
414
+ }
415
+ /**
416
+ * Build FeatureMapping object
417
+ *
418
+ * CRITICAL: For greenfield (FS-XXX format), use the feature ID directly.
419
+ * The feature ID manager is only used for brownfield (date-based) IDs.
420
+ */
421
+ buildFeatureMapping(featureId, projects, epic, confidence, detectionMethod) {
422
+ // Check if this is greenfield (FS-XXX) or brownfield (FS-YY-MM-DD-name)
423
+ const isGreenfield = /^FS-\d{3}$/.test(featureId);
424
+ // For greenfield, use the feature ID directly (no registry lookup)
425
+ // For brownfield, get assigned ID from registry (for deduplication)
426
+ const finalFeatureId = isGreenfield
427
+ ? featureId
428
+ : this.featureIdManager.getAssignedId(featureId);
429
+ // Use final ID for folders
430
+ const featureFolder = finalFeatureId;
431
+ const featurePath = path.join(this.config.specsBaseDir, '_features', featureFolder);
432
+ // Build project paths map using final ID
433
+ const projectPaths = new Map();
434
+ for (const project of projects) {
435
+ projectPaths.set(project, path.join(this.config.specsBaseDir, project, finalFeatureId));
153
436
  }
154
437
  return {
155
- featureId: featureFolder, // ID = folder name (for consistency)
438
+ featureId: finalFeatureId,
156
439
  featureFolder,
157
- featurePath: path.join(this.config.specsBaseDir, featureFolder),
158
- userStoriesPath: path.join(this.config.specsBaseDir, featureFolder, this.config.userStoriesSubdir),
159
- confidence: 90,
160
- detectionMethod: 'increment-name',
440
+ featurePath,
441
+ projects,
442
+ projectPaths,
443
+ epic,
444
+ confidence,
445
+ detectionMethod,
161
446
  };
162
447
  }
163
448
  /**
164
- * Detect feature from config mapping (explicit increment → feature mapping)
449
+ * Detect projects from frontmatter
165
450
  */
166
- async detectFromConfig(incrementId) {
167
- // Load config to check for explicit mapping
168
- const configPath = path.join(this.projectRoot, '.specweave', 'config.json');
169
- if (!fs.existsSync(configPath))
170
- return null;
451
+ async detectProjectsFromFrontmatter(content) {
452
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
453
+ if (!frontmatterMatch)
454
+ return [];
171
455
  try {
172
- const config = JSON.parse(await fs.readFile(configPath, 'utf-8'));
173
- const featureMapping = config.livingDocs?.hierarchyMapping?.incrementToFeature?.[incrementId];
174
- if (featureMapping && typeof featureMapping === 'string') {
175
- const featureFolder = await this.findFeatureFolder(featureMapping);
176
- if (featureFolder) {
177
- return {
178
- featureId: featureMapping,
179
- featureFolder,
180
- featurePath: path.join(this.config.specsBaseDir, featureFolder),
181
- userStoriesPath: path.join(this.config.specsBaseDir, featureFolder, this.config.userStoriesSubdir),
182
- confidence: 100,
183
- detectionMethod: 'config',
184
- };
185
- }
456
+ const yaml = await import('yaml');
457
+ const frontmatter = yaml.parse(frontmatterMatch[1]);
458
+ // Single project: project: backend
459
+ if (frontmatter.project && typeof frontmatter.project === 'string') {
460
+ return [frontmatter.project];
461
+ }
462
+ // Multiple projects: projects: [backend, frontend]
463
+ if (frontmatter.projects && Array.isArray(frontmatter.projects)) {
464
+ return frontmatter.projects.filter((p) => typeof p === 'string');
186
465
  }
187
466
  }
188
467
  catch (error) {
189
- console.warn(` ⚠️ Failed to load config: ${error}`);
468
+ console.warn(` ⚠️ Failed to parse frontmatter for project detection: ${error}`);
190
469
  }
191
- return null;
470
+ return [];
192
471
  }
193
472
  /**
194
- * Create fallback mapping (extract feature name from increment ID)
195
- *
196
- * FORMAT (v0.18.1+): Simple descriptive names (no prefixes)
197
- * - Uses core feature name extracted from increment ID
198
- * - No date prefixes (removed to prevent duplicates)
199
- * - No FS- prefixes (features are not numbered)
200
- *
201
- * Example: 0023-release-management-enhancements → release-management
473
+ * Detect projects from increment name (keyword matching)
202
474
  */
203
- async createFallbackMapping(incrementId) {
204
- // Extract name from increment ID
205
- const nameMatch = incrementId.match(/^\d+-(.+)/);
206
- const fullName = nameMatch ? nameMatch[1] : 'unknown';
207
- // Remove common suffixes to get core feature name
208
- const suffixesToRemove = ['-enhancements', '-improvements', '-fixes', '-updates', '-v2', '-v3'];
209
- let featureName = fullName;
210
- for (const suffix of suffixesToRemove) {
211
- if (featureName.endsWith(suffix)) {
212
- featureName = featureName.slice(0, -suffix.length);
213
- break;
475
+ detectProjectsFromIncrementName(incrementId, configuredProjects, config) {
476
+ const detectedProjects = [];
477
+ for (const projectId of configuredProjects) {
478
+ if (projectId === 'default')
479
+ continue;
480
+ const projectConfig = config.multiProject?.projects?.[projectId];
481
+ if (!projectConfig)
482
+ continue;
483
+ // Check if increment name contains project keywords
484
+ const keywords = projectConfig.keywords || [projectId];
485
+ for (const keyword of keywords) {
486
+ if (incrementId.toLowerCase().includes(keyword.toLowerCase())) {
487
+ detectedProjects.push(projectId);
488
+ break;
489
+ }
214
490
  }
215
491
  }
216
- // Use simple feature name (no date prefix, no FS- prefix)
217
- const featureFolder = featureName; // Simple: external-tool-status-sync
218
- const featurePath = path.join(this.config.specsBaseDir, featureFolder);
219
- const userStoriesPath = path.join(featurePath, this.config.userStoriesSubdir);
220
- // Create folder if it doesn't exist
221
- if (!fs.existsSync(featurePath)) {
222
- console.log(` 📁 Creating new feature folder: ${featureFolder}`);
223
- await fs.ensureDir(featurePath);
492
+ return detectedProjects;
493
+ }
494
+ /**
495
+ * Detect projects from config
496
+ */
497
+ async detectProjectsFromConfig(incrementId) {
498
+ const config = await this.getSpecweaveConfig();
499
+ const projectMapping = config.livingDocs?.hierarchyMapping?.incrementToProjects?.[incrementId];
500
+ if (projectMapping) {
501
+ if (typeof projectMapping === 'string') {
502
+ return [projectMapping];
503
+ }
504
+ if (Array.isArray(projectMapping)) {
505
+ return projectMapping.filter((p) => typeof p === 'string');
506
+ }
224
507
  }
225
- return {
226
- featureId: featureFolder, // ID = folder name (external-tool-status-sync)
227
- featureFolder,
228
- featurePath,
229
- userStoriesPath,
230
- confidence: 80,
231
- detectionMethod: 'fallback',
232
- };
508
+ return [];
233
509
  }
234
510
  /**
235
- * Get increment creation date in yy-mm-dd format
511
+ * Get increment creation date in YY-MM-DD format
512
+ *
236
513
  * Tries: metadata.json → spec.md frontmatter → current date
237
514
  */
238
515
  async getIncrementCreationDate(incrementId) {
@@ -246,7 +523,7 @@ export class HierarchyMapper {
246
523
  }
247
524
  }
248
525
  catch (error) {
249
- // Fall through to next method
526
+ // Fall through
250
527
  }
251
528
  }
252
529
  // Try spec.md frontmatter
@@ -264,14 +541,14 @@ export class HierarchyMapper {
264
541
  }
265
542
  }
266
543
  catch (error) {
267
- // Fall through to current date
544
+ // Fall through
268
545
  }
269
546
  }
270
547
  // Fallback: current date
271
548
  return this.formatDateShort(new Date().toISOString());
272
549
  }
273
550
  /**
274
- * Format date as yy-mm-dd
551
+ * Format date as YY-MM-DD
275
552
  * Input: "2025-11-14" or "2025-11-14T12:00:00Z"
276
553
  * Output: "25-11-14"
277
554
  */
@@ -283,58 +560,66 @@ export class HierarchyMapper {
283
560
  return `${yy}-${mm}-${dd}`;
284
561
  }
285
562
  /**
286
- * NEW: Find feature folder by name (exact match or fuzzy match)
563
+ * Find existing feature folder (exact or fuzzy match)
287
564
  *
288
- * Examples:
289
- * release-management release-management/
290
- * external-tool-sync → external-tool-status-sync/ (fuzzy match)
565
+ * CRITICAL: For greenfield (FS-XXX), only exact match.
566
+ * For brownfield (FS-YY-MM-DD-name), fuzzy match allowed.
291
567
  */
292
- async findFeatureFolder(featureName) {
568
+ async findExistingFeatureFolder(featureId) {
569
+ const featuresDir = path.join(this.config.specsBaseDir, '_features');
570
+ if (!fs.existsSync(featuresDir)) {
571
+ return null;
572
+ }
293
573
  try {
294
- const folders = await fs.readdir(this.config.specsBaseDir);
295
- // First: Try exact match
296
- if (folders.includes(featureName)) {
297
- const folderPath = path.join(this.config.specsBaseDir, featureName);
298
- const stats = await fs.stat(folderPath);
299
- if (stats.isDirectory()) {
300
- return featureName;
301
- }
574
+ const folders = await fs.readdir(featuresDir);
575
+ // Exact match (always try this first)
576
+ if (folders.includes(featureId)) {
577
+ return featureId;
578
+ }
579
+ // Fuzzy match ONLY for brownfield (date-based) IDs
580
+ // Greenfield IDs (FS-XXX) should NEVER fuzzy match
581
+ const isGreenfield = /^FS-\d{3}$/.test(featureId);
582
+ if (isGreenfield) {
583
+ return null; // No fuzzy match for greenfield
584
+ }
585
+ // Fuzzy match for brownfield (feature name is substring)
586
+ // Example: FS-25-11-14-external-tool-sync matches FS-25-11-14-external-tool-status-sync
587
+ const featureNamePart = featureId.split('-').slice(3).join('-'); // external-tool-sync
588
+ if (!featureNamePart) {
589
+ return null; // No feature name part, skip fuzzy match
302
590
  }
303
- // Second: Try fuzzy match (feature name is substring of folder name)
304
591
  for (const folder of folders) {
305
- if (folder.startsWith(featureName) || folder.includes(featureName)) {
306
- const folderPath = path.join(this.config.specsBaseDir, folder);
592
+ if (folder.includes(featureNamePart)) {
593
+ const folderPath = path.join(featuresDir, folder);
307
594
  const stats = await fs.stat(folderPath);
308
- if (stats.isDirectory() && folder !== 'README.md') {
595
+ if (stats.isDirectory()) {
309
596
  return folder;
310
597
  }
311
598
  }
312
599
  }
313
600
  }
314
601
  catch (error) {
315
- console.warn(` ⚠️ Failed to find feature folder for ${featureName}: ${error}`);
602
+ console.warn(` ⚠️ Failed to find feature folder: ${error}`);
316
603
  }
317
604
  return null;
318
605
  }
319
606
  /**
320
- * LEGACY: Find FS-* epic folder (for backward compatibility)
321
- */
322
- async findEpicFolder(epicId) {
323
- return this.findFeatureFolder(epicId);
324
- }
325
- /**
326
- * Get all feature folders (NEW: no FS- prefix filtering)
607
+ * Get all feature folders
327
608
  */
328
609
  async getAllFeatureFolders() {
610
+ const featuresDir = path.join(this.config.specsBaseDir, '_features');
611
+ if (!fs.existsSync(featuresDir)) {
612
+ return [];
613
+ }
329
614
  try {
330
- const folders = await fs.readdir(this.config.specsBaseDir);
615
+ const folders = await fs.readdir(featuresDir);
331
616
  const featureFolders = [];
332
617
  for (const folder of folders) {
333
618
  // Skip special files/folders
334
- if (folder === 'README.md' || folder.startsWith('.') || folder.startsWith('_')) {
619
+ if (folder.startsWith('.') || folder.startsWith('_')) {
335
620
  continue;
336
621
  }
337
- const folderPath = path.join(this.config.specsBaseDir, folder);
622
+ const folderPath = path.join(featuresDir, folder);
338
623
  const stats = await fs.stat(folderPath);
339
624
  if (stats.isDirectory()) {
340
625
  featureFolders.push(folder);
@@ -348,106 +633,39 @@ export class HierarchyMapper {
348
633
  }
349
634
  }
350
635
  /**
351
- * LEGACY: Get all epic folders (for backward compatibility)
636
+ * Get all epic folders
352
637
  */
353
638
  async getAllEpicFolders() {
354
- return this.getAllFeatureFolders();
355
- }
356
- /**
357
- * Validate feature folder structure (NEW: checks for FEATURE.md)
358
- */
359
- async validateFeatureFolder(featureFolder) {
360
- const featurePath = path.join(this.config.specsBaseDir, featureFolder);
361
- const missing = [];
362
- // Check FEATURE.md exists (feature overview)
363
- if (!fs.existsSync(path.join(featurePath, 'FEATURE.md'))) {
364
- missing.push('FEATURE.md');
365
- }
366
- // Note: User stories (us-*.md) go directly in feature folder, no validation needed here
367
- return {
368
- valid: missing.length === 0,
369
- missing,
370
- };
371
- }
372
- /**
373
- * LEGACY: Validate epic folder structure (for backward compatibility)
374
- */
375
- async validateEpicFolder(epicFolder) {
376
- return this.validateFeatureFolder(epicFolder);
377
- }
378
- /**
379
- * Create feature folder structure if missing (NEW: creates FEATURE.md)
380
- */
381
- async createFeatureFolderStructure(featureFolder, title) {
382
- const featurePath = path.join(this.config.specsBaseDir, featureFolder);
383
- // Create feature directory (user stories go directly here, no subfolder)
384
- await fs.ensureDir(featurePath);
385
- // Create FEATURE.md if missing (feature overview - high-level summary)
386
- const featureFilePath = path.join(featurePath, 'FEATURE.md');
387
- if (!fs.existsSync(featureFilePath)) {
388
- // ID = folder name (e.g., FS-25-11-14-release-management)
389
- const featureContent = `---
390
- id: ${featureFolder}
391
- title: "${title}"
392
- type: feature
393
- status: in-progress
394
- priority: P1
395
- created: ${new Date().toISOString().split('T')[0]}
396
- last_updated: ${new Date().toISOString().split('T')[0]}
397
- # External Tool Mapping
398
- external_tools:
399
- github:
400
- type: project
401
- id: null
402
- url: null
403
- jira:
404
- type: epic
405
- key: null
406
- url: null
407
- ado:
408
- type: epic
409
- id: null
410
- url: null
411
- ---
412
-
413
- # ${featureFolder}: ${title}
414
-
415
- ## Overview
416
-
417
- [High-level description of this feature - what it does and why it matters]
418
-
419
- ## Business Value
420
-
421
- [Add business value and impact]
422
-
423
- ## User Stories
424
-
425
- User stories are in this folder as \`us-*.md\` files:
426
- - us-001-*.md
427
- - us-002-*.md
428
- - ...
429
-
430
- ## Implementation History
431
-
432
- | Increment | Stories Implemented | Status | Completion Date |
433
- |-----------|-------------------|--------|----------------|
434
- | [TBD]() | TBD | 🚧 In Progress | TBD |
435
-
436
- ## External Tool Integration
437
-
438
- **GitHub Project**: Not yet synced
439
- **Jira Epic**: Not yet synced
440
- **Azure DevOps Epic**: Not yet synced
441
- `;
442
- await fs.writeFile(featureFilePath, featureContent, 'utf-8');
443
- console.log(` ✅ Created FEATURE.md for ${featureFolder}`);
639
+ const epicsDir = path.join(this.config.specsBaseDir, '_epics');
640
+ if (!fs.existsSync(epicsDir)) {
641
+ return [];
642
+ }
643
+ try {
644
+ const folders = await fs.readdir(epicsDir);
645
+ const epicFolders = [];
646
+ for (const folder of folders) {
647
+ // Skip special files/folders
648
+ if (folder.startsWith('.') || folder.startsWith('_')) {
649
+ continue;
650
+ }
651
+ const folderPath = path.join(epicsDir, folder);
652
+ const stats = await fs.stat(folderPath);
653
+ if (stats.isDirectory()) {
654
+ epicFolders.push(folder);
655
+ }
656
+ }
657
+ return epicFolders.sort();
658
+ }
659
+ catch (error) {
660
+ console.warn(` ⚠️ Failed to get epic folders: ${error}`);
661
+ return [];
444
662
  }
445
663
  }
446
664
  /**
447
- * LEGACY: Create epic folder structure (for backward compatibility)
665
+ * LEGACY: Backward compatibility alias for detectFeatureMapping
448
666
  */
449
- async createEpicFolderStructure(epicFolder, title) {
450
- return this.createFeatureFolderStructure(epicFolder, title);
667
+ async detectEpicMapping_LEGACY(incrementId) {
668
+ return this.detectFeatureMapping(incrementId);
451
669
  }
452
670
  }
453
671
  //# sourceMappingURL=hierarchy-mapper.js.map