specweave 0.32.2 → 0.32.5

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 (244) hide show
  1. package/CLAUDE.md +51 -9
  2. package/bin/specweave.js +34 -0
  3. package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.d.ts +100 -0
  4. package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.d.ts.map +1 -0
  5. package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.js +291 -0
  6. package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.js.map +1 -0
  7. package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.d.ts +103 -0
  8. package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.d.ts.map +1 -0
  9. package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.js +310 -0
  10. package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.js.map +1 -0
  11. package/dist/plugins/specweave-jira/lib/jira-permission-gate.d.ts +126 -0
  12. package/dist/plugins/specweave-jira/lib/jira-permission-gate.d.ts.map +1 -0
  13. package/dist/plugins/specweave-jira/lib/jira-permission-gate.js +207 -0
  14. package/dist/plugins/specweave-jira/lib/jira-permission-gate.js.map +1 -0
  15. package/dist/src/adapters/codex/README.md +1 -1
  16. package/dist/src/adapters/codex/adapter.js +1 -1
  17. package/dist/src/cli/commands/archive.d.ts +2 -0
  18. package/dist/src/cli/commands/archive.d.ts.map +1 -1
  19. package/dist/src/cli/commands/archive.js +33 -0
  20. package/dist/src/cli/commands/archive.js.map +1 -1
  21. package/dist/src/cli/commands/context.d.ts +92 -0
  22. package/dist/src/cli/commands/context.d.ts.map +1 -0
  23. package/dist/src/cli/commands/context.js +205 -0
  24. package/dist/src/cli/commands/context.js.map +1 -0
  25. package/dist/src/cli/commands/init-multiproject.js +1 -1
  26. package/dist/src/cli/commands/init-multiproject.js.map +1 -1
  27. package/dist/src/cli/commands/init.d.ts.map +1 -1
  28. package/dist/src/cli/commands/init.js +111 -69
  29. package/dist/src/cli/commands/init.js.map +1 -1
  30. package/dist/src/cli/commands/migrate-to-multiproject.js +2 -2
  31. package/dist/src/cli/commands/migrate-to-multiproject.js.map +1 -1
  32. package/dist/src/cli/helpers/init/external-import.d.ts +3 -0
  33. package/dist/src/cli/helpers/init/external-import.d.ts.map +1 -1
  34. package/dist/src/cli/helpers/init/external-import.js +17 -4
  35. package/dist/src/cli/helpers/init/external-import.js.map +1 -1
  36. package/dist/src/cli/helpers/init/index.d.ts +1 -0
  37. package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
  38. package/dist/src/cli/helpers/init/index.js +2 -0
  39. package/dist/src/cli/helpers/init/index.js.map +1 -1
  40. package/dist/src/cli/helpers/init/jira-ado-auto-detect.d.ts +70 -0
  41. package/dist/src/cli/helpers/init/jira-ado-auto-detect.d.ts.map +1 -1
  42. package/dist/src/cli/helpers/init/jira-ado-auto-detect.js +214 -4
  43. package/dist/src/cli/helpers/init/jira-ado-auto-detect.js.map +1 -1
  44. package/dist/src/cli/helpers/init/living-docs-preflight.d.ts +4 -0
  45. package/dist/src/cli/helpers/init/living-docs-preflight.d.ts.map +1 -1
  46. package/dist/src/cli/helpers/init/living-docs-preflight.js +34 -3
  47. package/dist/src/cli/helpers/init/living-docs-preflight.js.map +1 -1
  48. package/dist/src/cli/helpers/init/testing-config.d.ts +3 -0
  49. package/dist/src/cli/helpers/init/testing-config.d.ts.map +1 -1
  50. package/dist/src/cli/helpers/init/testing-config.js +9 -2
  51. package/dist/src/cli/helpers/init/testing-config.js.map +1 -1
  52. package/dist/src/cli/helpers/init/translation-config.d.ts +3 -0
  53. package/dist/src/cli/helpers/init/translation-config.d.ts.map +1 -1
  54. package/dist/src/cli/helpers/init/translation-config.js +21 -4
  55. package/dist/src/cli/helpers/init/translation-config.js.map +1 -1
  56. package/dist/src/cli/helpers/init/wizard-navigation.d.ts +45 -0
  57. package/dist/src/cli/helpers/init/wizard-navigation.d.ts.map +1 -0
  58. package/dist/src/cli/helpers/init/wizard-navigation.js +97 -0
  59. package/dist/src/cli/helpers/init/wizard-navigation.js.map +1 -0
  60. package/dist/src/core/increment/increment-archiver.d.ts +25 -4
  61. package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
  62. package/dist/src/core/increment/increment-archiver.js +64 -20
  63. package/dist/src/core/increment/increment-archiver.js.map +1 -1
  64. package/dist/src/core/increment/increment-utils.d.ts +65 -0
  65. package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
  66. package/dist/src/core/increment/increment-utils.js +114 -0
  67. package/dist/src/core/increment/increment-utils.js.map +1 -1
  68. package/dist/src/core/living-docs/cross-project-sync.d.ts +97 -0
  69. package/dist/src/core/living-docs/cross-project-sync.d.ts.map +1 -0
  70. package/dist/src/core/living-docs/cross-project-sync.js +135 -0
  71. package/dist/src/core/living-docs/cross-project-sync.js.map +1 -0
  72. package/dist/src/core/living-docs/external-sync-orchestrator.d.ts +106 -0
  73. package/dist/src/core/living-docs/external-sync-orchestrator.d.ts.map +1 -0
  74. package/dist/src/core/living-docs/external-sync-orchestrator.js +146 -0
  75. package/dist/src/core/living-docs/external-sync-orchestrator.js.map +1 -0
  76. package/dist/src/core/living-docs/feature-archiver.d.ts +4 -0
  77. package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -1
  78. package/dist/src/core/living-docs/feature-archiver.js +32 -10
  79. package/dist/src/core/living-docs/feature-archiver.js.map +1 -1
  80. package/dist/src/core/living-docs/feature-id-manager.d.ts.map +1 -1
  81. package/dist/src/core/living-docs/feature-id-manager.js +7 -3
  82. package/dist/src/core/living-docs/feature-id-manager.js.map +1 -1
  83. package/dist/src/core/living-docs/governance/ecosystem-detector.d.ts +38 -0
  84. package/dist/src/core/living-docs/governance/ecosystem-detector.d.ts.map +1 -0
  85. package/dist/src/core/living-docs/governance/ecosystem-detector.js +325 -0
  86. package/dist/src/core/living-docs/governance/ecosystem-detector.js.map +1 -0
  87. package/dist/src/core/living-docs/governance/frontend-standards-parser.d.ts +74 -0
  88. package/dist/src/core/living-docs/governance/frontend-standards-parser.d.ts.map +1 -0
  89. package/dist/src/core/living-docs/governance/frontend-standards-parser.js +366 -0
  90. package/dist/src/core/living-docs/governance/frontend-standards-parser.js.map +1 -0
  91. package/dist/src/core/living-docs/governance/go-standards-parser.d.ts +64 -0
  92. package/dist/src/core/living-docs/governance/go-standards-parser.d.ts.map +1 -0
  93. package/dist/src/core/living-docs/governance/go-standards-parser.js +229 -0
  94. package/dist/src/core/living-docs/governance/go-standards-parser.js.map +1 -0
  95. package/dist/src/core/living-docs/governance/index.d.ts +50 -0
  96. package/dist/src/core/living-docs/governance/index.d.ts.map +1 -0
  97. package/dist/src/core/living-docs/governance/index.js +56 -0
  98. package/dist/src/core/living-docs/governance/index.js.map +1 -0
  99. package/dist/src/core/living-docs/governance/java-standards-parser.d.ts +89 -0
  100. package/dist/src/core/living-docs/governance/java-standards-parser.d.ts.map +1 -0
  101. package/dist/src/core/living-docs/governance/java-standards-parser.js +356 -0
  102. package/dist/src/core/living-docs/governance/java-standards-parser.js.map +1 -0
  103. package/dist/src/core/living-docs/governance/python-standards-parser.d.ts +83 -0
  104. package/dist/src/core/living-docs/governance/python-standards-parser.d.ts.map +1 -0
  105. package/dist/src/core/living-docs/governance/python-standards-parser.js +347 -0
  106. package/dist/src/core/living-docs/governance/python-standards-parser.js.map +1 -0
  107. package/dist/src/core/living-docs/governance/standards-generator.d.ts +38 -0
  108. package/dist/src/core/living-docs/governance/standards-generator.d.ts.map +1 -0
  109. package/dist/src/core/living-docs/governance/standards-generator.js +476 -0
  110. package/dist/src/core/living-docs/governance/standards-generator.js.map +1 -0
  111. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -1
  112. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +54 -2
  113. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -1
  114. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts +5 -1
  115. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -1
  116. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +358 -30
  117. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -1
  118. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +44 -0
  119. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -1
  120. package/dist/src/core/living-docs/living-docs-sync.d.ts +7 -3
  121. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  122. package/dist/src/core/living-docs/living-docs-sync.js +94 -10
  123. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  124. package/dist/src/core/living-docs/module-analyzer.d.ts +22 -0
  125. package/dist/src/core/living-docs/module-analyzer.d.ts.map +1 -1
  126. package/dist/src/core/living-docs/module-analyzer.js +123 -19
  127. package/dist/src/core/living-docs/module-analyzer.js.map +1 -1
  128. package/dist/src/core/living-docs/sync-helpers/generators.d.ts +8 -1
  129. package/dist/src/core/living-docs/sync-helpers/generators.d.ts.map +1 -1
  130. package/dist/src/core/living-docs/sync-helpers/generators.js +18 -1
  131. package/dist/src/core/living-docs/sync-helpers/generators.js.map +1 -1
  132. package/dist/src/core/living-docs/sync-helpers/index.d.ts +1 -1
  133. package/dist/src/core/living-docs/sync-helpers/index.d.ts.map +1 -1
  134. package/dist/src/core/living-docs/sync-helpers/index.js.map +1 -1
  135. package/dist/src/core/living-docs/sync-helpers/parsers.d.ts +3 -1
  136. package/dist/src/core/living-docs/sync-helpers/parsers.d.ts.map +1 -1
  137. package/dist/src/core/living-docs/sync-helpers/parsers.js +24 -2
  138. package/dist/src/core/living-docs/sync-helpers/parsers.js.map +1 -1
  139. package/dist/src/core/living-docs/types.d.ts +6 -0
  140. package/dist/src/core/living-docs/types.d.ts.map +1 -1
  141. package/dist/src/core/living-docs/validators/index.d.ts +7 -0
  142. package/dist/src/core/living-docs/validators/index.d.ts.map +1 -0
  143. package/dist/src/core/living-docs/validators/index.js +7 -0
  144. package/dist/src/core/living-docs/validators/index.js.map +1 -0
  145. package/dist/src/core/living-docs/validators/project-validator.d.ts +92 -0
  146. package/dist/src/core/living-docs/validators/project-validator.d.ts.map +1 -0
  147. package/dist/src/core/living-docs/validators/project-validator.js +142 -0
  148. package/dist/src/core/living-docs/validators/project-validator.js.map +1 -0
  149. package/dist/src/core/llm/provider-factory.js +2 -2
  150. package/dist/src/core/llm/provider-factory.js.map +1 -1
  151. package/dist/src/core/llm/providers/anthropic-provider.js +1 -1
  152. package/dist/src/core/llm/providers/bedrock-provider.d.ts.map +1 -1
  153. package/dist/src/core/llm/providers/bedrock-provider.js +8 -4
  154. package/dist/src/core/llm/providers/bedrock-provider.js.map +1 -1
  155. package/dist/src/core/project/project-manager.d.ts.map +1 -1
  156. package/dist/src/core/project/project-manager.js +19 -17
  157. package/dist/src/core/project/project-manager.js.map +1 -1
  158. package/dist/src/core/types/config.d.ts +4 -2
  159. package/dist/src/core/types/config.d.ts.map +1 -1
  160. package/dist/src/core/types/config.js.map +1 -1
  161. package/dist/src/core/types/increment-metadata.d.ts +34 -0
  162. package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
  163. package/dist/src/importers/jira-importer.d.ts +14 -0
  164. package/dist/src/importers/jira-importer.d.ts.map +1 -1
  165. package/dist/src/importers/jira-importer.js +75 -0
  166. package/dist/src/importers/jira-importer.js.map +1 -1
  167. package/dist/src/integrations/jira/jira-token-provider.d.ts +93 -0
  168. package/dist/src/integrations/jira/jira-token-provider.d.ts.map +1 -0
  169. package/dist/src/integrations/jira/jira-token-provider.js +160 -0
  170. package/dist/src/integrations/jira/jira-token-provider.js.map +1 -0
  171. package/dist/src/sync/ado-reconciler.d.ts +92 -0
  172. package/dist/src/sync/ado-reconciler.d.ts.map +1 -0
  173. package/dist/src/sync/ado-reconciler.js +335 -0
  174. package/dist/src/sync/ado-reconciler.js.map +1 -0
  175. package/dist/src/sync/jira-reconciler.d.ts +106 -0
  176. package/dist/src/sync/jira-reconciler.d.ts.map +1 -0
  177. package/dist/src/sync/jira-reconciler.js +405 -0
  178. package/dist/src/sync/jira-reconciler.js.map +1 -0
  179. package/dist/src/types/model-selection.d.ts +6 -4
  180. package/dist/src/types/model-selection.d.ts.map +1 -1
  181. package/dist/src/types/model-selection.js +3 -1
  182. package/dist/src/types/model-selection.js.map +1 -1
  183. package/dist/src/utils/cross-cutting-detector.d.ts +66 -0
  184. package/dist/src/utils/cross-cutting-detector.d.ts.map +1 -0
  185. package/dist/src/utils/cross-cutting-detector.js +179 -0
  186. package/dist/src/utils/cross-cutting-detector.js.map +1 -0
  187. package/dist/src/utils/external-tool-drift-detector.d.ts +1 -1
  188. package/dist/src/utils/external-tool-drift-detector.d.ts.map +1 -1
  189. package/dist/src/utils/external-tool-drift-detector.js +5 -4
  190. package/dist/src/utils/external-tool-drift-detector.js.map +1 -1
  191. package/dist/src/utils/feature-id-derivation.d.ts +8 -3
  192. package/dist/src/utils/feature-id-derivation.d.ts.map +1 -1
  193. package/dist/src/utils/feature-id-derivation.js +14 -6
  194. package/dist/src/utils/feature-id-derivation.js.map +1 -1
  195. package/dist/src/utils/model-selection.d.ts +3 -4
  196. package/dist/src/utils/model-selection.d.ts.map +1 -1
  197. package/dist/src/utils/model-selection.js +3 -4
  198. package/dist/src/utils/model-selection.js.map +1 -1
  199. package/dist/src/utils/project-detection.d.ts +12 -8
  200. package/dist/src/utils/project-detection.d.ts.map +1 -1
  201. package/dist/src/utils/project-detection.js +13 -19
  202. package/dist/src/utils/project-detection.js.map +1 -1
  203. package/package.json +1 -1
  204. package/plugins/specweave/agents/code-standards-detective/AGENT.md +48 -0
  205. package/plugins/specweave/commands/specweave-costs.md +4 -4
  206. package/plugins/specweave/commands/specweave-do.md +9 -9
  207. package/plugins/specweave/commands/specweave-done.md +13 -0
  208. package/plugins/specweave/commands/specweave-status.md +64 -0
  209. package/plugins/specweave/commands/specweave-validate.md +27 -1
  210. package/plugins/specweave/hooks/hooks.json +11 -1
  211. package/plugins/specweave/hooks/spec-project-validator.sh +81 -25
  212. package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +135 -0
  213. package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +34 -0
  214. package/plugins/specweave/scripts/read-costs.sh +3 -3
  215. package/plugins/specweave/skills/code-standards-analyzer/SKILL.md +58 -6
  216. package/plugins/specweave/skills/increment-planner/SKILL.md +135 -29
  217. package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +4 -2
  218. package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +2 -1
  219. package/plugins/specweave/skills/increment-planner/templates/tasks-multi-project.md +1 -1
  220. package/plugins/specweave/skills/increment-planner/templates/tasks-single-project.md +1 -1
  221. package/plugins/specweave/skills/spec-generator/SKILL.md +78 -7
  222. package/plugins/specweave-ado/commands/cleanup-duplicates.md +212 -0
  223. package/plugins/specweave-ado/commands/reconcile.md +120 -0
  224. package/plugins/specweave-ado/lib/ado-duplicate-detector.js +279 -0
  225. package/plugins/specweave-ado/lib/ado-duplicate-detector.ts +407 -0
  226. package/plugins/specweave-github/agents/github-manager/AGENT.md +2 -2
  227. package/plugins/specweave-infrastructure/skills/hetzner-provisioner/README.md +1 -1
  228. package/plugins/specweave-jira/agents/jira-manager/AGENT.md +1 -1
  229. package/plugins/specweave-jira/agents/jira-multi-project-mapper/AGENT.md +530 -0
  230. package/plugins/specweave-jira/agents/jira-sync-judge/AGENT.md +438 -0
  231. package/plugins/specweave-jira/commands/cleanup-duplicates.md +219 -0
  232. package/plugins/specweave-jira/commands/close.md +297 -0
  233. package/plugins/specweave-jira/commands/create.md +198 -0
  234. package/plugins/specweave-jira/commands/reconcile.md +123 -0
  235. package/plugins/specweave-jira/commands/status.md +215 -0
  236. package/plugins/specweave-jira/lib/jira-duplicate-detector.js +296 -0
  237. package/plugins/specweave-jira/lib/jira-duplicate-detector.ts +434 -0
  238. package/plugins/specweave-jira/lib/jira-permission-gate.js +160 -0
  239. package/plugins/specweave-jira/lib/jira-permission-gate.ts +276 -0
  240. package/plugins/specweave-jira/lib/jira-profile-resolver.js +222 -0
  241. package/plugins/specweave-jira/lib/jira-profile-resolver.ts +427 -0
  242. package/plugins/specweave-jira/reference/jira-specweave-mapping.md +16 -11
  243. package/plugins/specweave-release/commands/specweave-release-npm.md +140 -14
  244. package/plugins/specweave/commands/specweave-switch-project.md +0 -168
