specweave 0.17.16 → 0.17.19

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 (232) hide show
  1. package/CLAUDE.md +405 -2495
  2. package/README.md +92 -2
  3. package/dist/locales/de/.gitkeep +0 -0
  4. package/dist/locales/de/cli.json +108 -0
  5. package/dist/locales/en/cli.json +287 -0
  6. package/dist/locales/en/errors.json +7 -0
  7. package/dist/locales/en/templates.json +6 -0
  8. package/dist/locales/es/.gitkeep +0 -0
  9. package/dist/locales/es/cli.json +41 -0
  10. package/dist/locales/fr/.gitkeep +0 -0
  11. package/dist/locales/fr/cli.json +108 -0
  12. package/dist/locales/ja/.gitkeep +0 -0
  13. package/dist/locales/ja/cli.json +108 -0
  14. package/dist/locales/ko/.gitkeep +0 -0
  15. package/dist/locales/ko/cli.json +108 -0
  16. package/dist/locales/pt/.gitkeep +0 -0
  17. package/dist/locales/pt/cli.json +108 -0
  18. package/dist/locales/ru/.gitkeep +0 -0
  19. package/dist/locales/ru/cli.json +269 -0
  20. package/dist/locales/zh/.gitkeep +0 -0
  21. package/dist/locales/zh/cli.json +108 -0
  22. package/dist/plugins/specweave/lib/hooks/sync-living-docs.d.ts.map +1 -1
  23. package/dist/plugins/specweave/lib/hooks/sync-living-docs.js +188 -36
  24. package/dist/plugins/specweave/lib/hooks/sync-living-docs.js.map +1 -1
  25. package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.d.ts.map +1 -1
  26. package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js +65 -6
  27. package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js.map +1 -1
  28. package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts +54 -0
  29. package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts.map +1 -0
  30. package/dist/plugins/specweave-ado/lib/ado-status-sync.js +86 -0
  31. package/dist/plugins/specweave-ado/lib/ado-status-sync.js.map +1 -0
  32. package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.d.ts +25 -0
  33. package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.d.ts.map +1 -0
  34. package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.js +191 -0
  35. package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.js.map +1 -0
  36. package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts +139 -0
  37. package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts.map +1 -0
  38. package/dist/plugins/specweave-github/lib/duplicate-detector.js +389 -0
  39. package/dist/plugins/specweave-github/lib/duplicate-detector.js.map +1 -0
  40. package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts +26 -0
  41. package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts.map +1 -0
  42. package/dist/plugins/specweave-github/lib/enhanced-github-sync.js +249 -0
  43. package/dist/plugins/specweave-github/lib/enhanced-github-sync.js.map +1 -0
  44. package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts +63 -0
  45. package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts.map +1 -0
  46. package/dist/plugins/specweave-github/lib/epic-content-builder.js +216 -0
  47. package/dist/plugins/specweave-github/lib/epic-content-builder.js.map +1 -0
  48. package/dist/plugins/specweave-github/lib/github-client.d.ts +1 -1
  49. package/dist/plugins/specweave-github/lib/github-client.d.ts.map +1 -1
  50. package/dist/plugins/specweave-github/lib/github-client.js +25 -13
  51. package/dist/plugins/specweave-github/lib/github-client.js.map +1 -1
  52. package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts +83 -0
  53. package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts.map +1 -0
  54. package/dist/plugins/specweave-github/lib/github-epic-sync.js +466 -0
  55. package/dist/plugins/specweave-github/lib/github-epic-sync.js.map +1 -0
  56. package/dist/plugins/specweave-github/lib/github-status-sync.d.ts +43 -0
  57. package/dist/plugins/specweave-github/lib/github-status-sync.d.ts.map +1 -0
  58. package/dist/plugins/specweave-github/lib/github-status-sync.js +82 -0
  59. package/dist/plugins/specweave-github/lib/github-status-sync.js.map +1 -0
  60. package/dist/plugins/specweave-github/lib/task-sync.d.ts +5 -0
  61. package/dist/plugins/specweave-github/lib/task-sync.d.ts.map +1 -1
  62. package/dist/plugins/specweave-github/lib/task-sync.js +38 -2
  63. package/dist/plugins/specweave-github/lib/task-sync.js.map +1 -1
  64. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts +28 -0
  65. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts.map +1 -0
  66. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js +156 -0
  67. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js.map +1 -0
  68. package/dist/plugins/specweave-jira/lib/jira-epic-sync.d.ts +66 -0
  69. package/dist/plugins/specweave-jira/lib/jira-epic-sync.d.ts.map +1 -0
  70. package/dist/plugins/specweave-jira/lib/jira-epic-sync.js +274 -0
  71. package/dist/plugins/specweave-jira/lib/jira-epic-sync.js.map +1 -0
  72. package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts +56 -0
  73. package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts.map +1 -0
  74. package/dist/plugins/specweave-jira/lib/jira-status-sync.js +93 -0
  75. package/dist/plugins/specweave-jira/lib/jira-status-sync.js.map +1 -0
  76. package/dist/spec-parser.js +629 -0
  77. package/dist/src/cli/commands/init.d.ts.map +1 -1
  78. package/dist/src/cli/commands/init.js +107 -3
  79. package/dist/src/cli/commands/init.js.map +1 -1
  80. package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
  81. package/dist/src/cli/helpers/issue-tracker/index.js +48 -3
  82. package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
  83. package/dist/src/core/deduplication/command-deduplicator.d.ts +166 -0
  84. package/dist/src/core/deduplication/command-deduplicator.d.ts.map +1 -0
  85. package/dist/src/core/deduplication/command-deduplicator.js +254 -0
  86. package/dist/src/core/deduplication/command-deduplicator.js.map +1 -0
  87. package/dist/src/core/living-docs/hierarchy-mapper.d.ts +142 -0
  88. package/dist/src/core/living-docs/hierarchy-mapper.d.ts.map +1 -0
  89. package/dist/src/core/living-docs/hierarchy-mapper.js +453 -0
  90. package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -0
  91. package/dist/src/core/living-docs/index.d.ts +10 -84
  92. package/dist/src/core/living-docs/index.d.ts.map +1 -1
  93. package/dist/src/core/living-docs/index.js +10 -164
  94. package/dist/src/core/living-docs/index.js.map +1 -1
  95. package/dist/src/core/living-docs/spec-distributor.d.ts +106 -0
  96. package/dist/src/core/living-docs/spec-distributor.d.ts.map +1 -0
  97. package/dist/src/core/living-docs/spec-distributor.js +823 -0
  98. package/dist/src/core/living-docs/spec-distributor.js.map +1 -0
  99. package/dist/src/core/living-docs/types.d.ts +201 -0
  100. package/dist/src/core/living-docs/types.d.ts.map +1 -0
  101. package/dist/src/core/living-docs/types.js +15 -0
  102. package/dist/src/core/living-docs/types.js.map +1 -0
  103. package/dist/src/core/logging/prompt-logger.d.ts +70 -0
  104. package/dist/src/core/logging/prompt-logger.d.ts.map +1 -0
  105. package/dist/src/core/logging/prompt-logger.js +247 -0
  106. package/dist/src/core/logging/prompt-logger.js.map +1 -0
  107. package/dist/src/core/status-line/status-line-manager.d.ts +15 -24
  108. package/dist/src/core/status-line/status-line-manager.d.ts.map +1 -1
  109. package/dist/src/core/status-line/status-line-manager.js +33 -70
  110. package/dist/src/core/status-line/status-line-manager.js.map +1 -1
  111. package/dist/src/core/status-line/types.d.ts +19 -31
  112. package/dist/src/core/status-line/types.d.ts.map +1 -1
  113. package/dist/src/core/status-line/types.js +5 -9
  114. package/dist/src/core/status-line/types.js.map +1 -1
  115. package/dist/src/core/sync/conflict-resolver.d.ts +66 -0
  116. package/dist/src/core/sync/conflict-resolver.d.ts.map +1 -0
  117. package/dist/src/core/sync/conflict-resolver.js +108 -0
  118. package/dist/src/core/sync/conflict-resolver.js.map +1 -0
  119. package/dist/src/core/sync/enhanced-content-builder.d.ts +55 -0
  120. package/dist/src/core/sync/enhanced-content-builder.d.ts.map +1 -0
  121. package/dist/src/core/sync/enhanced-content-builder.js +202 -0
  122. package/dist/src/core/sync/enhanced-content-builder.js.map +1 -0
  123. package/dist/src/core/sync/label-detector.d.ts +66 -0
  124. package/dist/src/core/sync/label-detector.d.ts.map +1 -0
  125. package/dist/src/core/sync/label-detector.js +211 -0
  126. package/dist/src/core/sync/label-detector.js.map +1 -0
  127. package/dist/src/core/sync/retry-logic.d.ts +64 -0
  128. package/dist/src/core/sync/retry-logic.d.ts.map +1 -0
  129. package/dist/src/core/sync/retry-logic.js +165 -0
  130. package/dist/src/core/sync/retry-logic.js.map +1 -0
  131. package/dist/src/core/sync/spec-content-sync.d.ts +88 -0
  132. package/dist/src/core/sync/spec-content-sync.d.ts.map +1 -0
  133. package/dist/src/core/sync/spec-content-sync.js +5 -0
  134. package/dist/src/core/sync/spec-content-sync.js.map +1 -0
  135. package/dist/src/core/sync/spec-increment-mapper.d.ts +100 -0
  136. package/dist/src/core/sync/spec-increment-mapper.d.ts.map +1 -0
  137. package/dist/src/core/sync/spec-increment-mapper.js +424 -0
  138. package/dist/src/core/sync/spec-increment-mapper.js.map +1 -0
  139. package/dist/src/core/sync/status-cache.d.ts +91 -0
  140. package/dist/src/core/sync/status-cache.d.ts.map +1 -0
  141. package/dist/src/core/sync/status-cache.js +140 -0
  142. package/dist/src/core/sync/status-cache.js.map +1 -0
  143. package/dist/src/core/sync/status-mapper.d.ts +69 -0
  144. package/dist/src/core/sync/status-mapper.d.ts.map +1 -0
  145. package/dist/src/core/sync/status-mapper.js +90 -0
  146. package/dist/src/core/sync/status-mapper.js.map +1 -0
  147. package/dist/src/core/sync/status-sync-engine.d.ts +162 -0
  148. package/dist/src/core/sync/status-sync-engine.d.ts.map +1 -0
  149. package/dist/src/core/sync/status-sync-engine.js +347 -0
  150. package/dist/src/core/sync/status-sync-engine.js.map +1 -0
  151. package/dist/src/core/sync/sync-event-logger.d.ts +99 -0
  152. package/dist/src/core/sync/sync-event-logger.d.ts.map +1 -0
  153. package/dist/src/core/sync/sync-event-logger.js +103 -0
  154. package/dist/src/core/sync/sync-event-logger.js.map +1 -0
  155. package/dist/src/core/sync/types.d.ts +52 -0
  156. package/dist/src/core/sync/types.d.ts.map +1 -0
  157. package/dist/src/core/sync/types.js +5 -0
  158. package/dist/src/core/sync/types.js.map +1 -0
  159. package/dist/src/core/sync/workflow-detector.d.ts +95 -0
  160. package/dist/src/core/sync/workflow-detector.d.ts.map +1 -0
  161. package/dist/src/core/sync/workflow-detector.js +175 -0
  162. package/dist/src/core/sync/workflow-detector.js.map +1 -0
  163. package/dist/src/core/types/config.d.ts +51 -0
  164. package/dist/src/core/types/config.d.ts.map +1 -1
  165. package/dist/src/core/types/config.js +47 -0
  166. package/dist/src/core/types/config.js.map +1 -1
  167. package/dist/src/core/types/increment-metadata.d.ts +4 -0
  168. package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
  169. package/dist/src/core/types/increment-metadata.js.map +1 -1
  170. package/dist/src/utils/github-url.d.ts +53 -0
  171. package/dist/src/utils/github-url.d.ts.map +1 -0
  172. package/dist/src/utils/github-url.js +90 -0
  173. package/dist/src/utils/github-url.js.map +1 -0
  174. package/dist/src/utils/spec-parser.d.ts +145 -0
  175. package/dist/src/utils/spec-parser.d.ts.map +1 -0
  176. package/dist/src/utils/spec-parser.js +640 -0
  177. package/dist/src/utils/spec-parser.js.map +1 -0
  178. package/dist/tsconfig.tsbuildinfo +1 -0
  179. package/package.json +1 -1
  180. package/plugins/specweave/agents/pm/AGENT.md +160 -13
  181. package/plugins/specweave/agents/pm/templates/increment-spec.md +158 -0
  182. package/plugins/specweave/agents/pm/templates/living-docs-spec.md +113 -0
  183. package/plugins/specweave/commands/specweave-done.md +163 -0
  184. package/plugins/specweave/commands/specweave.md +70 -405
  185. package/plugins/specweave/hooks/hooks.json +4 -0
  186. package/plugins/specweave/hooks/lib/sync-spec-content.sh +2 -2
  187. package/plugins/specweave/hooks/lib/update-status-line.sh +79 -111
  188. package/plugins/specweave/hooks/post-increment-planning.sh +133 -37
  189. package/plugins/specweave/hooks/pre-command-deduplication.sh +86 -0
  190. package/plugins/specweave/lib/hooks/sync-living-docs.js +139 -34
  191. package/plugins/specweave/lib/hooks/sync-living-docs.ts +234 -38
  192. package/plugins/specweave/skills/SKILLS-INDEX.md +4 -24
  193. package/plugins/specweave/skills/increment-planner/SKILL.md +94 -0
  194. package/plugins/specweave/skills/increment-work-router/SKILL.md +466 -0
  195. package/plugins/specweave-ado/commands/specweave-ado-sync-spec.md +1 -1
  196. package/plugins/specweave-ado/lib/ado-spec-content-sync.js +49 -5
  197. package/plugins/specweave-ado/lib/ado-spec-content-sync.ts +72 -6
  198. package/plugins/specweave-ado/lib/ado-status-sync.js +80 -0
  199. package/plugins/specweave-ado/lib/ado-status-sync.ts +121 -0
  200. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
  201. package/plugins/specweave-github/commands/specweave-github-cleanup-duplicates.md +205 -0
  202. package/plugins/specweave-github/commands/specweave-github-sync-epic.md +248 -0
  203. package/plugins/specweave-github/commands/specweave-github-sync-spec.md +1 -1
  204. package/plugins/specweave-github/hooks/post-task-completion.sh +32 -0
  205. package/plugins/specweave-github/lib/duplicate-detector.js +370 -0
  206. package/plugins/specweave-github/lib/duplicate-detector.ts +525 -0
  207. package/plugins/specweave-github/lib/enhanced-github-sync.js +220 -0
  208. package/plugins/specweave-github/lib/enhanced-github-sync.ts +322 -0
  209. package/plugins/specweave-github/lib/epic-content-builder.js +227 -0
  210. package/plugins/specweave-github/lib/epic-content-builder.ts +317 -0
  211. package/plugins/specweave-github/lib/github-client.js +21 -10
  212. package/plugins/specweave-github/lib/github-client.ts +27 -16
  213. package/plugins/specweave-github/lib/github-epic-sync.js +488 -0
  214. package/plugins/specweave-github/lib/github-epic-sync.ts +715 -0
  215. package/plugins/specweave-github/lib/github-status-sync.js +71 -0
  216. package/plugins/specweave-github/lib/github-status-sync.ts +107 -0
  217. package/plugins/specweave-github/lib/task-sync.js +33 -2
  218. package/plugins/specweave-github/lib/task-sync.ts +44 -2
  219. package/plugins/specweave-jira/commands/specweave-jira-sync-epic.md +267 -0
  220. package/plugins/specweave-jira/commands/specweave-jira-sync-spec.md +1 -1
  221. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
  222. package/plugins/specweave-jira/lib/enhanced-jira-sync.ts +196 -0
  223. package/plugins/specweave-jira/lib/jira-epic-sync.js +304 -0
  224. package/plugins/specweave-jira/lib/jira-epic-sync.ts +459 -0
  225. package/plugins/specweave-jira/lib/jira-status-sync.js +79 -0
  226. package/plugins/specweave-jira/lib/jira-status-sync.ts +139 -0
  227. package/plugins/specweave-release/commands/specweave-release-platform.md +1 -1
  228. package/plugins/specweave-release/hooks/post-task-completion.sh +2 -2
  229. package/src/templates/AGENTS.md.template +88 -1
  230. package/src/templates/CLAUDE.md.template +49 -0
  231. package/plugins/specweave/skills/increment-quality-judge/SKILL.md +0 -524
  232. package/plugins/specweave/skills/plugin-installer/SKILL.md +0 -353
