specweave 1.0.355 → 1.0.357

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 (169) hide show
  1. package/bin/specweave.js +21 -1
  2. package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts.map +1 -1
  3. package/dist/plugins/specweave-github/lib/duplicate-detector.js +18 -2
  4. package/dist/plugins/specweave-github/lib/duplicate-detector.js.map +1 -1
  5. package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -1
  6. package/dist/plugins/specweave-github/lib/github-feature-sync.js +10 -0
  7. package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
  8. package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts +17 -0
  9. package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts.map +1 -1
  10. package/dist/plugins/specweave-jira/lib/jira-status-sync.js +85 -0
  11. package/dist/plugins/specweave-jira/lib/jira-status-sync.js.map +1 -1
  12. package/dist/src/adapters/agents-md-generator.js +1 -1
  13. package/dist/src/adapters/agents-md-generator.js.map +1 -1
  14. package/dist/src/adapters/claude/adapter.d.ts +3 -4
  15. package/dist/src/adapters/claude/adapter.d.ts.map +1 -1
  16. package/dist/src/adapters/claude/adapter.js +3 -4
  17. package/dist/src/adapters/claude/adapter.js.map +1 -1
  18. package/dist/src/adapters/claude-md-generator.js +2 -2
  19. package/dist/src/adapters/claude-md-generator.js.map +1 -1
  20. package/dist/src/cli/commands/check-discipline.d.ts.map +1 -1
  21. package/dist/src/cli/commands/check-discipline.js +2 -1
  22. package/dist/src/cli/commands/check-discipline.js.map +1 -1
  23. package/dist/src/cli/commands/create-increment.d.ts.map +1 -1
  24. package/dist/src/cli/commands/create-increment.js +4 -1
  25. package/dist/src/cli/commands/create-increment.js.map +1 -1
  26. package/dist/src/cli/commands/health.d.ts +34 -0
  27. package/dist/src/cli/commands/health.d.ts.map +1 -0
  28. package/dist/src/cli/commands/health.js +349 -0
  29. package/dist/src/cli/commands/health.js.map +1 -0
  30. package/dist/src/cli/commands/migrate-to-umbrella.d.ts.map +1 -1
  31. package/dist/src/cli/commands/migrate-to-umbrella.js +38 -0
  32. package/dist/src/cli/commands/migrate-to-umbrella.js.map +1 -1
  33. package/dist/src/cli/commands/refresh-plugins.d.ts +3 -2
  34. package/dist/src/cli/commands/refresh-plugins.d.ts.map +1 -1
  35. package/dist/src/cli/commands/refresh-plugins.js +3 -8
  36. package/dist/src/cli/commands/refresh-plugins.js.map +1 -1
  37. package/dist/src/cli/commands/save.js +1 -1
  38. package/dist/src/cli/commands/save.js.map +1 -1
  39. package/dist/src/cli/commands/sync-living-docs.d.ts.map +1 -1
  40. package/dist/src/cli/commands/sync-living-docs.js +2 -1
  41. package/dist/src/cli/commands/sync-living-docs.js.map +1 -1
  42. package/dist/src/cli/commands/sync-progress.d.ts.map +1 -1
  43. package/dist/src/cli/commands/sync-progress.js +208 -8
  44. package/dist/src/cli/commands/sync-progress.js.map +1 -1
  45. package/dist/src/cli/commands/update.d.ts.map +1 -1
  46. package/dist/src/cli/commands/update.js +35 -4
  47. package/dist/src/cli/commands/update.js.map +1 -1
  48. package/dist/src/core/ac-progress-sync.d.ts.map +1 -1
  49. package/dist/src/core/ac-progress-sync.js +13 -4
  50. package/dist/src/core/ac-progress-sync.js.map +1 -1
  51. package/dist/src/core/config/types.d.ts +33 -0
  52. package/dist/src/core/config/types.d.ts.map +1 -1
  53. package/dist/src/core/config/types.js.map +1 -1
  54. package/dist/src/core/doctor/checkers/increments-checker.js +2 -2
  55. package/dist/src/core/doctor/checkers/increments-checker.js.map +1 -1
  56. package/dist/src/core/doctor/checkers/installation-health-checker.d.ts +14 -15
  57. package/dist/src/core/doctor/checkers/installation-health-checker.d.ts.map +1 -1
  58. package/dist/src/core/doctor/checkers/installation-health-checker.js +78 -148
  59. package/dist/src/core/doctor/checkers/installation-health-checker.js.map +1 -1
  60. package/dist/src/core/hooks/LifecycleHookDispatcher.d.ts.map +1 -1
  61. package/dist/src/core/hooks/LifecycleHookDispatcher.js +6 -1
  62. package/dist/src/core/hooks/LifecycleHookDispatcher.js.map +1 -1
  63. package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
  64. package/dist/src/core/increment/completion-validator.js +8 -7
  65. package/dist/src/core/increment/completion-validator.js.map +1 -1
  66. package/dist/src/core/increment/increment-reopener.d.ts.map +1 -1
  67. package/dist/src/core/increment/increment-reopener.js +5 -4
  68. package/dist/src/core/increment/increment-reopener.js.map +1 -1
  69. package/dist/src/core/increment/metadata-manager.d.ts +5 -6
  70. package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
  71. package/dist/src/core/increment/metadata-manager.js +12 -13
  72. package/dist/src/core/increment/metadata-manager.js.map +1 -1
  73. package/dist/src/core/increment/spec-frontmatter-updater.d.ts.map +1 -1
  74. package/dist/src/core/increment/spec-frontmatter-updater.js +3 -2
  75. package/dist/src/core/increment/spec-frontmatter-updater.js.map +1 -1
  76. package/dist/src/core/increment/status-auto-transition.d.ts.map +1 -1
  77. package/dist/src/core/increment/status-auto-transition.js +5 -4
  78. package/dist/src/core/increment/status-auto-transition.js.map +1 -1
  79. package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
  80. package/dist/src/core/increment/status-change-sync-trigger.js +3 -2
  81. package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
  82. package/dist/src/core/increment/status-commands.d.ts.map +1 -1
  83. package/dist/src/core/increment/status-commands.js +4 -3
  84. package/dist/src/core/increment/status-commands.js.map +1 -1
  85. package/dist/src/core/increment/template-creator.d.ts.map +1 -1
  86. package/dist/src/core/increment/template-creator.js +49 -5
  87. package/dist/src/core/increment/template-creator.js.map +1 -1
  88. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts +2 -2
  89. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts.map +1 -1
  90. package/dist/src/core/lazy-loading/llm-plugin-detector.js +32 -16
  91. package/dist/src/core/lazy-loading/llm-plugin-detector.js.map +1 -1
  92. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  93. package/dist/src/core/living-docs/living-docs-sync.js +16 -6
  94. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  95. package/dist/src/core/migration/consolidation-engine.d.ts +59 -0
  96. package/dist/src/core/migration/consolidation-engine.d.ts.map +1 -0
  97. package/dist/src/core/migration/consolidation-engine.js +177 -0
  98. package/dist/src/core/migration/consolidation-engine.js.map +1 -0
  99. package/dist/src/core/migration/types.d.ts +2 -0
  100. package/dist/src/core/migration/types.d.ts.map +1 -1
  101. package/dist/src/core/project/project-resolution.d.ts.map +1 -1
  102. package/dist/src/core/project/project-resolution.js +20 -2
  103. package/dist/src/core/project/project-resolution.js.map +1 -1
  104. package/dist/src/generators/spec/spec-parser.js +2 -2
  105. package/dist/src/generators/spec/spec-parser.js.map +1 -1
  106. package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
  107. package/dist/src/integrations/jira/jira-client.js +83 -30
  108. package/dist/src/integrations/jira/jira-client.js.map +1 -1
  109. package/dist/src/sync/external-issue-auto-creator.d.ts +6 -2
  110. package/dist/src/sync/external-issue-auto-creator.d.ts.map +1 -1
  111. package/dist/src/sync/external-issue-auto-creator.js +75 -19
  112. package/dist/src/sync/external-issue-auto-creator.js.map +1 -1
  113. package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
  114. package/dist/src/sync/sync-coordinator.js +20 -4
  115. package/dist/src/sync/sync-coordinator.js.map +1 -1
  116. package/dist/src/sync/sync-target-resolver.d.ts +36 -0
  117. package/dist/src/sync/sync-target-resolver.d.ts.map +1 -0
  118. package/dist/src/sync/sync-target-resolver.js +72 -0
  119. package/dist/src/sync/sync-target-resolver.js.map +1 -0
  120. package/dist/src/utils/external-tool-drift-detector.js +4 -4
  121. package/dist/src/utils/external-tool-drift-detector.js.map +1 -1
  122. package/dist/src/utils/find-project-root.d.ts +26 -0
  123. package/dist/src/utils/find-project-root.d.ts.map +1 -1
  124. package/dist/src/utils/find-project-root.js +78 -0
  125. package/dist/src/utils/find-project-root.js.map +1 -1
  126. package/dist/src/utils/multi-project-detector.js +1 -1
  127. package/dist/src/utils/multi-project-detector.js.map +1 -1
  128. package/dist/src/utils/plugin-copier.d.ts +28 -47
  129. package/dist/src/utils/plugin-copier.d.ts.map +1 -1
  130. package/dist/src/utils/plugin-copier.js +85 -155
  131. package/dist/src/utils/plugin-copier.js.map +1 -1
  132. package/package.json +1 -1
  133. package/plugins/specweave/PLUGIN.md +1 -0
  134. package/plugins/specweave/hooks/stop-auto-v5.sh +26 -17
  135. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +5 -6
  136. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +12 -13
  137. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
  138. package/plugins/specweave/lib/vendor/core/increment/status-auto-transition.js +5 -4
  139. package/plugins/specweave/lib/vendor/core/increment/status-auto-transition.js.map +1 -1
  140. package/plugins/specweave/skills/auto/SKILL.md +15 -1
  141. package/plugins/specweave/skills/brainstorm/SKILL.md +619 -0
  142. package/plugins/specweave/skills/do/SKILL.md +3 -3
  143. package/plugins/specweave/skills/increment/SKILL.md +3 -3
  144. package/plugins/specweave/skills/team-build/SKILL.md +1 -1
  145. package/plugins/specweave/skills/team-lead/SKILL.md +7 -5
  146. package/plugins/specweave/skills/validate/SKILL.md +1 -1
  147. package/plugins/specweave-ado/lib/ado-multi-project-sync.js +1 -0
  148. package/plugins/specweave-docs/PLUGIN.md +1 -1
  149. package/plugins/specweave-github/lib/duplicate-detector.js +21 -1
  150. package/plugins/specweave-github/lib/duplicate-detector.ts +18 -2
  151. package/plugins/specweave-github/lib/github-feature-sync.js +17 -0
  152. package/plugins/specweave-github/lib/github-feature-sync.ts +11 -0
  153. package/plugins/specweave-jira/lib/jira-status-sync.js +75 -0
  154. package/plugins/specweave-jira/lib/jira-status-sync.ts +91 -1
  155. package/src/templates/CLAUDE.md.template +3 -1
  156. package/dist/src/utils/claude-plugin-cli.d.ts +0 -32
  157. package/dist/src/utils/claude-plugin-cli.d.ts.map +0 -1
  158. package/dist/src/utils/claude-plugin-cli.js +0 -74
  159. package/dist/src/utils/claude-plugin-cli.js.map +0 -1
  160. package/plugins/specweave/hooks/.specweave/logs/auto-iterations.log +0 -1
  161. package/plugins/specweave/hooks/.specweave/logs/auto-stop-reasons.log +0 -1
  162. package/plugins/specweave/skills/.specweave/logs/reflect/auto-reflect.log +0 -15
  163. package/plugins/specweave/skills/.specweave/logs/reflect/reflect.log +0 -3
  164. package/plugins/specweave/skills/.specweave/logs/stop-auto.log +0 -1
  165. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +0 -180
  166. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +0 -1266
  167. package/plugins/specweave-github/lib/enhanced-github-sync.js +0 -249
  168. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +0 -150
  169. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +0 -1260