@@ -0,0 +1,310 @@
1
+ /**
2
+ * JIRA Duplicate Detector (v0.33.0)
3
+ *
4
+ * Implements 3-phase duplicate protection for JIRA issues:
5
+ * 1. Detection: Check before create
6
+ * 2. Verification: Count check after create
7
+ * 3. Reflection: Auto-close duplicates
8
+ *
9
+ * Mirrors the GitHub DuplicateDetector pattern for consistency.
10
+ */
11
+ import { consoleLogger } from '../../../src/utils/logger.js';
12
+ export class JiraDuplicateDetector {
13
+ constructor(options = {}) {
14
+ this.domain = options.domain || process.env.JIRA_DOMAIN || '';
15
+ const email = options.email || process.env.JIRA_EMAIL || '';
16
+ const token = options.token || process.env.JIRA_API_TOKEN || '';
17
+ this.auth = Buffer.from(`${email}:${token}`).toString('base64');
18
+ this.logger = options.logger || consoleLogger;
19
+ }
20
+ /**
21
+ * Phase 1: Check if issue exists before creating
22
+ */
23
+ async checkBeforeCreate(summaryPattern, incrementId) {
24
+ try {
25
+ const issues = await this.searchIssues(summaryPattern);
26
+ if (issues.length > 0) {
27
+ return {
28
+ found: true,
29
+ existingIssue: issues[0],
30
+ count: issues.length,
31
+ };
32
+ }
33
+ return { found: false, count: 0 };
34
+ }
35
+ catch (error) {
36
+ this.logger.log(`⚠️ Detection check failed: ${error.message}`);
37
+ // Graceful degradation - continue anyway
38
+ return { found: false, count: 0 };
39
+ }
40
+ }
41
+ /**
42
+ * Phase 2: Verify count after creation
43
+ */
44
+ async verifyAfterCreate(summaryPattern, expectedCount = 1) {
45
+ try {
46
+ const issues = await this.searchIssues(summaryPattern);
47
+ if (issues.length > expectedCount) {
48
+ // Duplicates detected!
49
+ const sorted = issues.sort((a, b) => new Date(a.created).getTime() - new Date(b.created).getTime());
50
+ return {
51
+ success: false,
52
+ expectedCount,
53
+ actualCount: issues.length,
54
+ duplicates: sorted.slice(expectedCount), // All issues after expected count
55
+ };
56
+ }
57
+ return {
58
+ success: true,
59
+ expectedCount,
60
+ actualCount: issues.length,
61
+ duplicates: [],
62
+ };
63
+ }
64
+ catch (error) {
65
+ this.logger.log(`⚠️ Verification check failed: ${error.message}`);
66
+ return {
67
+ success: true, // Assume success on error
68
+ expectedCount,
69
+ actualCount: expectedCount,
70
+ duplicates: [],
71
+ };
72
+ }
73
+ }
74
+ /**
75
+ * Phase 3: Auto-close duplicates
76
+ */
77
+ async closeDuplicates(duplicates, keepIssueKey) {
78
+ const result = {
79
+ closedCount: 0,
80
+ keptCount: 1,
81
+ errors: [],
82
+ };
83
+ for (const issue of duplicates) {
84
+ try {
85
+ await this.closeIssue(issue.key, keepIssueKey);
86
+ result.closedCount++;
87
+ this.logger.log(` ✅ Closed ${issue.key} (duplicate of ${keepIssueKey})`);
88
+ }
89
+ catch (error) {
90
+ result.errors.push(`${issue.key}: ${error.message}`);
91
+ this.logger.log(` ❌ Failed to close ${issue.key}: ${error.message}`);
92
+ }
93
+ }
94
+ return result;
95
+ }
96
+ /**
97
+ * Full cleanup: Find and close all duplicates for a feature
98
+ */
99
+ async cleanupFeatureDuplicates(featureId, dryRun = false) {
100
+ // 1. Search for all issues with feature ID
101
+ const searchPattern = `[${featureId}]`;
102
+ const issues = await this.searchIssues(searchPattern);
103
+ this.logger.log(`\n🔍 Scanning for duplicates in Feature ${featureId}...`);
104
+ this.logger.log(` Found ${issues.length} total issues`);
105
+ // 2. Group by summary
106
+ const groups = this.groupBySummary(issues);
107
+ const duplicateGroups = groups.filter(g => g.duplicates.length > 0);
108
+ if (duplicateGroups.length === 0) {
109
+ this.logger.log(` ✅ No duplicates found!`);
110
+ return {
111
+ groups: [],
112
+ totalIssues: issues.length,
113
+ duplicateCount: 0,
114
+ closedCount: 0,
115
+ };
116
+ }
117
+ this.logger.log(` Detected ${duplicateGroups.length} duplicate groups:\n`);
118
+ // 3. Display groups
119
+ for (let i = 0; i < duplicateGroups.length; i++) {
120
+ const group = duplicateGroups[i];
121
+ this.logger.log(` 📋 Group ${i + 1}: "${group.summary.substring(0, 50)}..."`);
122
+ this.logger.log(` - ${group.keepIssue.key} (KEEP) - Created ${group.keepIssue.created.split('T')[0]}`);
123
+ for (const dup of group.duplicates) {
124
+ this.logger.log(` - ${dup.key} (CLOSE) - Created ${dup.created.split('T')[0]} - DUPLICATE`);
125
+ }
126
+ this.logger.log('');
127
+ }
128
+ const totalDuplicates = duplicateGroups.reduce((sum, g) => sum + g.duplicates.length, 0);
129
+ if (dryRun) {
130
+ this.logger.log(`\n✅ Dry run complete!`);
131
+ this.logger.log(` Total issues: ${issues.length}`);
132
+ this.logger.log(` Duplicate groups: ${duplicateGroups.length}`);
133
+ this.logger.log(` Issues to close: ${totalDuplicates}`);
134
+ this.logger.log(`\n⚠️ This was a DRY RUN - no changes made.`);
135
+ return {
136
+ groups: duplicateGroups,
137
+ totalIssues: issues.length,
138
+ duplicateCount: totalDuplicates,
139
+ closedCount: 0,
140
+ };
141
+ }
142
+ // 4. Close duplicates
143
+ let closedCount = 0;
144
+ this.logger.log(`🗑️ Closing duplicates...`);
145
+ for (const group of duplicateGroups) {
146
+ const result = await this.closeDuplicates(group.duplicates, group.keepIssue.key);
147
+ closedCount += result.closedCount;
148
+ }
149
+ this.logger.log(`\n✅ Cleanup complete!`);
150
+ this.logger.log(` Closed: ${closedCount} duplicates`);
151
+ this.logger.log(` Kept: ${duplicateGroups.length} original issues`);
152
+ return {
153
+ groups: duplicateGroups,
154
+ totalIssues: issues.length,
155
+ duplicateCount: totalDuplicates,
156
+ closedCount,
157
+ };
158
+ }
159
+ /**
160
+ * Group issues by summary
161
+ */
162
+ groupBySummary(issues) {
163
+ const summaryMap = new Map();
164
+ for (const issue of issues) {
165
+ const existing = summaryMap.get(issue.summary) || [];
166
+ existing.push(issue);
167
+ summaryMap.set(issue.summary, existing);
168
+ }
169
+ const groups = [];
170
+ for (const [summary, groupIssues] of summaryMap) {
171
+ // Sort by created date (oldest first)
172
+ const sorted = groupIssues.sort((a, b) => new Date(a.created).getTime() - new Date(b.created).getTime());
173
+ groups.push({
174
+ summary,
175
+ issues: sorted,
176
+ keepIssue: sorted[0],
177
+ duplicates: sorted.slice(1),
178
+ });
179
+ }
180
+ return groups;
181
+ }
182
+ /**
183
+ * Search for issues using JQL
184
+ */
185
+ async searchIssues(summaryPattern) {
186
+ if (!this.domain || !this.auth) {
187
+ throw new Error('JIRA credentials not configured');
188
+ }
189
+ const jql = encodeURIComponent(`summary ~ "${summaryPattern}" ORDER BY created ASC`);
190
+ const url = `https://${this.domain}/rest/api/3/search?jql=${jql}&fields=summary,status,created`;
191
+ const response = await fetch(url, {
192
+ headers: {
193
+ Authorization: `Basic ${this.auth}`,
194
+ Accept: 'application/json',
195
+ },
196
+ });
197
+ if (!response.ok) {
198
+ throw new Error(`JQL search failed: ${response.status}`);
199
+ }
200
+ const data = await response.json();
201
+ return (data.issues || []).map((issue) => ({
202
+ key: issue.key,
203
+ summary: issue.fields.summary,
204
+ status: issue.fields.status?.name,
205
+ created: issue.fields.created,
206
+ url: `https://${this.domain}/browse/${issue.key}`,
207
+ }));
208
+ }
209
+ /**
210
+ * Close an issue with duplicate comment
211
+ */
212
+ async closeIssue(issueKey, originalKey) {
213
+ // First, add comment
214
+ await this.addComment(issueKey, originalKey);
215
+ // Then, transition to closed
216
+ const transitions = await this.getTransitions(issueKey);
217
+ // Find "Won't Do" or "Done" transition
218
+ const closeTransition = transitions.find((t) => t.name === "Won't Do" ||
219
+ t.name === 'Done' ||
220
+ t.name === 'Closed' ||
221
+ t.to?.name === "Won't Do" ||
222
+ t.to?.name === 'Done');
223
+ if (!closeTransition) {
224
+ throw new Error(`No close transition found. Available: ${transitions.map((t) => t.name).join(', ')}`);
225
+ }
226
+ const url = `https://${this.domain}/rest/api/3/issue/${issueKey}/transitions`;
227
+ const response = await fetch(url, {
228
+ method: 'POST',
229
+ headers: {
230
+ Authorization: `Basic ${this.auth}`,
231
+ 'Content-Type': 'application/json',
232
+ },
233
+ body: JSON.stringify({
234
+ transition: { id: closeTransition.id },
235
+ }),
236
+ });
237
+ if (!response.ok) {
238
+ const error = await response.text();
239
+ throw new Error(`Failed to transition issue: ${response.status} - ${error}`);
240
+ }
241
+ }
242
+ /**
243
+ * Get available transitions for an issue
244
+ */
245
+ async getTransitions(issueKey) {
246
+ const url = `https://${this.domain}/rest/api/3/issue/${issueKey}/transitions`;
247
+ const response = await fetch(url, {
248
+ headers: {
249
+ Authorization: `Basic ${this.auth}`,
250
+ Accept: 'application/json',
251
+ },
252
+ });
253
+ if (!response.ok) {
254
+ throw new Error(`Failed to get transitions: ${response.status}`);
255
+ }
256
+ const data = await response.json();
257
+ return data.transitions || [];
258
+ }
259
+ /**
260
+ * Add duplicate comment to issue
261
+ */
262
+ async addComment(issueKey, originalKey) {
263
+ const url = `https://${this.domain}/rest/api/3/issue/${issueKey}/comment`;
264
+ const comment = `h2. Duplicate of ${originalKey}
265
+
266
+ This issue was automatically closed by SpecWeave cleanup because it is a duplicate.
267
+
268
+ The original issue (${originalKey}) contains the same content and should be used for tracking instead.
269
+
270
+ ----
271
+ 🤖 Auto-closed by SpecWeave Duplicate Cleanup`;
272
+ const response = await fetch(url, {
273
+ method: 'POST',
274
+ headers: {
275
+ Authorization: `Basic ${this.auth}`,
276
+ 'Content-Type': 'application/json',
277
+ },
278
+ body: JSON.stringify({
279
+ body: {
280
+ type: 'doc',
281
+ version: 1,
282
+ content: [
283
+ {
284
+ type: 'paragraph',
285
+ content: [
286
+ {
287
+ type: 'text',
288
+ text: comment,
289
+ },
290
+ ],
291
+ },
292
+ ],
293
+ },
294
+ }),
295
+ });
296
+ if (!response.ok) {
297
+ // Non-fatal, just log warning
298
+ this.logger.log(` ⚠️ Failed to add comment to ${issueKey}`);
299
+ }
300
+ }
301
+ }
302
+ /**
303
+ * Convenience function for quick duplicate cleanup
304
+ */
305
+ export async function cleanupJiraDuplicates(featureId, dryRun = false) {
306
+ const detector = new JiraDuplicateDetector();
307
+ return detector.cleanupFeatureDuplicates(featureId, dryRun);
308
+ }
309
+ export default JiraDuplicateDetector;
310
+ //# sourceMappingURL=jira-duplicate-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira-duplicate-detector.js","sourceRoot":"","sources":["../../../../plugins/specweave-jira/lib/jira-duplicate-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * JIRA Permission Gate
3
+ *
4
+ * Validates that sync permissions are enabled before allowing JIRA write operations.
5
+ * This ensures manual JIRA commands respect the same permission settings as the
6
+ * sync-coordinator.
7
+ *
8
+ * Usage:
9
+ * ```typescript
10
+ * const gate = await createJiraPermissionGate();
11
+ * const result = gate.checkWritePermission();
12
+ * if (!result.allowed) {
13
+ * console.log(result.reason);
14
+ * return;
15
+ * }
16
+ * ```
17
+ *
18
+ * @module jira-permission-gate
19
+ */
20
+ /**
21
+ * Permission check result
22
+ */
23
+ export interface PermissionCheckResult {
24
+ /**
25
+ * Whether the operation is allowed
26
+ */
27
+ allowed: boolean;
28
+ /**
29
+ * Human-readable reason for the decision
30
+ */
31
+ reason: string;
32
+ /**
33
+ * Suggested action if permission denied
34
+ */
35
+ suggestedAction?: string;
36
+ /**
37
+ * Which setting controls this permission
38
+ */
39
+ settingPath?: string;
40
+ }
41
+ /**
42
+ * Sync settings from config.json
43
+ */
44
+ export interface SyncSettings {
45
+ canUpsertInternalItems: boolean;
46
+ canUpdateExternalItems: boolean;
47
+ canUpdateStatus: boolean;
48
+ }
49
+ /**
50
+ * Default settings (all disabled for safety)
51
+ */
52
+ export declare const DEFAULT_SYNC_SETTINGS: SyncSettings;
53
+ /**
54
+ * JIRA Permission Gate
55
+ *
56
+ * Checks permission settings before allowing JIRA write operations.
57
+ */
58
+ export declare class JiraPermissionGate {
59
+ private settings;
60
+ private configPath;
61
+ constructor(settings: SyncSettings, configPath: string);
62
+ /**
63
+ * Check if write operations (create/update issues) are allowed
64
+ *
65
+ * Requires: canUpdateExternalItems = true
66
+ */
67
+ checkWritePermission(): PermissionCheckResult;
68
+ /**
69
+ * Check if status updates (transitions) are allowed
70
+ *
71
+ * Requires: canUpdateStatus = true
72
+ */
73
+ checkStatusPermission(): PermissionCheckResult;
74
+ /**
75
+ * Check if internal item creation is allowed
76
+ *
77
+ * Requires: canUpsertInternalItems = true
78
+ */
79
+ checkCreateInternalPermission(): PermissionCheckResult;
80
+ /**
81
+ * Check if close operation is allowed (requires both write AND status)
82
+ *
83
+ * Requires: canUpdateExternalItems = true AND canUpdateStatus = true
84
+ */
85
+ checkClosePermission(): PermissionCheckResult;
86
+ /**
87
+ * Get current settings
88
+ */
89
+ getSettings(): SyncSettings;
90
+ /**
91
+ * Get human-readable permission summary
92
+ */
93
+ getPermissionSummary(): string;
94
+ }
95
+ /**
96
+ * Create a JiraPermissionGate from config.json
97
+ *
98
+ * @param projectRoot - Project root directory (defaults to cwd)
99
+ * @returns JiraPermissionGate instance
100
+ */
101
+ export declare function createJiraPermissionGate(projectRoot?: string): Promise<JiraPermissionGate>;
102
+ /**
103
+ * Quick check: Are JIRA write operations allowed?
104
+ *
105
+ * Convenience function for simple permission checks.
106
+ *
107
+ * @param projectRoot - Project root directory
108
+ * @returns Permission check result
109
+ */
110
+ export declare function canWriteToJira(projectRoot?: string): Promise<PermissionCheckResult>;
111
+ /**
112
+ * Quick check: Are JIRA status updates allowed?
113
+ *
114
+ * @param projectRoot - Project root directory
115
+ * @returns Permission check result
116
+ */
117
+ export declare function canUpdateJiraStatus(projectRoot?: string): Promise<PermissionCheckResult>;
118
+ /**
119
+ * Quick check: Can JIRA issues be closed?
120
+ *
121
+ * @param projectRoot - Project root directory
122
+ * @returns Permission check result
123
+ */
124
+ export declare function canCloseJiraIssue(projectRoot?: string): Promise<PermissionCheckResult>;
125
+ export default JiraPermissionGate;
126
+ //# sourceMappingURL=jira-permission-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira-permission-gate.d.ts","sourceRoot":"","sources":["../../../../plugins/specweave-jira/lib/jira-permission-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,sBAAsB,EAAE,OAAO,CAAC;IAChC,sBAAsB,EAAE,OAAO,CAAC;IAChC,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,YAInC,CAAC;AAEF;;;;GAIG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,UAAU,CAAS;gBAEf,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM;IAKtD;;;;OAIG;IACH,oBAAoB,IAAI,qBAAqB;IAgB7C;;;;OAIG;IACH,qBAAqB,IAAI,qBAAqB;IAgB9C;;;;OAIG;IACH,6BAA6B,IAAI,qBAAqB;IAgBtD;;;;OAIG;IACH,oBAAoB,IAAI,qBAAqB;IA2B7C;;OAEG;IACH,WAAW,IAAI,YAAY;IAI3B;;OAEG;IACH,oBAAoB,IAAI,MAAM;CAmB/B;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,WAAW,GAAE,MAAsB,GAClC,OAAO,CAAC,kBAAkB,CAAC,CAkB7B;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,WAAW,GAAE,MAAsB,GAClC,OAAO,CAAC,qBAAqB,CAAC,CAGhC;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,GAAE,MAAsB,GAClC,OAAO,CAAC,qBAAqB,CAAC,CAGhC;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,GAAE,MAAsB,GAClC,OAAO,CAAC,qBAAqB,CAAC,CAGhC;AAED,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,207 @@
1
+ /**
2
+ * JIRA Permission Gate
3
+ *
4
+ * Validates that sync permissions are enabled before allowing JIRA write operations.
5
+ * This ensures manual JIRA commands respect the same permission settings as the
6
+ * sync-coordinator.
7
+ *
8
+ * Usage:
9
+ * ```typescript
10
+ * const gate = await createJiraPermissionGate();
11
+ * const result = gate.checkWritePermission();
12
+ * if (!result.allowed) {
13
+ * console.log(result.reason);
14
+ * return;
15
+ * }
16
+ * ```
17
+ *
18
+ * @module jira-permission-gate
19
+ */
20
+ import { promises as fs } from 'node:fs';
21
+ import * as path from 'node:path';
22
+ /**
23
+ * Default settings (all disabled for safety)
24
+ */
25
+ export const DEFAULT_SYNC_SETTINGS = {
26
+ canUpsertInternalItems: false,
27
+ canUpdateExternalItems: false,
28
+ canUpdateStatus: false,
29
+ };
30
+ /**
31
+ * JIRA Permission Gate
32
+ *
33
+ * Checks permission settings before allowing JIRA write operations.
34
+ */
35
+ export class JiraPermissionGate {
36
+ constructor(settings, configPath) {
37
+ this.settings = settings;
38
+ this.configPath = configPath;
39
+ }
40
+ /**
41
+ * Check if write operations (create/update issues) are allowed
42
+ *
43
+ * Requires: canUpdateExternalItems = true
44
+ */
45
+ checkWritePermission() {
46
+ if (this.settings.canUpdateExternalItems) {
47
+ return {
48
+ allowed: true,
49
+ reason: 'Write operations permitted (canUpdateExternalItems=true)',
50
+ };
51
+ }
52
+ return {
53
+ allowed: false,
54
+ reason: 'Permission denied: JIRA updates are disabled.',
55
+ suggestedAction: `Enable sync.settings.canUpdateExternalItems in ${this.configPath}`,
56
+ settingPath: 'sync.settings.canUpdateExternalItems',
57
+ };
58
+ }
59
+ /**
60
+ * Check if status updates (transitions) are allowed
61
+ *
62
+ * Requires: canUpdateStatus = true
63
+ */
64
+ checkStatusPermission() {
65
+ if (this.settings.canUpdateStatus) {
66
+ return {
67
+ allowed: true,
68
+ reason: 'Status updates permitted (canUpdateStatus=true)',
69
+ };
70
+ }
71
+ return {
72
+ allowed: false,
73
+ reason: 'Permission denied: JIRA status transitions are disabled.',
74
+ suggestedAction: `Enable sync.settings.canUpdateStatus in ${this.configPath}`,
75
+ settingPath: 'sync.settings.canUpdateStatus',
76
+ };
77
+ }
78
+ /**
79
+ * Check if internal item creation is allowed
80
+ *
81
+ * Requires: canUpsertInternalItems = true
82
+ */
83
+ checkCreateInternalPermission() {
84
+ if (this.settings.canUpsertInternalItems) {
85
+ return {
86
+ allowed: true,
87
+ reason: 'Internal item creation permitted (canUpsertInternalItems=true)',
88
+ };
89
+ }
90
+ return {
91
+ allowed: false,
92
+ reason: 'Permission denied: Creating internal items is disabled.',
93
+ suggestedAction: `Enable sync.settings.canUpsertInternalItems in ${this.configPath}`,
94
+ settingPath: 'sync.settings.canUpsertInternalItems',
95
+ };
96
+ }
97
+ /**
98
+ * Check if close operation is allowed (requires both write AND status)
99
+ *
100
+ * Requires: canUpdateExternalItems = true AND canUpdateStatus = true
101
+ */
102
+ checkClosePermission() {
103
+ const writeCheck = this.checkWritePermission();
104
+ const statusCheck = this.checkStatusPermission();
105
+ if (writeCheck.allowed && statusCheck.allowed) {
106
+ return {
107
+ allowed: true,
108
+ reason: 'Close operations permitted (canUpdateExternalItems=true, canUpdateStatus=true)',
109
+ };
110
+ }
111
+ const missingPermissions = [];
112
+ if (!writeCheck.allowed) {
113
+ missingPermissions.push('canUpdateExternalItems');
114
+ }
115
+ if (!statusCheck.allowed) {
116
+ missingPermissions.push('canUpdateStatus');
117
+ }
118
+ return {
119
+ allowed: false,
120
+ reason: `Permission denied: Closing JIRA issues requires ${missingPermissions.join(' and ')}.`,
121
+ suggestedAction: `Enable ${missingPermissions.map(p => `sync.settings.${p}`).join(' and ')} in ${this.configPath}`,
122
+ settingPath: missingPermissions.map(p => `sync.settings.${p}`).join(', '),
123
+ };
124
+ }
125
+ /**
126
+ * Get current settings
127
+ */
128
+ getSettings() {
129
+ return { ...this.settings };
130
+ }
131
+ /**
132
+ * Get human-readable permission summary
133
+ */
134
+ getPermissionSummary() {
135
+ const parts = [];
136
+ if (this.settings.canUpdateExternalItems) {
137
+ parts.push('create/update JIRA issues');
138
+ }
139
+ if (this.settings.canUpdateStatus) {
140
+ parts.push('transition issue status');
141
+ }
142
+ if (this.settings.canUpsertInternalItems) {
143
+ parts.push('create internal items');
144
+ }
145
+ if (parts.length === 0) {
146
+ return 'All JIRA write operations disabled (read-only mode)';
147
+ }
148
+ return `Allowed: ${parts.join(', ')}`;
149
+ }
150
+ }
151
+ /**
152
+ * Create a JiraPermissionGate from config.json
153
+ *
154
+ * @param projectRoot - Project root directory (defaults to cwd)
155
+ * @returns JiraPermissionGate instance
156
+ */
157
+ export async function createJiraPermissionGate(projectRoot = process.cwd()) {
158
+ const configPath = path.join(projectRoot, '.specweave', 'config.json');
159
+ try {
160
+ const content = await fs.readFile(configPath, 'utf-8');
161
+ const config = JSON.parse(content);
162
+ const settings = {
163
+ canUpsertInternalItems: config?.sync?.settings?.canUpsertInternalItems ?? false,
164
+ canUpdateExternalItems: config?.sync?.settings?.canUpdateExternalItems ?? false,
165
+ canUpdateStatus: config?.sync?.settings?.canUpdateStatus ?? false,
166
+ };
167
+ return new JiraPermissionGate(settings, configPath);
168
+ }
169
+ catch {
170
+ // Return gate with default (disabled) settings if config not found
171
+ return new JiraPermissionGate(DEFAULT_SYNC_SETTINGS, configPath);
172
+ }
173
+ }
174
+ /**
175
+ * Quick check: Are JIRA write operations allowed?
176
+ *
177
+ * Convenience function for simple permission checks.
178
+ *
179
+ * @param projectRoot - Project root directory
180
+ * @returns Permission check result
181
+ */
182
+ export async function canWriteToJira(projectRoot = process.cwd()) {
183
+ const gate = await createJiraPermissionGate(projectRoot);
184
+ return gate.checkWritePermission();
185
+ }
186
+ /**
187
+ * Quick check: Are JIRA status updates allowed?
188
+ *
189
+ * @param projectRoot - Project root directory
190
+ * @returns Permission check result
191
+ */
192
+ export async function canUpdateJiraStatus(projectRoot = process.cwd()) {
193
+ const gate = await createJiraPermissionGate(projectRoot);
194
+ return gate.checkStatusPermission();
195
+ }
196
+ /**
197
+ * Quick check: Can JIRA issues be closed?
198
+ *
199
+ * @param projectRoot - Project root directory
200
+ * @returns Permission check result
201
+ */
202
+ export async function canCloseJiraIssue(projectRoot = process.cwd()) {
203
+ const gate = await createJiraPermissionGate(projectRoot);
204
+ return gate.checkClosePermission();
205
+ }
206
+ export default JiraPermissionGate;
207
+ //# sourceMappingURL=jira-permission-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira-permission-gate.js","sourceRoot":"","sources":["../../../../plugins/specweave-jira/lib/jira-permission-gate.ts"],"names":[],"mappings":"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"}
@@ -82,7 +82,7 @@ codex "Read AGENTS.md and increment 0001. Fix auth bug. Run tests."
82
82
  | **Skills** | Native | Via AGENTS.md |
83
83
  | **Agents** | Native | Via AGENTS.md |
84
84
  | **Access Points** | CLI only | CLI + Web + IDE + GitHub + iOS |
85
- | **Model** | Sonnet 4.5 | GPT-5-Codex |
85
+ | **Model** | Opus 4.5 | GPT-5-Codex |
86
86
  | **Task Isolation** | No | Yes (isolated per task) |
87
87
  | **Hooks** | Yes | No |
88
88