@@ -0,0 +1,71 @@
1
+ import { Octokit } from "@octokit/rest";
2
+ class GitHubStatusSync {
3
+ constructor(token, owner, repo) {
4
+ this.octokit = new Octokit({ auth: token });
5
+ this.owner = owner;
6
+ this.repo = repo;
7
+ }
8
+ /**
9
+ * Get current status from GitHub issue
10
+ *
11
+ * @param issueNumber - GitHub issue number
12
+ * @returns External status with state and labels
13
+ */
14
+ async getStatus(issueNumber) {
15
+ const response = await this.octokit.rest.issues.get({
16
+ owner: this.owner,
17
+ repo: this.repo,
18
+ issue_number: issueNumber
19
+ });
20
+ const labels = response.data.labels.map((label) => typeof label === "string" ? label : label.name).filter(Boolean);
21
+ return {
22
+ state: response.data.state,
23
+ labels
24
+ };
25
+ }
26
+ /**
27
+ * Update GitHub issue status
28
+ *
29
+ * @param issueNumber - GitHub issue number
30
+ * @param status - New status (state and labels)
31
+ */
32
+ async updateStatus(issueNumber, status) {
33
+ const updateData = {
34
+ owner: this.owner,
35
+ repo: this.repo,
36
+ issue_number: issueNumber,
37
+ state: status.state
38
+ };
39
+ if (status.labels && status.labels.length > 0) {
40
+ updateData.labels = status.labels;
41
+ }
42
+ await this.octokit.rest.issues.update(updateData);
43
+ }
44
+ /**
45
+ * Post status change comment to GitHub issue
46
+ *
47
+ * @param issueNumber - GitHub issue number
48
+ * @param oldStatus - Previous SpecWeave status
49
+ * @param newStatus - New SpecWeave status
50
+ */
51
+ async postStatusComment(issueNumber, oldStatus, newStatus) {
52
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
53
+ const body = `\u{1F504} **Status Update**
54
+
55
+ SpecWeave increment status changed:
56
+ - **From**: ${oldStatus}
57
+ - **To**: ${newStatus}
58
+ - **When**: ${timestamp}
59
+
60
+ This update was automatically synchronized by SpecWeave.`;
61
+ await this.octokit.rest.issues.createComment({
62
+ owner: this.owner,
63
+ repo: this.repo,
64
+ issue_number: issueNumber,
65
+ body
66
+ });
67
+ }
68
+ }
69
+ export {
70
+ GitHubStatusSync
71
+ };
@@ -0,0 +1,107 @@
1
+ /**
2
+ * GitHub Status Sync
3
+ *
4
+ * Synchronizes SpecWeave increment statuses with GitHub issues.
5
+ *
6
+ * Responsibilities:
7
+ * - Get current status from GitHub issue
8
+ * - Update GitHub issue state and labels
9
+ * - Post status change comments
10
+ */
11
+
12
+ import { Octokit } from '@octokit/rest';
13
+
14
+ export interface ExternalStatus {
15
+ state: string;
16
+ labels?: string[];
17
+ }
18
+
19
+ export class GitHubStatusSync {
20
+ private octokit: Octokit;
21
+ private owner: string;
22
+ private repo: string;
23
+
24
+ constructor(token: string, owner: string, repo: string) {
25
+ this.octokit = new Octokit({ auth: token });
26
+ this.owner = owner;
27
+ this.repo = repo;
28
+ }
29
+
30
+ /**
31
+ * Get current status from GitHub issue
32
+ *
33
+ * @param issueNumber - GitHub issue number
34
+ * @returns External status with state and labels
35
+ */
36
+ public async getStatus(issueNumber: number): Promise<ExternalStatus> {
37
+ const response = await this.octokit.rest.issues.get({
38
+ owner: this.owner,
39
+ repo: this.repo,
40
+ issue_number: issueNumber
41
+ });
42
+
43
+ const labels = response.data.labels
44
+ .map((label: any) => (typeof label === 'string' ? label : label.name))
45
+ .filter(Boolean);
46
+
47
+ return {
48
+ state: response.data.state,
49
+ labels
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Update GitHub issue status
55
+ *
56
+ * @param issueNumber - GitHub issue number
57
+ * @param status - New status (state and labels)
58
+ */
59
+ public async updateStatus(
60
+ issueNumber: number,
61
+ status: ExternalStatus
62
+ ): Promise<void> {
63
+ const updateData: any = {
64
+ owner: this.owner,
65
+ repo: this.repo,
66
+ issue_number: issueNumber,
67
+ state: status.state
68
+ };
69
+
70
+ // Add labels if provided
71
+ if (status.labels && status.labels.length > 0) {
72
+ updateData.labels = status.labels;
73
+ }
74
+
75
+ await this.octokit.rest.issues.update(updateData);
76
+ }
77
+
78
+ /**
79
+ * Post status change comment to GitHub issue
80
+ *
81
+ * @param issueNumber - GitHub issue number
82
+ * @param oldStatus - Previous SpecWeave status
83
+ * @param newStatus - New SpecWeave status
84
+ */
85
+ public async postStatusComment(
86
+ issueNumber: number,
87
+ oldStatus: string,
88
+ newStatus: string
89
+ ): Promise<void> {
90
+ const timestamp = new Date().toISOString();
91
+ const body = `🔄 **Status Update**
92
+
93
+ SpecWeave increment status changed:
94
+ - **From**: ${oldStatus}
95
+ - **To**: ${newStatus}
96
+ - **When**: ${timestamp}
97
+
98
+ This update was automatically synchronized by SpecWeave.`;
99
+
100
+ await this.octokit.rest.issues.createComment({
101
+ owner: this.owner,
102
+ repo: this.repo,
103
+ issue_number: issueNumber,
104
+ body
105
+ });
106
+ }
107
+ }
@@ -61,9 +61,10 @@ class TaskSync {
61
61
  console.log(` \u2705 Milestone #${milestone.number}: ${milestone.title}`);
62
62
  console.log(`
63
63
  \u{1F3AF} Creating epic issue for increment ${metadata.id}...`);
64
+ const issuePrefix = this.getIssuePrefix(metadata.id);
64
65
  const epicBody = this.generateEpicBody(metadata, tasks);
65
66
  const epic = await this.client.createEpicIssue(
66
- `[INC-${metadata.id}] ${metadata.title}`,
67
+ `[${issuePrefix}] ${metadata.title}`,
67
68
  epicBody,
68
69
  milestone.title,
69
70
  ["increment", "specweave", metadata.priority.toLowerCase()]
@@ -328,6 +329,35 @@ This task blocks:
328
329
  const metadataPath = path.join(this.incrementPath, ".metadata.yaml");
329
330
  fs.writeFileSync(metadataPath, yaml.dump(metadata), "utf-8");
330
331
  }
332
+ /**
333
+ * Get issue prefix from metadata.json (with created date)
334
+ * Returns FS-YY-MM-DD format or fallback to FS-UNKNOWN
335
+ */
336
+ getIssuePrefix(incrementId) {
337
+ const metadataJsonPath = path.join(this.incrementPath, "metadata.json");
338
+ if (fs.existsSync(metadataJsonPath)) {
339
+ try {
340
+ const metadataContent = fs.readFileSync(metadataJsonPath, "utf-8");
341
+ const metadata = JSON.parse(metadataContent);
342
+ if (metadata.created) {
343
+ const dateMatch = metadata.created.match(/^(\d{4})-(\d{2})-(\d{2})/);
344
+ if (dateMatch) {
345
+ const year = dateMatch[1].slice(2);
346
+ const month = dateMatch[2];
347
+ const day = dateMatch[3];
348
+ return `FS-${year}-${month}-${day}`;
349
+ }
350
+ }
351
+ } catch (error) {
352
+ console.warn(`\u26A0\uFE0F Could not parse metadata.json: ${error}`);
353
+ }
354
+ }
355
+ const incrementNumber = incrementId.match(/^(\d+)/)?.[1];
356
+ if (incrementNumber) {
357
+ return `FS-${incrementNumber}`;
358
+ }
359
+ return "FS-UNKNOWN";
360
+ }
331
361
  /**
332
362
  * Save sync mapping
333
363
  */
@@ -363,10 +393,11 @@ This task blocks:
363
393
  * Print dry run summary
364
394
  */
365
395
  printDryRunSummary(tasks, metadata) {
396
+ const issuePrefix = this.getIssuePrefix(metadata.id);
366
397
  console.log(`
367
398
  \u{1F4CA} Dry Run Summary:`);
368
399
  console.log(` Milestone: ${this.getMilestoneTitle(metadata)}`);
369
- console.log(` Epic: [INC-${metadata.id}] ${metadata.title}`);
400
+ console.log(` Epic: [${issuePrefix}] ${metadata.title}`);
370
401
  console.log(` Task Issues: ${tasks.length}`);
371
402
  console.log(`
372
403
  \u{1F4DD} Would create:`);
@@ -81,9 +81,13 @@ export class TaskSync {
81
81
 
82
82
  // 6. Create epic issue
83
83
  console.log(`\n🎯 Creating epic issue for increment ${metadata.id}...`);
84
+
85
+ // Get issue prefix from metadata.json (with created date)
86
+ const issuePrefix = this.getIssuePrefix(metadata.id);
87
+
84
88
  const epicBody = this.generateEpicBody(metadata, tasks);
85
89
  const epic = await this.client.createEpicIssue(
86
- `[INC-${metadata.id}] ${metadata.title}`,
90
+ `[${issuePrefix}] ${metadata.title}`,
87
91
  epicBody,
88
92
  milestone.title,
89
93
  ['increment', 'specweave', metadata.priority.toLowerCase()]
@@ -348,6 +352,43 @@ ${task.description}
348
352
  fs.writeFileSync(metadataPath, yaml.dump(metadata), 'utf-8');
349
353
  }
350
354
 
355
+ /**
356
+ * Get issue prefix from metadata.json (with created date)
357
+ * Returns FS-YY-MM-DD format or fallback to FS-UNKNOWN
358
+ */
359
+ private getIssuePrefix(incrementId: string): string {
360
+ const metadataJsonPath = path.join(this.incrementPath, 'metadata.json');
361
+
362
+ // Try to read metadata.json for created date
363
+ if (fs.existsSync(metadataJsonPath)) {
364
+ try {
365
+ const metadataContent = fs.readFileSync(metadataJsonPath, 'utf-8');
366
+ const metadata = JSON.parse(metadataContent);
367
+
368
+ if (metadata.created) {
369
+ // Extract YY-MM-DD from date (e.g., "2025-11-12T12:46:00Z" -> "25-11-12")
370
+ const dateMatch = metadata.created.match(/^(\d{4})-(\d{2})-(\d{2})/);
371
+ if (dateMatch) {
372
+ const year = dateMatch[1].slice(2); // "2025" -> "25"
373
+ const month = dateMatch[2]; // "11"
374
+ const day = dateMatch[3]; // "12"
375
+ return `FS-${year}-${month}-${day}`;
376
+ }
377
+ }
378
+ } catch (error) {
379
+ console.warn(`⚠️ Could not parse metadata.json: ${error}`);
380
+ }
381
+ }
382
+
383
+ // Fallback: use increment number if available
384
+ const incrementNumber = incrementId.match(/^(\d+)/)?.[1];
385
+ if (incrementNumber) {
386
+ return `FS-${incrementNumber}`;
387
+ }
388
+
389
+ return 'FS-UNKNOWN';
390
+ }
391
+
351
392
  /**
352
393
  * Save sync mapping
353
394
  */
@@ -390,9 +431,10 @@ ${task.description}
390
431
  * Print dry run summary
391
432
  */
392
433
  private printDryRunSummary(tasks: Task[], metadata: IncrementMetadata): void {
434
+ const issuePrefix = this.getIssuePrefix(metadata.id);
393
435
  console.log(`\n📊 Dry Run Summary:`);
394
436
  console.log(` Milestone: ${this.getMilestoneTitle(metadata)}`);
395
- console.log(` Epic: [INC-${metadata.id}] ${metadata.title}`);
437
+ console.log(` Epic: [${issuePrefix}] ${metadata.title}`);
396
438
  console.log(` Task Issues: ${tasks.length}`);
397
439
  console.log(`\n📝 Would create:`);
398
440
  tasks.forEach((task, i) => {
@@ -0,0 +1,267 @@
1
+ ---
2
+ name: specweave-jira:sync-epic
3
+ description: Sync SpecWeave Epic folder to JIRA (Epic + Stories with Epic Link). Implements Universal Hierarchy architecture - Epic → JIRA Epic, Increments → Stories.
4
+ ---
5
+
6
+ # Sync Epic to JIRA (Universal Hierarchy)
7
+
8
+ **Architecture**: Hierarchical sync using Epic folder structure
9
+
10
+ - **Epic (FS-001)** → **JIRA Epic**
11
+ - **Increment (0001-core-framework)** → **JIRA Story** (Epic Link field)
12
+
13
+ ## Usage
14
+
15
+ ```bash
16
+ /specweave-jira:sync-epic <epic-id> [--project <project-key>]
17
+ ```
18
+
19
+ ## What It Does
20
+
21
+ **Hierarchical Sync Process**:
22
+
23
+ 1. **Load Epic folder** from `.specweave/docs/internal/specs/FS-XXX-name/`
24
+ 2. **Parse Epic README.md** to get Epic metadata (title, increments, status)
25
+ 3. **Create or update JIRA Epic**:
26
+ - Summary: `[FS-001] Epic Title`
27
+ - Description: Epic overview + progress stats
28
+ - Priority: Mapped from P0→Highest, P1→High, etc.
29
+ - Labels: `epic-sync`, `fs-001`
30
+ 4. **Sync each increment as JIRA Story**:
31
+ - Summary: `[INC-0001-core-framework] Title`
32
+ - Description: Increment overview
33
+ - Epic Link: Links to Epic via `epicKey` field
34
+ - Labels: `increment`, `epic-sync`
35
+ 5. **Update frontmatter** in Epic README.md and increment files
36
+
37
+ ## Examples
38
+
39
+ ### Sync Epic FS-001 (Core Framework Architecture)
40
+
41
+ ```bash
42
+ /specweave-jira:sync-epic FS-001 --project SPEC
43
+ ```
44
+
45
+ **Output**:
46
+ ```
47
+ 🔄 Syncing Epic FS-001 to JIRA...
48
+ 📦 Epic: Core Framework Architecture
49
+ 📊 Increments: 4
50
+ 🚀 Creating JIRA Epic...
51
+ ✅ Created Epic SPEC-100
52
+
53
+ 📝 Syncing 4 increments...
54
+ ✅ Created Story SPEC-101 for 0001-core-framework
55
+ ✅ Created Story SPEC-102 for 0002-core-enhancements
56
+ ✅ Created Story SPEC-103 for 0004-plugin-architecture
57
+ ✅ Created Story SPEC-104 for 0005-cross-platform-cli
58
+
59
+ ✅ Epic sync complete!
60
+ Epic: https://mycompany.atlassian.net/browse/SPEC-100
61
+ Stories created: 4
62
+ Stories updated: 0
63
+ ```
64
+
65
+ ### Sync Epic with short ID
66
+
67
+ ```bash
68
+ /specweave-jira:sync-epic 031 --project SPEC
69
+ ```
70
+
71
+ ### Re-sync Epic (updates existing)
72
+
73
+ ```bash
74
+ /specweave-jira:sync-epic FS-001 --project SPEC
75
+ ```
76
+
77
+ **Output**:
78
+ ```
79
+ 🔄 Syncing Epic FS-001 to JIRA...
80
+ ♻️ Updating existing Epic SPEC-100...
81
+ ✅ Updated Epic SPEC-100
82
+
83
+ 📝 Syncing 4 increments...
84
+ ♻️ Updated Story SPEC-101 for 0001-core-framework
85
+ ♻️ Updated Story SPEC-102 for 0002-core-enhancements
86
+ ♻️ Updated Story SPEC-103 for 0004-plugin-architecture
87
+ ♻️ Updated Story SPEC-104 for 0005-cross-platform-cli
88
+
89
+ ✅ Epic sync complete!
90
+ Epic: https://mycompany.atlassian.net/browse/SPEC-100
91
+ Stories created: 0
92
+ Stories updated: 4
93
+ ```
94
+
95
+ ## Arguments
96
+
97
+ - `<epic-id>` - Epic ID (e.g., `FS-001` or just `001`)
98
+ - `--project <key>` - JIRA project key (e.g., `SPEC`, `PROJ`, `DEV`)
99
+
100
+ ## What Gets Created
101
+
102
+ ### JIRA Epic (Epic-level)
103
+
104
+ ```
105
+ Key: SPEC-100
106
+ Summary: [FS-001] Core Framework Architecture
107
+
108
+ Description:
109
+ Epic: Core Framework Architecture
110
+
111
+ Progress: 4/4 increments (100%)
112
+
113
+ Priority: P0
114
+ Status: complete
115
+
116
+ Labels: epic-sync, fs-001
117
+ Priority: Highest (P0 → Highest)
118
+ ```
119
+
120
+ ### JIRA Story (Increment-level)
121
+
122
+ ```
123
+ Key: SPEC-101
124
+ Summary: [INC-0001-core-framework] Core Framework
125
+
126
+ Description:
127
+ Foundation framework with CLI, plugin system, and agent architecture...
128
+
129
+ ---
130
+
131
+ **Increment**: 0001-core-framework
132
+ **Epic**: SPEC-100
133
+
134
+ 🤖 Auto-created by SpecWeave Epic Sync
135
+
136
+ Epic Link: SPEC-100 (linked via Epic Link field)
137
+ Labels: increment, epic-sync
138
+ ```
139
+
140
+ ## Frontmatter Updates
141
+
142
+ ### Epic README.md (after sync)
143
+
144
+ ```yaml
145
+ ---
146
+ id: FS-001
147
+ title: "Core Framework Architecture"
148
+ external_tools:
149
+ jira:
150
+ type: epic
151
+ key: SPEC-100 # ← Added
152
+ url: https://mycompany.atlassian.net/browse/SPEC-100 # ← Added
153
+ increments:
154
+ - id: 0001-core-framework
155
+ external:
156
+ jira: SPEC-101 # ← Added
157
+ - id: 0002-core-enhancements
158
+ external:
159
+ jira: SPEC-102 # ← Added
160
+ ---
161
+ ```
162
+
163
+ ### Increment file (0001-core-framework.md)
164
+
165
+ ```yaml
166
+ ---
167
+ id: 0001-core-framework
168
+ epic: FS-001
169
+ external:
170
+ jira:
171
+ story: SPEC-101 # ← Added
172
+ url: https://mycompany.atlassian.net/browse/SPEC-101 # ← Added
173
+ ---
174
+ ```
175
+
176
+ ## Benefits
177
+
178
+ ✅ **Hierarchical tracking**: JIRA Epics group related Stories
179
+ ✅ **Epic-level progress**: See completion in Epic details
180
+ ✅ **Automatic linking**: Epic Link field links Stories to Epic
181
+ ✅ **Idempotent**: Safe to re-run (updates existing Epic/Stories)
182
+ ✅ **Brownfield-ready**: Links existing JIRA Epics/Stories
183
+
184
+ ## Requirements
185
+
186
+ 1. **JIRA credentials** in `.env`:
187
+ ```
188
+ JIRA_DOMAIN=mycompany.atlassian.net
189
+ JIRA_EMAIL=user@mycompany.com
190
+ JIRA_API_TOKEN=your-api-token
191
+ ```
192
+ 2. **Project exists** in JIRA
193
+ 3. **Write access** to project (for creating Epics/Stories)
194
+ 4. **Epic folder exists** at `.specweave/docs/internal/specs/FS-XXX-name/`
195
+
196
+ ## Architecture: Why Epic Link?
197
+
198
+ **JIRA's Hierarchy**:
199
+ - JIRA Epics = Top-level grouping
200
+ - JIRA Stories = Implementation work
201
+ - Epic Link field = Links Stories to Epic
202
+
203
+ **Comparison with GitHub/ADO**:
204
+ - GitHub: Epic → Milestone, Increment → Issue (Milestone link)
205
+ - JIRA: Epic → Epic, Increment → Story (Epic Link field)
206
+ - ADO: Epic → Feature, Increment → User Story (Parent link)
207
+
208
+ All three implement the same Universal Hierarchy, just with different fields/terminology.
209
+
210
+ ## Priority Mapping
211
+
212
+ | SpecWeave | JIRA |
213
+ |-----------|------|
214
+ | P0 | Highest |
215
+ | P1 | High |
216
+ | P2 | Medium |
217
+ | P3 | Low |
218
+
219
+ ## Troubleshooting
220
+
221
+ **"Epic FS-001 not found"**:
222
+ - Check Epic folder exists: `ls .specweave/docs/internal/specs/`
223
+ - Verify Epic ID format: `FS-001-epic-name/`
224
+
225
+ **"Epic README.md missing YAML frontmatter"**:
226
+ - Ensure Epic was migrated with `migrate-to-epic-folders.ts`
227
+ - Frontmatter must start with `---` on line 1
228
+
229
+ **"Failed to create JIRA Epic"**:
230
+ - Check credentials: `echo $JIRA_API_TOKEN`
231
+ - Verify project exists in JIRA
232
+ - Check write permissions
233
+
234
+ **"Epic Link field not found"**:
235
+ - Some JIRA instances use custom field for Epic Link
236
+ - Check with JIRA admin for custom field ID
237
+ - May need configuration update
238
+
239
+ ## Related Commands
240
+
241
+ - `/specweave-github:sync-epic` - Sync to GitHub Milestone + Issues
242
+ - `/specweave-ado:sync-epic` - Sync to ADO Feature + User Stories
243
+ - `/specweave-jira:sync-spec` - OLD (flat spec → epic) - DEPRECATED
244
+
245
+ ## Implementation
246
+
247
+ **File**: `plugins/specweave-jira/lib/jira-epic-sync.ts`
248
+
249
+ **Core Class**: `JiraEpicSync`
250
+
251
+ **Methods**:
252
+ - `syncEpicToJira(epicId)` - Main sync logic
253
+ - `createEpic(epic)` - Create JIRA Epic
254
+ - `updateEpic(key, epic)` - Update existing Epic
255
+ - `createStory(increment, epicKey)` - Create JIRA Story
256
+ - `updateStory(key, increment, epicKey)` - Update existing Story
257
+ - `updateEpicReadme(path, jira)` - Update frontmatter
258
+ - `updateIncrementExternalLink(...)` - Update increment frontmatter
259
+
260
+ ## Next Steps
261
+
262
+ After syncing Epic to JIRA:
263
+
264
+ 1. **View Epic**: Open in JIRA browser
265
+ 2. **List Stories**: Filter by Epic Link field
266
+ 3. **Track completion**: JIRA shows Epic progress automatically
267
+ 4. **Link to Sprint**: Drag Stories into Sprint planning
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: specweave-jira-sync-spec
2
+ name: specweave-jira:sync-spec
3
3
  description: Sync SpecWeave spec to Jira Epic (bidirectional). Use when syncing .specweave/docs/internal/specs/spec-*.md files with Jira Epics for permanent feature tracking.
4
4
  ---
5
5