@@ -5,7 +5,7 @@ hooks:
5
5
  - matcher: TeamCreate
6
6
  hooks:
7
7
  - type: command
8
- command: bash plugins/specweave/hooks/v2/guards/increment-existence-guard.sh
8
+ command: bash -c 'W="${CLAUDE_PLUGIN_ROOT}/hooks/universal/fail-fast-wrapper.sh"; S="${CLAUDE_PLUGIN_ROOT}/hooks/v2/guards/increment-existence-guard.sh"; [[ -x "$W" ]] && exec "$W" "$S" || (cat >/dev/null && printf "{\"decision\":\"allow\"}")'
9
9
  ---
10
10
 
11
11
  # Team Lead
@@ -439,7 +439,7 @@ SendMessage({
439
439
  SendMessage({
440
440
  type: "message",
441
441
  recipient: "team-lead",
442
- content: "COMPLETION: All 8 tasks done. Tests passing (24/24). /sw:grill passed. Frontend increment ready for merge.",
442
+ content: "COMPLETION: All 8 tasks done. Tests passing (24/24). Ready for team-lead closure.",
443
443
  summary: "Frontend agent completed all tasks"
444
444
  });
445
445
  ```
@@ -563,12 +563,14 @@ Orchestrator Final Check:
563
563
 
564
564
  Agents can get stuck in extended thinking if their context overflows. The team-lead MUST monitor for stuck agents.
565
565
 
566
- ### Timeout Rules
566
+ ### Stuck Detection Rules
567
+
568
+ **Note**: Claude Code has no built-in timers. These are best-effort heuristics applied when the team-lead regains control (e.g., after processing other agent messages).
567
569
 
568
570
  | Condition | Action |
569
571
  |-----------|--------|
570
- | Agent idle >20 min after last message | Send `STATUS_CHECK` message to agent |
571
- | No response to STATUS_CHECK within 5 min | Declare agent stuck |
572
+ | Agent has not messaged since team-lead's last turn | Send `STATUS_CHECK` message to agent |
573
+ | Agent does not respond to STATUS_CHECK on next team-lead turn | Declare agent stuck |
572
574
  | Agent stuck | Log warning, proceed with other agents, handle stuck agent's increment manually in team-merge |
573
575
  | All agents stuck | STOP team, report to user |
574
576
 
@@ -5,7 +5,7 @@ hooks:
5
5
  Stop:
6
6
  - hooks:
7
7
  - type: command
8
- command: bash plugins/specweave/hooks/v2/guards/spec-validation-guard.sh
8
+ command: bash -c 'W="${CLAUDE_PLUGIN_ROOT}/hooks/universal/fail-fast-wrapper.sh"; S="${CLAUDE_PLUGIN_ROOT}/hooks/v2/guards/spec-validation-guard.sh"; [[ -x "$W" ]] && exec "$W" "$S" || (cat >/dev/null && printf "{\"decision\":\"approve\"}")'
9
9
  ---
10
10
 
11
11
  # Validate Increment
@@ -373,6 +373,7 @@ ${userStory.technicalContext}
373
373
  return mapping.task || "Task";
374
374
  case "Subtask":
375
375
  return mapping.task || "Task";
376
+ // ADO doesn't have subtasks, use Task
376
377
  default:
377
378
  return "User Story";
378
379
  }
@@ -14,7 +14,7 @@ Complete documentation ecosystem for technical writers, product teams, and devel
14
14
  |-------|-------------|
15
15
  | technical-writing | Expert in API documentation, README files, tutorials, changelog management, and developer documentation with style guides and OpenAPI/Swagger support |
16
16
  | docusaurus | Docusaurus 3.x framework expert for MDX authoring, theming, versioning, and internationalization |
17
- | spec-driven-brainstorming | Product discovery expert for feature ideation, story mapping, MoSCoW/RICE prioritization, and MVP definition |
17
+ | spec-driven-brainstorming | DEPRECATED: Use `/sw:brainstorm` (core plugin). Multi-perspective ideation with cognitive lenses |
18
18
 
19
19
  ## Installation
20
20
 
@@ -272,6 +272,26 @@ The original issue (#${keepIssueNumber}) should be used for tracking instead.
272
272
  \u2501\u2501\u2501 PHASE 2: CREATION \u2501\u2501\u2501`);
273
273
  console.log(` Creating new GitHub issue...`);
274
274
  try {
275
+ const safeLabels = labels.filter((l) => /^[a-zA-Z0-9:_\- ]+$/.test(l));
276
+ if (repo) {
277
+ for (const label of safeLabels) {
278
+ try {
279
+ execFileSync("gh", [
280
+ "label",
281
+ "create",
282
+ label,
283
+ "--repo",
284
+ repo,
285
+ "--color",
286
+ "ededed",
287
+ "--description",
288
+ "SpecWeave auto-label",
289
+ "--force"
290
+ ], { encoding: "utf-8", env: getGhEnv() });
291
+ } catch {
292
+ }
293
+ }
294
+ }
275
295
  const args = [
276
296
  "issue",
277
297
  "create",
@@ -283,7 +303,7 @@ The original issue (#${keepIssueNumber}) should be used for tracking instead.
283
303
  if (repo) {
284
304
  args.push("--repo", repo);
285
305
  }
286
- labels.forEach((label) => {
306
+ safeLabels.forEach((label) => {
287
307
  args.push("--label", label);
288
308
  });
289
309
  if (milestone) {
@@ -419,6 +419,22 @@ The original issue (#${keepIssueNumber}) should be used for tracking instead.
419
419
  console.log(` Creating new GitHub issue...`);
420
420
 
421
421
  try {
422
+ // Ensure all labels exist in target repo before creating issue
423
+ // Sanitize labels: only allow alphanumeric, hyphens, colons, spaces
424
+ const safeLabels = labels.filter(l => /^[a-zA-Z0-9:_\- ]+$/.test(l));
425
+ if (repo) {
426
+ for (const label of safeLabels) {
427
+ try {
428
+ execFileSync('gh', [
429
+ 'label', 'create', label, '--repo', repo,
430
+ '--color', 'ededed', '--description', 'SpecWeave auto-label', '--force'
431
+ ], { encoding: 'utf-8', env: getGhEnv() });
432
+ } catch {
433
+ // Label creation failure is non-fatal
434
+ }
435
+ }
436
+ }
437
+
422
438
  const args = [
423
439
  'issue',
424
440
  'create',
@@ -430,8 +446,8 @@ The original issue (#${keepIssueNumber}) should be used for tracking instead.
430
446
  args.push('--repo', repo);
431
447
  }
432
448
 
433
- // Add labels
434
- labels.forEach(label => {
449
+ // Add sanitized labels
450
+ safeLabels.forEach(label => {
435
451
  args.push('--label', label);
436
452
  });
437
453
 
@@ -622,9 +622,26 @@ Created: ${featureData.created}`;
622
622
  * - Prevents premature closure on creation
623
623
  */
624
624
  async createUserStoryIssue(issueContent, milestoneTitle, userStoryPath) {
625
+ const repoSlug = `${this.client.getOwner()}/${this.client.getRepo()}`;
626
+ for (const label of issueContent.labels) {
627
+ await execFileNoThrow("gh", [
628
+ "label",
629
+ "create",
630
+ label,
631
+ "--repo",
632
+ repoSlug,
633
+ "--color",
634
+ "ededed",
635
+ "--description",
636
+ "SpecWeave auto-label",
637
+ "--force"
638
+ ], { env: this.getGhEnv() });
639
+ }
625
640
  const result = await execFileNoThrow("gh", [
626
641
  "issue",
627
642
  "create",
643
+ "--repo",
644
+ repoSlug,
628
645
  "--title",
629
646
  issueContent.title,
630
647
  "--body",
@@ -907,10 +907,21 @@ export class GitHubFeatureSync {
907
907
  milestoneTitle: string,
908
908
  userStoryPath: string
909
909
  ): Promise<number> {
910
+ // Step 0: Ensure all required labels exist in the target repo
911
+ const repoSlug = `${this.client.getOwner()}/${this.client.getRepo()}`;
912
+ for (const label of issueContent.labels) {
913
+ await execFileNoThrow('gh', [
914
+ 'label', 'create', label, '--repo', repoSlug,
915
+ '--color', 'ededed', '--description', 'SpecWeave auto-label', '--force'
916
+ ], { env: this.getGhEnv() });
917
+ }
918
+
910
919
  // Step 1: Create issue (always open initially - gh CLI limitation)
911
920
  const result = await execFileNoThrow('gh', [
912
921
  'issue',
913
922
  'create',
923
+ '--repo',
924
+ repoSlug,
914
925
  '--title',
915
926
  issueContent.title,
916
927
  '--body',
@@ -92,6 +92,81 @@ _Synced from SpecWeave_`;
92
92
  body
93
93
  });
94
94
  }
95
+ /**
96
+ * Post AC progress comment with proper ADF formatting and dedup.
97
+ *
98
+ * Builds native ADF with:
99
+ * - Bold header showing completion percentage
100
+ * - Bullet list with checkmark/cross emojis per AC
101
+ * - Fingerprint marker to prevent duplicate comments
102
+ *
103
+ * @param issueKey - JIRA issue key (e.g., PROJ-123)
104
+ * @param acStates - Array of AC states with id, description, completed
105
+ * @returns true if comment was posted, false if skipped (duplicate)
106
+ */
107
+ async postProgressComment(issueKey, acStates) {
108
+ const total = acStates.length;
109
+ const completed = acStates.filter((ac) => ac.completed).length;
110
+ const percentage = Math.round(completed / total * 100);
111
+ const fingerprint = `sw-progress:${completed}/${total}`;
112
+ try {
113
+ const commentsResp = await this.client.get(`/issue/${issueKey}/comment`, {
114
+ params: { orderBy: "-created", maxResults: 1 }
115
+ });
116
+ const lastComment = commentsResp.data?.comments?.[0];
117
+ if (lastComment) {
118
+ const lastText = extractAdfText(lastComment.body);
119
+ if (lastText.includes(fingerprint)) {
120
+ return false;
121
+ }
122
+ }
123
+ } catch {
124
+ }
125
+ const listItems = acStates.map((ac) => ({
126
+ type: "listItem",
127
+ content: [{
128
+ type: "paragraph",
129
+ content: [
130
+ { type: "text", text: `${ac.completed ? "\u2705" : "\u274C"} ${ac.id}: ${ac.description}` }
131
+ ]
132
+ }]
133
+ }));
134
+ const body = {
135
+ type: "doc",
136
+ version: 1,
137
+ content: [
138
+ {
139
+ type: "heading",
140
+ attrs: { level: 3 },
141
+ content: [{ type: "text", text: `Progress: ${completed}/${total} ACs (${percentage}%)` }]
142
+ },
143
+ {
144
+ type: "bulletList",
145
+ content: listItems
146
+ },
147
+ {
148
+ type: "paragraph",
149
+ content: [
150
+ { type: "text", text: `${fingerprint} | Synced from SpecWeave`, marks: [{ type: "em" }] }
151
+ ]
152
+ }
153
+ ]
154
+ };
155
+ await this.client.post(`/issue/${issueKey}/comment`, { body });
156
+ return true;
157
+ }
158
+ }
159
+ function extractAdfText(adf) {
160
+ if (!adf) return "";
161
+ if (typeof adf === "string") return adf;
162
+ let text = "";
163
+ if (adf.text) text += adf.text;
164
+ if (Array.isArray(adf.content)) {
165
+ for (const child of adf.content) {
166
+ text += extractAdfText(child);
167
+ }
168
+ }
169
+ return text;
95
170
  }
96
171
  export {
97
172
  JiraStatusSync
@@ -13,7 +13,7 @@
13
13
 
14
14
  import axios, { AxiosInstance } from 'axios';
15
15
  import { detectDeploymentType, getApiBaseUrl } from './jira-deployment-detector.js';
16
- import { toCommentBody } from './content-format-adapter.js';
16
+ import { toCommentBody, type AdfDocument, type AdfNode } from './content-format-adapter.js';
17
17
 
18
18
  /**
19
19
  * External status representation (JIRA-specific)
@@ -160,4 +160,94 @@ export class JiraStatusSync {
160
160
  body
161
161
  });
162
162
  }
163
+
164
+ /**
165
+ * Post AC progress comment with proper ADF formatting and dedup.
166
+ *
167
+ * Builds native ADF with:
168
+ * - Bold header showing completion percentage
169
+ * - Bullet list with checkmark/cross emojis per AC
170
+ * - Fingerprint marker to prevent duplicate comments
171
+ *
172
+ * @param issueKey - JIRA issue key (e.g., PROJ-123)
173
+ * @param acStates - Array of AC states with id, description, completed
174
+ * @returns true if comment was posted, false if skipped (duplicate)
175
+ */
176
+ async postProgressComment(
177
+ issueKey: string,
178
+ acStates: Array<{ id: string; description: string; completed: boolean }>,
179
+ ): Promise<boolean> {
180
+ const total = acStates.length;
181
+ const completed = acStates.filter(ac => ac.completed).length;
182
+ const percentage = Math.round((completed / total) * 100);
183
+ const fingerprint = `sw-progress:${completed}/${total}`;
184
+
185
+ // Dedup: check last comment for same fingerprint
186
+ try {
187
+ const commentsResp = await this.client.get(`/issue/${issueKey}/comment`, {
188
+ params: { orderBy: '-created', maxResults: 1 },
189
+ });
190
+ const lastComment = commentsResp.data?.comments?.[0];
191
+ if (lastComment) {
192
+ const lastText = extractAdfText(lastComment.body);
193
+ if (lastText.includes(fingerprint)) {
194
+ return false; // Already posted for this state
195
+ }
196
+ }
197
+ } catch {
198
+ // If comment fetch fails, proceed with posting
199
+ }
200
+
201
+ // Build ADF body directly — ✅ for done, ❌ for pending
202
+ const listItems: AdfNode[] = acStates.map(ac => ({
203
+ type: 'listItem',
204
+ content: [{
205
+ type: 'paragraph',
206
+ content: [
207
+ { type: 'text', text: `${ac.completed ? '\u2705' : '\u274C'} ${ac.id}: ${ac.description}` },
208
+ ],
209
+ }],
210
+ }));
211
+
212
+ const body: AdfDocument = {
213
+ type: 'doc',
214
+ version: 1,
215
+ content: [
216
+ {
217
+ type: 'heading',
218
+ attrs: { level: 3 },
219
+ content: [{ type: 'text', text: `Progress: ${completed}/${total} ACs (${percentage}%)` }],
220
+ },
221
+ {
222
+ type: 'bulletList',
223
+ content: listItems,
224
+ },
225
+ {
226
+ type: 'paragraph',
227
+ content: [
228
+ { type: 'text', text: `${fingerprint} | Synced from SpecWeave`, marks: [{ type: 'em' }] },
229
+ ],
230
+ },
231
+ ],
232
+ };
233
+
234
+ await this.client.post(`/issue/${issueKey}/comment`, { body });
235
+ return true;
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Extract plain text from an ADF document (for dedup comparison).
241
+ */
242
+ function extractAdfText(adf: any): string {
243
+ if (!adf) return '';
244
+ if (typeof adf === 'string') return adf;
245
+ let text = '';
246
+ if (adf.text) text += adf.text;
247
+ if (Array.isArray(adf.content)) {
248
+ for (const child of adf.content) {
249
+ text += extractAdfText(child);
250
+ }
251
+ }
252
+ return text;
163
253
  }
@@ -64,7 +64,9 @@ SpecWeave auto-detects product descriptions and routes to `/sw:increment`:
64
64
 
65
65
  **Signals** (5+ = auto-route): Project name | Features list (3+) | Tech stack | Timeline/MVP | Problem statement | Business model
66
66
 
67
- **Opt-out phrases**: "Just brainstorm first" | "Don't plan yet" | "Quick discussion" | "Let's explore ideas"
67
+ **Opt-out phrases**: "Don't plan yet" | "Quick discussion" | "Let's explore ideas"
68
+
69
+ **Brainstorm routing**: "Just brainstorm first" | "brainstorm" | "ideate" | "what are our options" → routes to `/sw:brainstorm`
68
70
 
69
71
  **NOT opt-out phrases**: "simple" | "quick" | "basic" | "small" — these still require `/sw:increment`
70
72
 
@@ -1,32 +0,0 @@
1
- /**
2
- * Claude Code Plugin CLI Integration
3
- *
4
- * Registers SpecWeave plugins with Claude Code's plugin system via the
5
- * `claude` CLI binary. This makes plugins appear in the `/plugin Installed`
6
- * UI alongside marketplace metadata.
7
- *
8
- * All operations are non-fatal — failures are logged (in DEBUG mode) but
9
- * never block the primary plugin installation to ~/.claude/commands/.
10
- *
11
- * @since 1.0.290
12
- */
13
- export interface CliRegistrationResult {
14
- /** True if marketplace was registered successfully */
15
- marketplaceRegistered: boolean;
16
- /** Plugin names that were successfully installed via CLI */
17
- installedPlugins: string[];
18
- /** Plugin names that failed CLI installation */
19
- failedPlugins: string[];
20
- }
21
- /**
22
- * Register SpecWeave marketplace and install plugins via Claude CLI.
23
- *
24
- * Steps:
25
- * 1. Validates specweaveRoot contains .claude-plugin/marketplace.json
26
- * 2. Runs `claude plugin marketplace add <specweaveRoot>`
27
- * 3. Runs `claude plugin install <name>@specweave` for each plugin
28
- *
29
- * Entirely non-fatal. Returns a result with success/failure per plugin.
30
- */
31
- export declare function registerPluginsWithClaudeCli(specweaveRoot: string, pluginNames: string[]): CliRegistrationResult;
32
- //# sourceMappingURL=claude-plugin-cli.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"claude-plugin-cli.d.ts","sourceRoot":"","sources":["../../../src/utils/claude-plugin-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,MAAM,WAAW,qBAAqB;IACpC,sDAAsD;IACtD,qBAAqB,EAAE,OAAO,CAAC;IAC/B,4DAA4D;IAC5D,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gDAAgD;IAChD,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAMD;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EAAE,GACpB,qBAAqB,CA6CvB"}
@@ -1,74 +0,0 @@
1
- /**
2
- * Claude Code Plugin CLI Integration
3
- *
4
- * Registers SpecWeave plugins with Claude Code's plugin system via the
5
- * `claude` CLI binary. This makes plugins appear in the `/plugin Installed`
6
- * UI alongside marketplace metadata.
7
- *
8
- * All operations are non-fatal — failures are logged (in DEBUG mode) but
9
- * never block the primary plugin installation to ~/.claude/commands/.
10
- *
11
- * @since 1.0.290
12
- */
13
- import { existsSync } from 'node:fs';
14
- import { join } from 'node:path';
15
- import { execFileNoThrowSync } from './execFileNoThrow.js';
16
- import { getPluginScope, getScopeArgs } from '../core/types/plugin-scope.js';
17
- // ---------------------------------------------------------------------------
18
- // Core
19
- // ---------------------------------------------------------------------------
20
- /**
21
- * Register SpecWeave marketplace and install plugins via Claude CLI.
22
- *
23
- * Steps:
24
- * 1. Validates specweaveRoot contains .claude-plugin/marketplace.json
25
- * 2. Runs `claude plugin marketplace add <specweaveRoot>`
26
- * 3. Runs `claude plugin install <name>@specweave` for each plugin
27
- *
28
- * Entirely non-fatal. Returns a result with success/failure per plugin.
29
- */
30
- export function registerPluginsWithClaudeCli(specweaveRoot, pluginNames) {
31
- const result = {
32
- marketplaceRegistered: false,
33
- installedPlugins: [],
34
- failedPlugins: [],
35
- };
36
- if (pluginNames.length === 0)
37
- return result;
38
- // Validate marketplace exists at path
39
- const marketplacePath = join(specweaveRoot, '.claude-plugin', 'marketplace.json');
40
- if (!existsSync(marketplacePath)) {
41
- if (process.env.DEBUG) {
42
- console.warn(`[DEBUG] Skipping Claude CLI registration: no marketplace.json at ${marketplacePath}`);
43
- }
44
- return result;
45
- }
46
- // Register marketplace
47
- const addResult = execFileNoThrowSync('claude', ['plugin', 'marketplace', 'add', specweaveRoot]);
48
- if (addResult.success || addResult.exitCode === 0) {
49
- result.marketplaceRegistered = true;
50
- }
51
- else {
52
- if (process.env.DEBUG) {
53
- console.warn(`[DEBUG] claude plugin marketplace add failed (exit ${addResult.exitCode}): ${addResult.stderr}`);
54
- }
55
- // Don't bail — install may still work if marketplace was already registered
56
- }
57
- // Install each plugin with correct scope
58
- for (const name of pluginNames) {
59
- const scope = getPluginScope(name, 'specweave');
60
- const scopeArgs = getScopeArgs(scope);
61
- const installResult = execFileNoThrowSync('claude', ['plugin', 'install', `${name}@specweave`, ...scopeArgs]);
62
- if (installResult.success || installResult.exitCode === 0) {
63
- result.installedPlugins.push(name);
64
- }
65
- else {
66
- result.failedPlugins.push(name);
67
- if (process.env.DEBUG) {
68
- console.warn(`[DEBUG] claude plugin install ${name}@specweave failed (exit ${installResult.exitCode}): ${installResult.stderr}`);
69
- }
70
- }
71
- }
72
- return result;
73
- }
74
- //# sourceMappingURL=claude-plugin-cli.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"claude-plugin-cli.js","sourceRoot":"","sources":["../../../src/utils/claude-plugin-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAe7E,8EAA8E;AAC9E,OAAO;AACP,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAC1C,aAAqB,EACrB,WAAqB;IAErB,MAAM,MAAM,GAA0B;QACpC,qBAAqB,EAAE,KAAK;QAC5B,gBAAgB,EAAE,EAAE;QACpB,aAAa,EAAE,EAAE;KAClB,CAAC;IAEF,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IAE5C,sCAAsC;IACtC,MAAM,eAAe,GAAG,IAAI,CAAC,aAAa,EAAE,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;IAClF,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QACjC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,oEAAoE,eAAe,EAAE,CAAC,CAAC;QACtG,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,uBAAuB;IACvB,MAAM,SAAS,GAAG,mBAAmB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC;IACjG,IAAI,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAClD,MAAM,CAAC,qBAAqB,GAAG,IAAI,CAAC;IACtC,CAAC;SAAM,CAAC;QACN,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,sDAAsD,SAAS,CAAC,QAAQ,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;QACjH,CAAC;QACD,4EAA4E;IAC9E,CAAC;IAED,yCAAyC;IACzC,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QAChD,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,aAAa,GAAG,mBAAmB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,YAAY,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC;QAC9G,IAAI,aAAa,CAAC,OAAO,IAAI,aAAa,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC1D,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;gBACtB,OAAO,CAAC,IAAI,CAAC,iCAAiC,IAAI,2BAA2B,aAAa,CAAC,QAAQ,MAAM,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC;YACnI,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1 +0,0 @@
1
- {"timestamp":"2026-01-04T16:11:43Z","event":"session_stop","reason":"No auto session active","details":"approve_called:main","success":false,"iteration":0,"increment":"none"}
@@ -1 +0,0 @@
1
- {"timestamp":"2026-01-04T16:11:43Z","sessionId":"unknown","reason":"No auto session active","details":"approve_called:main","success":false,"iteration":0,"increment":"none","testsRun":false,"testsPassed":0,"testsFailed":0}
@@ -1,15 +0,0 @@
1
- {
2
- "ran": false,
3
- "inputSummary": {
4
- "transcriptLines": 266
5
- },
6
- "extracted": {
7
- "skillLearnings": []
8
- },
9
- "written": {
10
- "learningsAdded": 0,
11
- "learningsSkippedDuplicate": 0
12
- },
13
- "durationMs": 1.4448749999999997,
14
- "reason": "CLAUDE.md not found in project"
15
- }
@@ -1,3 +0,0 @@
1
- [2026-02-03T22:03:06Z] [info] Starting reflection (265 lines)
2
- [2026-02-03T22:03:06Z] [info] Reflection started in background
3
- [2026-02-03T22:03:06Z] [info] Reflection completed - no learnings
@@ -1 +0,0 @@
1
- [2026-02-03T22:03:06Z] APPROVE: No increments directory