specweave 0.22.13 → 0.23.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 (312) hide show
  1. package/.claude-plugin/README.md +2 -2
  2. package/CLAUDE.md +447 -52
  3. package/README.md +33 -10
  4. package/dist/plugins/specweave-github/lib/ThreeLayerSyncManager.d.ts +1 -1
  5. package/dist/plugins/specweave-github/lib/ThreeLayerSyncManager.js +1 -1
  6. package/dist/plugins/specweave-github/lib/enhanced-github-sync.js +1 -1
  7. package/dist/plugins/specweave-github/lib/enhanced-github-sync.js.map +1 -1
  8. package/dist/plugins/specweave-github/lib/github-spec-content-sync.d.ts.map +1 -1
  9. package/dist/plugins/specweave-github/lib/github-spec-content-sync.js +4 -1
  10. package/dist/plugins/specweave-github/lib/github-spec-content-sync.js.map +1 -1
  11. package/dist/plugins/specweave-github/lib/github-spec-sync.d.ts +1 -1
  12. package/dist/plugins/specweave-github/lib/github-spec-sync.js +1 -1
  13. package/dist/plugins/specweave-github/lib/github-sync-bidirectional.d.ts +9 -0
  14. package/dist/plugins/specweave-github/lib/github-sync-bidirectional.d.ts.map +1 -1
  15. package/dist/plugins/specweave-github/lib/github-sync-bidirectional.js +10 -1
  16. package/dist/plugins/specweave-github/lib/github-sync-bidirectional.js.map +1 -1
  17. package/dist/plugins/specweave-github/lib/progress-comment-builder.js +2 -2
  18. package/dist/plugins/specweave-github/lib/progress-comment-builder.js.map +1 -1
  19. package/dist/plugins/specweave-github/lib/types.d.ts +1 -1
  20. package/dist/src/cli/commands/import-external.d.ts +22 -0
  21. package/dist/src/cli/commands/import-external.d.ts.map +1 -0
  22. package/dist/src/cli/commands/import-external.js +282 -0
  23. package/dist/src/cli/commands/import-external.js.map +1 -0
  24. package/dist/src/cli/commands/init.d.ts.map +1 -1
  25. package/dist/src/cli/commands/init.js +359 -1
  26. package/dist/src/cli/commands/init.js.map +1 -1
  27. package/dist/src/cli/helpers/github-repo-selector.d.ts +59 -0
  28. package/dist/src/cli/helpers/github-repo-selector.d.ts.map +1 -0
  29. package/dist/src/cli/helpers/github-repo-selector.js +265 -0
  30. package/dist/src/cli/helpers/github-repo-selector.js.map +1 -0
  31. package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
  32. package/dist/src/cli/helpers/issue-tracker/index.js +41 -24
  33. package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
  34. package/dist/src/config/import-config.d.ts +69 -0
  35. package/dist/src/config/import-config.d.ts.map +1 -0
  36. package/dist/src/config/import-config.js +136 -0
  37. package/dist/src/config/import-config.js.map +1 -0
  38. package/dist/src/config/types.d.ts +26 -26
  39. package/dist/src/core/increment/ac-status-manager.d.ts.map +1 -1
  40. package/dist/src/core/increment/ac-status-manager.js +4 -2
  41. package/dist/src/core/increment/ac-status-manager.js.map +1 -1
  42. package/dist/src/core/increment/completion-validator.d.ts +30 -1
  43. package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
  44. package/dist/src/core/increment/completion-validator.js +151 -3
  45. package/dist/src/core/increment/completion-validator.js.map +1 -1
  46. package/dist/src/core/increment/increment-archiver.d.ts +25 -0
  47. package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
  48. package/dist/src/core/increment/increment-archiver.js +130 -3
  49. package/dist/src/core/increment/increment-archiver.js.map +1 -1
  50. package/dist/src/core/living-docs/feature-archiver.d.ts +37 -0
  51. package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -1
  52. package/dist/src/core/living-docs/feature-archiver.js +262 -18
  53. package/dist/src/core/living-docs/feature-archiver.js.map +1 -1
  54. package/dist/src/core/living-docs/feature-id-manager.d.ts +17 -0
  55. package/dist/src/core/living-docs/feature-id-manager.d.ts.map +1 -1
  56. package/dist/src/core/living-docs/feature-id-manager.js +25 -0
  57. package/dist/src/core/living-docs/feature-id-manager.js.map +1 -1
  58. package/dist/src/core/living-docs/living-docs-sync.d.ts +16 -0
  59. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  60. package/dist/src/core/living-docs/living-docs-sync.js +56 -1
  61. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  62. package/dist/src/core/living-docs/task-project-specific-generator.d.ts +2 -2
  63. package/dist/src/core/living-docs/task-project-specific-generator.js +2 -2
  64. package/dist/src/core/repo-structure/prompt-consolidator.d.ts +2 -2
  65. package/dist/src/core/repo-structure/prompt-consolidator.d.ts.map +1 -1
  66. package/dist/src/core/repo-structure/prompt-consolidator.js +3 -15
  67. package/dist/src/core/repo-structure/prompt-consolidator.js.map +1 -1
  68. package/dist/src/core/repo-structure/repo-structure-manager.d.ts +1 -1
  69. package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
  70. package/dist/src/core/repo-structure/repo-structure-manager.js +3 -6
  71. package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
  72. package/dist/src/core/spec-content-sync.d.ts +4 -1
  73. package/dist/src/core/spec-content-sync.d.ts.map +1 -1
  74. package/dist/src/core/spec-content-sync.js +139 -4
  75. package/dist/src/core/spec-content-sync.js.map +1 -1
  76. package/dist/src/core/spec-task-mapper.d.ts.map +1 -1
  77. package/dist/src/core/spec-task-mapper.js +9 -8
  78. package/dist/src/core/spec-task-mapper.js.map +1 -1
  79. package/dist/src/core/status-line-validator.d.ts +63 -0
  80. package/dist/src/core/status-line-validator.d.ts.map +1 -0
  81. package/dist/src/core/status-line-validator.js +253 -0
  82. package/dist/src/core/status-line-validator.js.map +1 -0
  83. package/dist/src/core/sync/bidirectional-engine.d.ts +10 -1
  84. package/dist/src/core/sync/bidirectional-engine.d.ts.map +1 -1
  85. package/dist/src/core/sync/bidirectional-engine.js +10 -1
  86. package/dist/src/core/sync/bidirectional-engine.js.map +1 -1
  87. package/dist/src/core/sync/profile-manager.d.ts.map +1 -1
  88. package/dist/src/core/sync/profile-manager.js +3 -0
  89. package/dist/src/core/sync/profile-manager.js.map +1 -1
  90. package/dist/src/core/sync/project-context.d.ts.map +1 -1
  91. package/dist/src/core/sync/project-context.js +3 -0
  92. package/dist/src/core/sync/project-context.js.map +1 -1
  93. package/dist/src/core/sync/status-sync-engine.d.ts +1 -1
  94. package/dist/src/core/sync/status-sync-engine.js +1 -1
  95. package/dist/src/core/sync/sync-event-logger.d.ts +15 -1
  96. package/dist/src/core/sync/sync-event-logger.d.ts.map +1 -1
  97. package/dist/src/core/sync/sync-event-logger.js +39 -1
  98. package/dist/src/core/sync/sync-event-logger.js.map +1 -1
  99. package/dist/src/core/types/origin-metadata.d.ts +153 -0
  100. package/dist/src/core/types/origin-metadata.d.ts.map +1 -0
  101. package/dist/src/core/types/origin-metadata.js +166 -0
  102. package/dist/src/core/types/origin-metadata.js.map +1 -0
  103. package/dist/src/core/types/sync-config-validator.d.ts +57 -0
  104. package/dist/src/core/types/sync-config-validator.d.ts.map +1 -0
  105. package/dist/src/core/types/sync-config-validator.js +116 -0
  106. package/dist/src/core/types/sync-config-validator.js.map +1 -0
  107. package/dist/src/core/types/sync-profile.d.ts +8 -2
  108. package/dist/src/core/types/sync-profile.d.ts.map +1 -1
  109. package/dist/src/core/types/sync-profile.js.map +1 -1
  110. package/dist/src/core/types/sync-settings.d.ts +73 -0
  111. package/dist/src/core/types/sync-settings.d.ts.map +1 -0
  112. package/dist/src/core/types/sync-settings.js +90 -0
  113. package/dist/src/core/types/sync-settings.js.map +1 -0
  114. package/dist/src/core/utils/permission-checker.d.ts +100 -0
  115. package/dist/src/core/utils/permission-checker.d.ts.map +1 -0
  116. package/dist/src/core/utils/permission-checker.js +166 -0
  117. package/dist/src/core/utils/permission-checker.js.map +1 -0
  118. package/dist/src/generators/spec/spec-parser.js +3 -3
  119. package/dist/src/generators/spec/spec-parser.js.map +1 -1
  120. package/dist/src/generators/spec/task-parser.js +4 -4
  121. package/dist/src/generators/spec/task-parser.js.map +1 -1
  122. package/dist/src/id-generators/task-id-generator.d.ts +96 -0
  123. package/dist/src/id-generators/task-id-generator.d.ts.map +1 -0
  124. package/dist/src/id-generators/task-id-generator.js +143 -0
  125. package/dist/src/id-generators/task-id-generator.js.map +1 -0
  126. package/dist/src/id-generators/us-id-generator.d.ts +96 -0
  127. package/dist/src/id-generators/us-id-generator.d.ts.map +1 -0
  128. package/dist/src/id-generators/us-id-generator.js +143 -0
  129. package/dist/src/id-generators/us-id-generator.js.map +1 -0
  130. package/dist/src/importers/ado-importer.d.ts +43 -0
  131. package/dist/src/importers/ado-importer.d.ts.map +1 -0
  132. package/dist/src/importers/ado-importer.js +234 -0
  133. package/dist/src/importers/ado-importer.js.map +1 -0
  134. package/dist/src/importers/duplicate-detector.d.ts +107 -0
  135. package/dist/src/importers/duplicate-detector.d.ts.map +1 -0
  136. package/dist/src/importers/duplicate-detector.js +189 -0
  137. package/dist/src/importers/duplicate-detector.js.map +1 -0
  138. package/dist/src/importers/external-importer.d.ts +96 -0
  139. package/dist/src/importers/external-importer.d.ts.map +1 -0
  140. package/dist/src/importers/external-importer.js +13 -0
  141. package/dist/src/importers/external-importer.js.map +1 -0
  142. package/dist/src/importers/github-importer.d.ts +37 -0
  143. package/dist/src/importers/github-importer.d.ts.map +1 -0
  144. package/dist/src/importers/github-importer.js +161 -0
  145. package/dist/src/importers/github-importer.js.map +1 -0
  146. package/dist/src/importers/import-coordinator.d.ts +105 -0
  147. package/dist/src/importers/import-coordinator.d.ts.map +1 -0
  148. package/dist/src/importers/import-coordinator.js +224 -0
  149. package/dist/src/importers/import-coordinator.js.map +1 -0
  150. package/dist/src/importers/item-converter.d.ts +96 -0
  151. package/dist/src/importers/item-converter.d.ts.map +1 -0
  152. package/dist/src/importers/item-converter.js +246 -0
  153. package/dist/src/importers/item-converter.js.map +1 -0
  154. package/dist/src/importers/jira-importer.d.ts +42 -0
  155. package/dist/src/importers/jira-importer.d.ts.map +1 -0
  156. package/dist/src/importers/jira-importer.js +221 -0
  157. package/dist/src/importers/jira-importer.js.map +1 -0
  158. package/dist/src/importers/rate-limiter.d.ts +128 -0
  159. package/dist/src/importers/rate-limiter.d.ts.map +1 -0
  160. package/dist/src/importers/rate-limiter.js +200 -0
  161. package/dist/src/importers/rate-limiter.js.map +1 -0
  162. package/dist/src/init/compliance/types.d.ts +2 -2
  163. package/dist/src/init/repo/types.d.ts +2 -2
  164. package/dist/src/integrations/ado/ado-client.d.ts +6 -0
  165. package/dist/src/integrations/ado/ado-client.d.ts.map +1 -1
  166. package/dist/src/integrations/ado/ado-client.js +23 -0
  167. package/dist/src/integrations/ado/ado-client.js.map +1 -1
  168. package/dist/src/integrations/jira/jira-client.d.ts +6 -0
  169. package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
  170. package/dist/src/integrations/jira/jira-client.js +38 -0
  171. package/dist/src/integrations/jira/jira-client.js.map +1 -1
  172. package/dist/src/integrations/jira/jira-mapper.d.ts +1 -1
  173. package/dist/src/integrations/jira/jira-mapper.js +1 -1
  174. package/dist/src/living-docs/fs-id-allocator.d.ts +149 -0
  175. package/dist/src/living-docs/fs-id-allocator.d.ts.map +1 -0
  176. package/dist/src/living-docs/fs-id-allocator.js +325 -0
  177. package/dist/src/living-docs/fs-id-allocator.js.map +1 -0
  178. package/dist/src/living-docs/id-registry.d.ts +124 -0
  179. package/dist/src/living-docs/id-registry.d.ts.map +1 -0
  180. package/dist/src/living-docs/id-registry.js +230 -0
  181. package/dist/src/living-docs/id-registry.js.map +1 -0
  182. package/dist/src/progress/us-progress-tracker.d.ts +68 -0
  183. package/dist/src/progress/us-progress-tracker.d.ts.map +1 -0
  184. package/dist/src/progress/us-progress-tracker.js +120 -0
  185. package/dist/src/progress/us-progress-tracker.js.map +1 -0
  186. package/dist/src/sync/external-item-sync-service.d.ts +150 -0
  187. package/dist/src/sync/external-item-sync-service.d.ts.map +1 -0
  188. package/dist/src/sync/external-item-sync-service.js +241 -0
  189. package/dist/src/sync/external-item-sync-service.js.map +1 -0
  190. package/dist/src/sync/format-preservation-sync.d.ts +90 -0
  191. package/dist/src/sync/format-preservation-sync.d.ts.map +1 -0
  192. package/dist/src/sync/format-preservation-sync.js +173 -0
  193. package/dist/src/sync/format-preservation-sync.js.map +1 -0
  194. package/dist/src/sync/index.d.ts +8 -0
  195. package/dist/src/sync/index.d.ts.map +1 -0
  196. package/dist/src/sync/index.js +6 -0
  197. package/dist/src/sync/index.js.map +1 -0
  198. package/dist/src/sync/sync-coordinator.d.ts +49 -0
  199. package/dist/src/sync/sync-coordinator.d.ts.map +1 -0
  200. package/dist/src/sync/sync-coordinator.js +248 -0
  201. package/dist/src/sync/sync-coordinator.js.map +1 -0
  202. package/dist/src/sync/sync-metadata.d.ts +75 -0
  203. package/dist/src/sync/sync-metadata.d.ts.map +1 -0
  204. package/dist/src/sync/sync-metadata.js +100 -0
  205. package/dist/src/sync/sync-metadata.js.map +1 -0
  206. package/dist/src/types/living-docs-us-file.d.ts +63 -0
  207. package/dist/src/types/living-docs-us-file.d.ts.map +1 -0
  208. package/dist/src/types/living-docs-us-file.js +27 -0
  209. package/dist/src/types/living-docs-us-file.js.map +1 -0
  210. package/dist/src/validators/format-preservation-validator.d.ts +127 -0
  211. package/dist/src/validators/format-preservation-validator.d.ts.map +1 -0
  212. package/dist/src/validators/format-preservation-validator.js +187 -0
  213. package/dist/src/validators/format-preservation-validator.js.map +1 -0
  214. package/package.json +3 -2
  215. package/plugins/specweave/.claude-plugin/plugin.json +36 -2
  216. package/plugins/specweave/agents/architect/AGENT.md +11 -2
  217. package/plugins/specweave/agents/test-aware-planner/AGENT.md +81 -25
  218. package/plugins/specweave/commands/specweave-archive-features.md +11 -1
  219. package/plugins/specweave/commands/specweave-import-external.md +407 -0
  220. package/plugins/specweave/commands/specweave-progress.md +45 -97
  221. package/plugins/specweave/hooks/post-edit-spec.sh +41 -0
  222. package/plugins/specweave/hooks/post-increment-completion.sh +168 -26
  223. package/plugins/specweave/hooks/post-increment-planning.sh +148 -4
  224. package/plugins/specweave/hooks/post-spec-update.sh +0 -0
  225. package/plugins/specweave/hooks/post-task-completion.sh +75 -5
  226. package/plugins/specweave/hooks/post-write-spec.sh +37 -0
  227. package/plugins/specweave/lib/hooks/auto-transition.js +1 -1
  228. package/plugins/specweave/lib/hooks/auto-transition.ts +1 -1
  229. package/plugins/specweave/lib/hooks/invoke-translator-skill.js +1 -1
  230. package/plugins/specweave/lib/hooks/invoke-translator-skill.ts +1 -1
  231. package/plugins/specweave/lib/hooks/sync-cache.js +294 -0
  232. package/plugins/specweave/lib/hooks/sync-living-docs.js +67 -2
  233. package/plugins/specweave/lib/hooks/sync-us-tasks.js +200 -14
  234. package/plugins/specweave/lib/hooks/translate-file.js +1 -1
  235. package/plugins/specweave/lib/hooks/translate-file.ts +1 -1
  236. package/plugins/specweave/lib/hooks/update-ac-status.js +1 -1
  237. package/plugins/specweave/lib/hooks/update-ac-status.ts +1 -1
  238. package/plugins/specweave/lib/vendor/core/increment/ac-status-manager.d.ts +115 -0
  239. package/plugins/specweave/lib/vendor/core/increment/ac-status-manager.js +345 -0
  240. package/plugins/specweave/lib/vendor/core/increment/ac-status-manager.js.map +1 -0
  241. package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.d.ts +106 -0
  242. package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js +220 -0
  243. package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js.map +1 -0
  244. package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.d.ts +60 -0
  245. package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js +192 -0
  246. package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js.map +1 -0
  247. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.d.ts +52 -0
  248. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js +276 -0
  249. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js.map +1 -0
  250. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +163 -0
  251. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +541 -0
  252. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -0
  253. package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +157 -0
  254. package/plugins/specweave/lib/vendor/core/types/increment-metadata.js +191 -0
  255. package/plugins/specweave/lib/vendor/core/types/increment-metadata.js.map +1 -0
  256. package/plugins/specweave/lib/vendor/generators/spec/task-parser.d.ts +95 -0
  257. package/plugins/specweave/lib/vendor/generators/spec/task-parser.js +301 -0
  258. package/plugins/specweave/lib/vendor/generators/spec/task-parser.js.map +1 -0
  259. package/plugins/specweave/lib/vendor/utils/logger.d.ts +48 -0
  260. package/plugins/specweave/lib/vendor/utils/logger.js +53 -0
  261. package/plugins/specweave/lib/vendor/utils/logger.js.map +1 -0
  262. package/plugins/specweave/lib/vendor/utils/translation.d.ts +187 -0
  263. package/plugins/specweave/lib/vendor/utils/translation.js +414 -0
  264. package/plugins/specweave/lib/vendor/utils/translation.js.map +1 -0
  265. package/plugins/specweave-ado/.claude-plugin/plugin.json +1 -1
  266. package/plugins/specweave-ado/lib/ado-multi-project-sync.js +0 -1
  267. package/plugins/specweave-ado/lib/conflict-resolver.ts +1 -1
  268. package/plugins/specweave-alternatives/.claude-plugin/plugin.json +1 -1
  269. package/plugins/specweave-backend/.claude-plugin/plugin.json +1 -1
  270. package/plugins/specweave-confluent/.claude-plugin/plugin.json +1 -1
  271. package/plugins/specweave-cost-optimizer/.claude-plugin/plugin.json +1 -1
  272. package/plugins/specweave-diagrams/.claude-plugin/plugin.json +1 -1
  273. package/plugins/specweave-docs/.claude-plugin/plugin.json +1 -1
  274. package/plugins/specweave-docs-preview/.claude-plugin/plugin.json +1 -1
  275. package/plugins/specweave-figma/.claude-plugin/plugin.json +1 -1
  276. package/plugins/specweave-frontend/.claude-plugin/plugin.json +1 -1
  277. package/plugins/specweave-github/.claude-plugin/plugin.json +1 -1
  278. package/plugins/specweave-github/commands/specweave-github-update-user-story.md +1 -1
  279. package/plugins/specweave-github/hooks/post-task-completion.sh +37 -22
  280. package/plugins/specweave-github/lib/ThreeLayerSyncManager.ts +1 -1
  281. package/plugins/specweave-github/lib/enhanced-github-sync.js +1 -1
  282. package/plugins/specweave-github/lib/enhanced-github-sync.ts +1 -1
  283. package/plugins/specweave-github/lib/github-spec-content-sync.js +2 -1
  284. package/plugins/specweave-github/lib/github-spec-content-sync.ts +4 -1
  285. package/plugins/specweave-github/lib/github-spec-sync.js +1 -1
  286. package/plugins/specweave-github/lib/github-spec-sync.ts +1 -1
  287. package/plugins/specweave-github/lib/github-sync-bidirectional.js +1 -1
  288. package/plugins/specweave-github/lib/github-sync-bidirectional.ts +10 -1
  289. package/plugins/specweave-github/lib/progress-comment-builder.js +1 -1
  290. package/plugins/specweave-github/lib/progress-comment-builder.ts +2 -2
  291. package/plugins/specweave-github/lib/types.ts +1 -1
  292. package/plugins/specweave-github/skills/github-issue-standard/SKILL.md +1 -1
  293. package/plugins/specweave-infrastructure/.claude-plugin/plugin.json +1 -1
  294. package/plugins/specweave-jira/.claude-plugin/plugin.json +1 -1
  295. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
  296. package/plugins/specweave-kafka/.claude-plugin/plugin.json +1 -1
  297. package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +1 -1
  298. package/plugins/specweave-kubernetes/.claude-plugin/plugin.json +1 -1
  299. package/plugins/specweave-ml/.claude-plugin/plugin.json +1 -1
  300. package/plugins/specweave-mobile/.claude-plugin/plugin.json +1 -1
  301. package/plugins/specweave-n8n/.claude-plugin/plugin.json +1 -1
  302. package/plugins/specweave-payments/.claude-plugin/plugin.json +1 -1
  303. package/plugins/specweave-release/.claude-plugin/plugin.json +1 -1
  304. package/plugins/specweave-testing/.claude-plugin/plugin.json +1 -1
  305. package/plugins/specweave-tooling/.claude-plugin/plugin.json +1 -1
  306. package/plugins/specweave-ui/.claude-plugin/plugin.json +1 -1
  307. package/src/templates/.env.example +5 -0
  308. package/src/templates/config-permissions-guide.md +413 -0
  309. package/src/templates/config.json.template +68 -0
  310. package/src/templates/tasks.md.template +180 -201
  311. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +0 -170
  312. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +0 -5442
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Sync Performance Cache Module
4
+ *
5
+ * Provides caching layer for living docs sync to meet <500ms target.
6
+ * Caches:
7
+ * - Parsed tasks.md content
8
+ * - File modification timestamps
9
+ * - US-Task mappings
10
+ *
11
+ * Part of increment 0047-us-task-linkage (T-012).
12
+ */
13
+
14
+ import fs from 'fs-extra';
15
+ import path from 'path';
16
+ import crypto from 'crypto';
17
+
18
+ /**
19
+ * In-memory cache with TTL
20
+ */
21
+ class SyncCache {
22
+ constructor() {
23
+ this.cache = new Map();
24
+ this.ttl = 60000; // 60 seconds default TTL
25
+ }
26
+
27
+ /**
28
+ * Get cached value
29
+ *
30
+ * @param {string} key - Cache key
31
+ * @returns {any|null} Cached value or null if expired/missing
32
+ */
33
+ get(key) {
34
+ const entry = this.cache.get(key);
35
+
36
+ if (!entry) {
37
+ return null;
38
+ }
39
+
40
+ // Check if expired
41
+ if (Date.now() > entry.expiry) {
42
+ this.cache.delete(key);
43
+ return null;
44
+ }
45
+
46
+ return entry.value;
47
+ }
48
+
49
+ /**
50
+ * Set cached value
51
+ *
52
+ * @param {string} key - Cache key
53
+ * @param {any} value - Value to cache
54
+ * @param {number} ttl - Time to live in milliseconds (optional)
55
+ */
56
+ set(key, value, ttl = this.ttl) {
57
+ this.cache.set(key, {
58
+ value,
59
+ expiry: Date.now() + ttl
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Invalidate cache entry
65
+ *
66
+ * @param {string} key - Cache key
67
+ */
68
+ invalidate(key) {
69
+ this.cache.delete(key);
70
+ }
71
+
72
+ /**
73
+ * Clear all cache
74
+ */
75
+ clear() {
76
+ this.cache.clear();
77
+ }
78
+
79
+ /**
80
+ * Get cache size
81
+ *
82
+ * @returns {number} Number of cached entries
83
+ */
84
+ size() {
85
+ return this.cache.size;
86
+ }
87
+ }
88
+
89
+ // Global cache instance
90
+ const globalCache = new SyncCache();
91
+
92
+ /**
93
+ * Get file hash for change detection
94
+ *
95
+ * @param {string} filePath - Path to file
96
+ * @returns {string} SHA256 hash of file content
97
+ */
98
+ export function getFileHash(filePath) {
99
+ try {
100
+ const content = fs.readFileSync(filePath, 'utf-8');
101
+ return crypto.createHash('sha256').update(content).digest('hex');
102
+ } catch (error) {
103
+ return null;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Get cached parsed tasks.md
109
+ *
110
+ * @param {string} tasksPath - Path to tasks.md
111
+ * @param {Function} parser - Parser function to call if cache miss
112
+ * @returns {any} Parsed tasks (from cache or fresh)
113
+ */
114
+ export function getCachedTasks(tasksPath, parser) {
115
+ // Generate cache key from file path + hash
116
+ const fileHash = getFileHash(tasksPath);
117
+
118
+ if (!fileHash) {
119
+ // File doesn't exist, call parser directly
120
+ return parser(tasksPath);
121
+ }
122
+
123
+ const cacheKey = `tasks:${tasksPath}:${fileHash}`;
124
+
125
+ // Try to get from cache
126
+ const cached = globalCache.get(cacheKey);
127
+
128
+ if (cached) {
129
+ // Cache hit
130
+ return cached;
131
+ }
132
+
133
+ // Cache miss - parse and cache
134
+ const parsed = parser(tasksPath);
135
+ globalCache.set(cacheKey, parsed);
136
+
137
+ return parsed;
138
+ }
139
+
140
+ /**
141
+ * Get cached US file metadata
142
+ *
143
+ * @param {string} usFilePath - Path to US file
144
+ * @returns {object|null} Metadata object or null if file doesn't exist
145
+ */
146
+ export function getCachedUSMetadata(usFilePath) {
147
+ try {
148
+ const stats = fs.statSync(usFilePath);
149
+ const cacheKey = `us-metadata:${usFilePath}`;
150
+
151
+ const cached = globalCache.get(cacheKey);
152
+
153
+ if (cached && cached.mtime === stats.mtimeMs) {
154
+ // File hasn't changed, return cached metadata
155
+ return cached.metadata;
156
+ }
157
+
158
+ // File changed or not in cache - read and cache
159
+ const content = fs.readFileSync(usFilePath, 'utf-8');
160
+ const metadata = {
161
+ mtime: stats.mtimeMs,
162
+ metadata: {
163
+ path: usFilePath,
164
+ size: content.length,
165
+ tasksSectionExists: content.includes('## Tasks'),
166
+ acCount: (content.match(/- \[[x ]\] \*\*AC-US\d+-\d{2}\*\*/g) || []).length
167
+ }
168
+ };
169
+
170
+ globalCache.set(cacheKey, metadata);
171
+
172
+ return metadata.metadata;
173
+ } catch (error) {
174
+ return null;
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Batch file operations to reduce I/O
180
+ *
181
+ * @param {Array<{path: string, content: string}>} updates - Files to update
182
+ * @returns {Promise<void>}
183
+ */
184
+ export async function batchFileUpdates(updates) {
185
+ // Group updates by directory to optimize disk I/O
186
+ const updatesByDir = new Map();
187
+
188
+ updates.forEach(({ path: filePath, content }) => {
189
+ const dir = path.dirname(filePath);
190
+
191
+ if (!updatesByDir.has(dir)) {
192
+ updatesByDir.set(dir, []);
193
+ }
194
+
195
+ updatesByDir.get(dir).push({ path: filePath, content });
196
+ });
197
+
198
+ // Write files sequentially within same directory (better disk I/O)
199
+ for (const [dir, fileUpdates] of updatesByDir.entries()) {
200
+ // Ensure directory exists once per directory
201
+ await fs.ensureDir(dir);
202
+
203
+ // Write all files in this directory
204
+ await Promise.all(
205
+ fileUpdates.map(({ path: filePath, content }) =>
206
+ fs.writeFile(filePath, content, 'utf-8')
207
+ )
208
+ );
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Check if sync is needed for a US file
214
+ *
215
+ * @param {string} usFilePath - Path to US file
216
+ * @param {Array} tasks - Tasks for this US
217
+ * @param {string} tasksPath - Path to tasks.md
218
+ * @returns {boolean} True if sync is needed
219
+ */
220
+ export function needsSync(usFilePath, tasks, tasksPath) {
221
+ try {
222
+ // Check if US file exists
223
+ if (!fs.existsSync(usFilePath)) {
224
+ return false; // File doesn't exist, can't sync
225
+ }
226
+
227
+ // Get file modification times
228
+ const usStats = fs.statSync(usFilePath);
229
+ const tasksStats = fs.statSync(tasksPath);
230
+
231
+ // If tasks.md is newer than US file, sync is needed
232
+ if (tasksStats.mtimeMs > usStats.mtimeMs) {
233
+ return true;
234
+ }
235
+
236
+ // Check cache for last sync result
237
+ const cacheKey = `sync-result:${usFilePath}`;
238
+ const cached = globalCache.get(cacheKey);
239
+
240
+ if (cached) {
241
+ // Compare task list with cached
242
+ const currentTaskIds = tasks.map(t => t.id).sort().join(',');
243
+ const cachedTaskIds = cached.taskIds;
244
+
245
+ if (currentTaskIds === cachedTaskIds) {
246
+ // Task list unchanged, no sync needed
247
+ return false;
248
+ }
249
+ }
250
+
251
+ // Default: sync is needed
252
+ return true;
253
+ } catch (error) {
254
+ // If error checking, assume sync is needed
255
+ return true;
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Record sync result for incremental sync
261
+ *
262
+ * @param {string} usFilePath - Path to US file
263
+ * @param {Array} tasks - Tasks that were synced
264
+ */
265
+ export function recordSync(usFilePath, tasks) {
266
+ const cacheKey = `sync-result:${usFilePath}`;
267
+ const taskIds = tasks.map(t => t.id).sort().join(',');
268
+
269
+ globalCache.set(cacheKey, {
270
+ taskIds,
271
+ timestamp: Date.now()
272
+ });
273
+ }
274
+
275
+ /**
276
+ * Get cache statistics
277
+ *
278
+ * @returns {object} Cache stats
279
+ */
280
+ export function getCacheStats() {
281
+ return {
282
+ size: globalCache.size(),
283
+ entries: Array.from(globalCache.cache.keys())
284
+ };
285
+ }
286
+
287
+ /**
288
+ * Clear cache (for testing)
289
+ */
290
+ export function clearCache() {
291
+ globalCache.clear();
292
+ }
293
+
294
+ export default globalCache;
@@ -37,7 +37,26 @@ async function syncLivingDocs(incrementId) {
37
37
  return;
38
38
  }
39
39
  console.log(`\u{1F4C4} Changed/created ${changedDocs.length} file(s)`);
40
- await syncToGitHub(incrementId, changedDocs);
40
+
41
+ // ========================================================================
42
+ // CHECK PERMISSION: canUpdateExternalItems (v0.24.0 - Three-Permission Architecture)
43
+ // ========================================================================
44
+ // This permission controls whether SpecWeave can UPDATE externally-created items
45
+ // (full content: title, description, ACs, tasks, comments).
46
+ // If false, living docs sync happens locally but doesn't push to external tools.
47
+ const canUpdateExternal = config.sync?.settings?.canUpdateExternalItems ?? false;
48
+
49
+ if (!canUpdateExternal) {
50
+ console.log("\u2139\uFE0F GitHub sync skipped (canUpdateExternalItems = false)");
51
+ console.log(" Living docs updated locally only");
52
+ console.log(" To enable: Set sync.settings.canUpdateExternalItems = true in config.json");
53
+ console.log("\u2705 Living docs sync complete (local only)\n");
54
+ return;
55
+ }
56
+
57
+ // T-034E: Use FormatPreservationSyncService for origin-aware sync
58
+ await syncWithFormatPreservation(incrementId);
59
+
41
60
  console.log("\u2705 Living docs sync complete\n");
42
61
  } catch (error) {
43
62
  console.error("\u274C Error syncing living docs:", error);
@@ -77,6 +96,18 @@ async function hierarchicalDistribution(incrementId) {
77
96
  console.log(" \u{1F4CA} Syncing increment to living docs structure...");
78
97
  const projectRoot = process.cwd();
79
98
 
99
+ // ========================================================================
100
+ // USE FEATURE ID FROM ENVIRONMENT (NEW in v0.23.0 - Increment 0047)
101
+ // ========================================================================
102
+ // If FEATURE_ID is provided via environment variable (extracted from spec.md),
103
+ // use it directly instead of auto-generating. This ensures correct traceability.
104
+ const explicitFeatureId = process.env.FEATURE_ID;
105
+ if (explicitFeatureId) {
106
+ console.log(` \u{1F4CE} Using explicit feature ID from spec.md: ${explicitFeatureId}`);
107
+ } else {
108
+ console.log(" \u{1F504} Feature ID will be auto-generated from increment number");
109
+ }
110
+
80
111
  // Create logger adapter for LivingDocsSync
81
112
  const logger = {
82
113
  log: (msg) => console.log(` ${msg}`),
@@ -87,7 +118,9 @@ async function hierarchicalDistribution(incrementId) {
87
118
  const sync = new LivingDocsSync(projectRoot, { logger });
88
119
  const result = await sync.syncIncrement(incrementId, {
89
120
  dryRun: false,
90
- force: false
121
+ force: false,
122
+ // Pass explicit feature ID if available (v0.23.0+)
123
+ explicitFeatureId: explicitFeatureId || undefined
91
124
  });
92
125
 
93
126
  if (!result.success) {
@@ -303,6 +336,38 @@ if (isMainModule) {
303
336
  console.error("\u274C Fatal error:", error);
304
337
  });
305
338
  }
339
+
340
+ /**
341
+ * Sync with format preservation (T-034E)
342
+ * Routes sync based on origin metadata (external vs internal)
343
+ */
344
+ async function syncWithFormatPreservation(incrementId) {
345
+ try {
346
+ console.log("\n🔄 Using format-preserving sync...");
347
+
348
+ const { SyncCoordinator } = await import("../../../../dist/src/sync/sync-coordinator.js");
349
+
350
+ const coordinator = new SyncCoordinator({
351
+ projectRoot: process.cwd(),
352
+ incrementId
353
+ });
354
+
355
+ const result = await coordinator.syncIncrementCompletion();
356
+
357
+ if (result.success) {
358
+ console.log(`✅ Format-preserving sync complete (${result.syncMode} mode)`);
359
+ console.log(` ${result.userStoriesSynced} user story/stories synced`);
360
+ } else {
361
+ console.log(`⚠️ Sync completed with ${result.errors.length} error(s)`);
362
+ result.errors.forEach(err => console.error(` - ${err}`));
363
+ }
364
+ } catch (error) {
365
+ console.error("⚠️ Format-preserving sync error:", error);
366
+ console.log(" Falling back to legacy sync...");
367
+ // Don't fail - just log and continue
368
+ }
369
+ }
370
+
306
371
  export {
307
372
  syncLivingDocs
308
373
  };
@@ -12,8 +12,9 @@
12
12
 
13
13
  import fs from 'fs-extra';
14
14
  import path from 'path';
15
- import { parseTasksWithUSLinks, getAllTasks } from '../../../../dist/src/generators/spec/task-parser.js';
15
+ import { parseTasksWithUSLinks, getAllTasks } from '../vendor/generators/spec/task-parser.js';
16
16
  import { glob } from 'glob';
17
+ import { getCachedTasks, needsSync, recordSync, batchFileUpdates } from './sync-cache.js';
17
18
 
18
19
  /**
19
20
  * Sync tasks from tasks.md to living docs User Story files
@@ -36,10 +37,10 @@ export async function syncUSTasksToLivingDocs(incrementId, projectRoot, featureI
36
37
  return { success: true, updatedFiles: [], errors: [] };
37
38
  }
38
39
 
39
- // Parse tasks with US linkage
40
+ // Parse tasks with US linkage (with caching for performance)
40
41
  let tasksByUS;
41
42
  try {
42
- tasksByUS = parseTasksWithUSLinks(tasksPath);
43
+ tasksByUS = getCachedTasks(tasksPath, parseTasksWithUSLinks);
43
44
  } catch (error) {
44
45
  console.error(` ❌ Failed to parse tasks.md:`, error.message);
45
46
  return { success: false, updatedFiles: [], errors: [error.message] };
@@ -59,6 +60,7 @@ export async function syncUSTasksToLivingDocs(incrementId, projectRoot, featureI
59
60
 
60
61
  const updatedFiles = [];
61
62
  const errors = [];
63
+ const filesToUpdate = []; // Batch file updates
62
64
 
63
65
  // For each User Story with tasks, update its living docs file
64
66
  for (const [usId, tasks] of Object.entries(tasksByUS)) {
@@ -73,12 +75,19 @@ export async function syncUSTasksToLivingDocs(incrementId, projectRoot, featureI
73
75
  continue;
74
76
  }
75
77
 
78
+ // Incremental sync: Check if sync is needed
79
+ if (!needsSync(usFilePath, tasks, tasksPath)) {
80
+ console.log(` ⏭️ ${usId} unchanged, skipping sync`);
81
+ continue;
82
+ }
83
+
76
84
  // Update US file with task list
77
- const updated = await updateUSFile(usFilePath, tasks, incrementId);
85
+ const result = await updateUSFile(usFilePath, tasks, incrementId);
78
86
 
79
- if (updated) {
80
- updatedFiles.push(usFilePath);
81
- console.log(` ✓ Updated ${usId} with ${tasks.length} tasks`);
87
+ if (result.updated) {
88
+ filesToUpdate.push({ path: usFilePath, content: result.content });
89
+ recordSync(usFilePath, tasks); // Cache sync result for next run
90
+ console.log(` ✓ Prepared update for ${usId} (${tasks.length} tasks)`);
82
91
  }
83
92
  } catch (error) {
84
93
  console.error(` ❌ Error updating ${usId}:`, error.message);
@@ -86,6 +95,12 @@ export async function syncUSTasksToLivingDocs(incrementId, projectRoot, featureI
86
95
  }
87
96
  }
88
97
 
98
+ // Batch write all file updates (reduce I/O)
99
+ if (filesToUpdate.length > 0) {
100
+ await batchFileUpdates(filesToUpdate);
101
+ updatedFiles.push(...filesToUpdate.map(f => f.path));
102
+ }
103
+
89
104
  if (updatedFiles.length > 0) {
90
105
  console.log(` ✅ Updated ${updatedFiles.length} User Story file(s)`);
91
106
  } else {
@@ -144,13 +159,20 @@ async function findUSFile(projectRoot, projectId, featureId, usId) {
144
159
  * @param {string} usFilePath - Path to US markdown file
145
160
  * @param {Array} tasks - Tasks linked to this US
146
161
  * @param {string} incrementId - Increment ID
147
- * @returns {Promise<boolean>} True if file was updated
162
+ * @returns {Promise<{updated: boolean, content: string}>} Update result
148
163
  */
149
164
  async function updateUSFile(usFilePath, tasks, incrementId) {
150
165
  let content = await fs.readFile(usFilePath, 'utf-8');
151
166
  let updated = false;
152
167
 
153
- // 1. Update task list section
168
+ // 1. Update origin badge (NEW - T-034)
169
+ const updatedOrigin = updateOriginBadge(content, usFilePath);
170
+ if (updatedOrigin !== content) {
171
+ content = updatedOrigin;
172
+ updated = true;
173
+ }
174
+
175
+ // 2. Update task list section
154
176
  const taskList = generateTaskList(tasks, incrementId);
155
177
  const newTasksSection = `## Tasks\n\n${taskList}`;
156
178
 
@@ -166,19 +188,183 @@ async function updateUSFile(usFilePath, tasks, incrementId) {
166
188
  }
167
189
  }
168
190
 
169
- // 2. Update AC checkboxes based on task completion
191
+ // 3. Update AC checkboxes based on task completion
170
192
  const updatedACs = updateACCheckboxes(content, tasks);
171
193
  if (updatedACs !== content) {
172
194
  content = updatedACs;
173
195
  updated = true;
174
196
  }
175
197
 
176
- // Write updated content
177
- if (updated) {
178
- await fs.writeFile(usFilePath, content, 'utf-8');
198
+ return { updated, content };
199
+ }
200
+
201
+ /**
202
+ * Update origin badge in User Story file
203
+ * Detects origin from US ID (E suffix = external)
204
+ *
205
+ * @param {string} content - US file content
206
+ * @param {string} usFilePath - Path to US file
207
+ * @returns {string} Updated content with origin badge
208
+ */
209
+ function updateOriginBadge(content, usFilePath) {
210
+ // Extract US ID from filename (e.g., us-001e-title.md -> US-001E)
211
+ const filename = path.basename(usFilePath);
212
+ const usIdMatch = filename.match(/^(us-\d{3}e?)-/i);
213
+
214
+ if (!usIdMatch) {
215
+ return content; // Can't determine US ID, skip
216
+ }
217
+
218
+ const usId = usIdMatch[1].toUpperCase();
219
+ const isExternal = usId.endsWith('E');
220
+
221
+ // Validate origin immutability (prevent internal ↔ external changes)
222
+ const existingOrigin = extractExistingOrigin(content);
223
+ if (existingOrigin) {
224
+ const existingIsExternal = existingOrigin !== 'internal';
225
+ if (isExternal !== existingIsExternal) {
226
+ console.log(` ⚠️ Origin immutable: Cannot change ${usId} from ${existingOrigin} to ${isExternal ? 'external' : 'internal'}`);
227
+ return content; // Don't modify origin
228
+ }
179
229
  }
180
230
 
181
- return updated;
231
+ // Determine origin badge
232
+ let originBadge;
233
+ if (isExternal) {
234
+ // Try to detect external source from metadata
235
+ const externalSource = detectExternalSource(content);
236
+ originBadge = getExternalOriginBadge(externalSource, content);
237
+ } else {
238
+ originBadge = '🏠 **Internal**';
239
+ }
240
+
241
+ // Check if origin badge already exists
242
+ const originPattern = /\*\*Origin\*\*:\s*.*/;
243
+ if (originPattern.test(content)) {
244
+ // Replace existing origin badge
245
+ return content.replace(originPattern, `**Origin**: ${originBadge}`);
246
+ }
247
+
248
+ // Add origin badge after frontmatter (if exists) or at beginning
249
+ const frontmatterEnd = content.match(/^---\n.*?\n---\n/s);
250
+ if (frontmatterEnd) {
251
+ const insertPos = frontmatterEnd[0].length;
252
+ return content.slice(0, insertPos) + `\n**Origin**: ${originBadge}\n\n` + content.slice(insertPos);
253
+ }
254
+
255
+ // No frontmatter, add at beginning after title
256
+ const titleMatch = content.match(/^#\s+.+\n/);
257
+ if (titleMatch) {
258
+ const insertPos = titleMatch[0].length;
259
+ return content.slice(0, insertPos) + `\n**Origin**: ${originBadge}\n\n` + content.slice(insertPos);
260
+ }
261
+
262
+ // Fallback: add at very beginning
263
+ return `**Origin**: ${originBadge}\n\n` + content;
264
+ }
265
+
266
+ /**
267
+ * Extract existing origin from content (for immutability validation)
268
+ *
269
+ * @param {string} content - US file content
270
+ * @returns {string|null} Existing origin (internal, github, jira, ado) or null
271
+ */
272
+ function extractExistingOrigin(content) {
273
+ const originMatch = content.match(/\*\*Origin\*\*:\s*(.+)/);
274
+ if (!originMatch) {
275
+ return null;
276
+ }
277
+
278
+ const originText = originMatch[1].toLowerCase();
279
+ if (originText.includes('internal')) {
280
+ return 'internal';
281
+ }
282
+ if (originText.includes('github')) {
283
+ return 'github';
284
+ }
285
+ if (originText.includes('jira')) {
286
+ return 'jira';
287
+ }
288
+ if (originText.includes('ado') || originText.includes('azure')) {
289
+ return 'ado';
290
+ }
291
+ if (originText.includes('external')) {
292
+ return 'external'; // Generic external
293
+ }
294
+
295
+ return null;
296
+ }
297
+
298
+ /**
299
+ * Detect external source from content metadata
300
+ *
301
+ * @param {string} content - US file content
302
+ * @returns {string} External source (github, jira, ado) or 'unknown'
303
+ */
304
+ function detectExternalSource(content) {
305
+ // Check frontmatter for external_source or externalSource
306
+ const frontmatterMatch = content.match(/^---\n(.*?)\n---/s);
307
+ if (frontmatterMatch) {
308
+ const frontmatter = frontmatterMatch[1];
309
+ if (frontmatter.includes('external_source: github') || frontmatter.includes('externalSource: github')) {
310
+ return 'github';
311
+ }
312
+ if (frontmatter.includes('external_source: jira') || frontmatter.includes('externalSource: jira')) {
313
+ return 'jira';
314
+ }
315
+ if (frontmatter.includes('external_source: ado') || frontmatter.includes('externalSource: ado')) {
316
+ return 'ado';
317
+ }
318
+ }
319
+
320
+ // Check for external ID patterns
321
+ if (content.includes('externalId: GH-') || content.includes('external_id: GH-')) {
322
+ return 'github';
323
+ }
324
+ if (content.includes('externalId: JIRA-') || content.includes('external_id: JIRA-')) {
325
+ return 'jira';
326
+ }
327
+ if (content.includes('externalId: ADO-') || content.includes('external_id: ADO-')) {
328
+ return 'ado';
329
+ }
330
+
331
+ return 'unknown';
332
+ }
333
+
334
+ /**
335
+ * Get external origin badge with link if available
336
+ *
337
+ * @param {string} source - External source (github, jira, ado)
338
+ * @param {string} content - US file content
339
+ * @returns {string} Origin badge markdown
340
+ */
341
+ function getExternalOriginBadge(source, content) {
342
+ // Extract external ID and URL from content
343
+ const externalIdMatch = content.match(/external_?[iI]d:\s*([^\n]+)/);
344
+ const externalUrlMatch = content.match(/external_?[uU]rl:\s*([^\n]+)/);
345
+
346
+ const externalId = externalIdMatch ? externalIdMatch[1].trim() : null;
347
+ const externalUrl = externalUrlMatch ? externalUrlMatch[1].trim() : null;
348
+
349
+ switch (source) {
350
+ case 'github':
351
+ if (externalId && externalUrl) {
352
+ return `🔗 [GitHub ${externalId}](${externalUrl})`;
353
+ }
354
+ return '🔗 **GitHub**';
355
+ case 'jira':
356
+ if (externalId && externalUrl) {
357
+ return `🎫 [JIRA ${externalId}](${externalUrl})`;
358
+ }
359
+ return '🎫 **JIRA**';
360
+ case 'ado':
361
+ if (externalId && externalUrl) {
362
+ return `📋 [ADO ${externalId}](${externalUrl})`;
363
+ }
364
+ return '📋 **Azure DevOps**';
365
+ default:
366
+ return '🔗 **External**';
367
+ }
182
368
  }
183
369
 
184
370
  /**
@@ -6,7 +6,7 @@ import {
6
6
  validateTranslation,
7
7
  getLanguageName,
8
8
  formatCost
9
- } from "../../../../dist/src/utils/translation.js";
9
+ } from "../vendor/utils/translation.js";
10
10
  async function translateFile(options) {
11
11
  const { filePath, targetLang, preview, verbose } = options;
12
12
  if (!await fs.pathExists(filePath)) {
@@ -26,7 +26,7 @@ import {
26
26
  getLanguageName,
27
27
  formatCost,
28
28
  type SupportedLanguage,
29
- } from '../../../../dist/src/utils/translation.js';
29
+ } from '../vendor/utils/translation.js';
30
30
 
31
31
  /**
32
32
  * CLI options
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { ACStatusManager } from "../../../../dist/src/core/increment/ac-status-manager.js";
2
+ import { ACStatusManager } from "../vendor/core/increment/ac-status-manager.js";
3
3
  async function updateACStatus(incrementId) {
4
4
  try {
5
5
  const projectRoot = process.cwd();
@@ -26,7 +26,7 @@
26
26
  * - spec.md: - [ ] AC-US1-02 → NO CHANGE (partial completion)
27
27
  */
28
28
 
29
- import { ACStatusManager } from '../../../../dist/src/core/increment/ac-status-manager.js';
29
+ import { ACStatusManager } from '../vendor/core/increment/ac-status-manager.js';
30
30
 
31
31
  /**
32
32
  * Main entry point - uses ACStatusManager for sophisticated sync