specweave 0.34.5 → 0.34.8

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 (181) hide show
  1. package/bin/fix-marketplace-errors.sh +55 -7
  2. package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -1
  3. package/dist/plugins/specweave-github/lib/github-feature-sync.js +3 -1
  4. package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
  5. package/dist/plugins/specweave-github/lib/github-spec-sync.js +1 -1
  6. package/dist/plugins/specweave-github/lib/github-spec-sync.js.map +1 -1
  7. package/dist/src/cli/commands/import-docs.d.ts +3 -0
  8. package/dist/src/cli/commands/import-docs.d.ts.map +1 -1
  9. package/dist/src/cli/commands/import-docs.js +110 -91
  10. package/dist/src/cli/commands/import-docs.js.map +1 -1
  11. package/dist/src/cli/commands/init.d.ts.map +1 -1
  12. package/dist/src/cli/commands/init.js +16 -7
  13. package/dist/src/cli/commands/init.js.map +1 -1
  14. package/dist/src/cli/commands/living-docs.js +4 -4
  15. package/dist/src/cli/commands/living-docs.js.map +1 -1
  16. package/dist/src/cli/commands/sync-spec-commits.js +1 -1
  17. package/dist/src/cli/commands/sync-spec-commits.js.map +1 -1
  18. package/dist/src/cli/commands/sync-spec-content.js +1 -1
  19. package/dist/src/cli/commands/sync-spec-content.js.map +1 -1
  20. package/dist/src/cli/helpers/github/increment-profile-selector.js +1 -1
  21. package/dist/src/cli/helpers/github/increment-profile-selector.js.map +1 -1
  22. package/dist/src/cli/helpers/github/profile-manager.d.ts +5 -5
  23. package/dist/src/cli/helpers/github/profile-manager.d.ts.map +1 -1
  24. package/dist/src/cli/helpers/github/profile-manager.js +24 -24
  25. package/dist/src/cli/helpers/github/profile-manager.js.map +1 -1
  26. package/dist/src/cli/helpers/init/brownfield-analysis.js +15 -15
  27. package/dist/src/cli/helpers/init/brownfield-analysis.js.map +1 -1
  28. package/dist/src/cli/helpers/init/config-detection.js +1 -1
  29. package/dist/src/cli/helpers/init/config-detection.js.map +1 -1
  30. package/dist/src/cli/helpers/init/living-docs-preflight.js +7 -7
  31. package/dist/src/cli/helpers/init/living-docs-preflight.js.map +1 -1
  32. package/dist/src/cli/helpers/init/plugin-installer.d.ts.map +1 -1
  33. package/dist/src/cli/helpers/init/plugin-installer.js +61 -9
  34. package/dist/src/cli/helpers/init/plugin-installer.js.map +1 -1
  35. package/dist/src/cli/helpers/init/sync-profile-helpers.d.ts.map +1 -1
  36. package/dist/src/cli/helpers/init/sync-profile-helpers.js +2 -4
  37. package/dist/src/cli/helpers/init/sync-profile-helpers.js.map +1 -1
  38. package/dist/src/cli/helpers/issue-tracker/ado-area-selection.d.ts +1 -4
  39. package/dist/src/cli/helpers/issue-tracker/ado-area-selection.d.ts.map +1 -1
  40. package/dist/src/cli/helpers/issue-tracker/ado-area-selection.js +0 -32
  41. package/dist/src/cli/helpers/issue-tracker/ado-area-selection.js.map +1 -1
  42. package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
  43. package/dist/src/cli/helpers/issue-tracker/ado.js +3 -13
  44. package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
  45. package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
  46. package/dist/src/cli/helpers/issue-tracker/index.js +0 -1
  47. package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
  48. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.d.ts.map +1 -1
  49. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js +0 -7
  50. package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js.map +1 -1
  51. package/dist/src/cli/helpers/issue-tracker/types.d.ts +1 -5
  52. package/dist/src/cli/helpers/issue-tracker/types.d.ts.map +1 -1
  53. package/dist/src/cli/helpers/issue-tracker/types.js.map +1 -1
  54. package/dist/src/cli/workers/import-worker.js +5 -111
  55. package/dist/src/cli/workers/import-worker.js.map +1 -1
  56. package/dist/src/core/background/types.d.ts +3 -0
  57. package/dist/src/core/background/types.d.ts.map +1 -1
  58. package/dist/src/core/config/types.d.ts +0 -5
  59. package/dist/src/core/config/types.d.ts.map +1 -1
  60. package/dist/src/core/config/types.js.map +1 -1
  61. package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
  62. package/dist/src/core/increment/increment-archiver.js +0 -6
  63. package/dist/src/core/increment/increment-archiver.js.map +1 -1
  64. package/dist/src/core/increment/increment-utils.d.ts +0 -4
  65. package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
  66. package/dist/src/core/increment/increment-utils.js +0 -6
  67. package/dist/src/core/increment/increment-utils.js.map +1 -1
  68. package/dist/src/core/living-docs/cross-project-sync.d.ts +0 -13
  69. package/dist/src/core/living-docs/cross-project-sync.d.ts.map +1 -1
  70. package/dist/src/core/living-docs/cross-project-sync.js +0 -25
  71. package/dist/src/core/living-docs/cross-project-sync.js.map +1 -1
  72. package/dist/src/core/living-docs/delivery/delivery-generator.d.ts +58 -0
  73. package/dist/src/core/living-docs/delivery/delivery-generator.d.ts.map +1 -0
  74. package/dist/src/core/living-docs/delivery/delivery-generator.js +501 -0
  75. package/dist/src/core/living-docs/delivery/delivery-generator.js.map +1 -0
  76. package/dist/src/core/living-docs/delivery/index.d.ts +8 -0
  77. package/dist/src/core/living-docs/delivery/index.d.ts.map +1 -0
  78. package/dist/src/core/living-docs/delivery/index.js +7 -0
  79. package/dist/src/core/living-docs/delivery/index.js.map +1 -0
  80. package/dist/src/core/living-docs/diagrams/index.d.ts +8 -0
  81. package/dist/src/core/living-docs/diagrams/index.d.ts.map +1 -0
  82. package/dist/src/core/living-docs/diagrams/index.js +7 -0
  83. package/dist/src/core/living-docs/diagrams/index.js.map +1 -0
  84. package/dist/src/core/living-docs/diagrams/mermaid-generator.d.ts +103 -0
  85. package/dist/src/core/living-docs/diagrams/mermaid-generator.d.ts.map +1 -0
  86. package/dist/src/core/living-docs/diagrams/mermaid-generator.js +515 -0
  87. package/dist/src/core/living-docs/diagrams/mermaid-generator.js.map +1 -0
  88. package/dist/src/core/living-docs/enterprise/enterprise-generator.d.ts +85 -0
  89. package/dist/src/core/living-docs/enterprise/enterprise-generator.d.ts.map +1 -0
  90. package/dist/src/core/living-docs/enterprise/enterprise-generator.js +556 -0
  91. package/dist/src/core/living-docs/enterprise/enterprise-generator.js.map +1 -0
  92. package/dist/src/core/living-docs/enterprise/history-analyzer.d.ts +91 -0
  93. package/dist/src/core/living-docs/enterprise/history-analyzer.d.ts.map +1 -0
  94. package/dist/src/core/living-docs/enterprise/history-analyzer.js +321 -0
  95. package/dist/src/core/living-docs/enterprise/history-analyzer.js.map +1 -0
  96. package/dist/src/core/living-docs/enterprise/index.d.ts +18 -0
  97. package/dist/src/core/living-docs/enterprise/index.d.ts.map +1 -0
  98. package/dist/src/core/living-docs/enterprise/index.js +14 -0
  99. package/dist/src/core/living-docs/enterprise/index.js.map +1 -0
  100. package/dist/src/core/living-docs/enterprise/relationship-mapper.d.ts +58 -0
  101. package/dist/src/core/living-docs/enterprise/relationship-mapper.d.ts.map +1 -0
  102. package/dist/src/core/living-docs/enterprise/relationship-mapper.js +227 -0
  103. package/dist/src/core/living-docs/enterprise/relationship-mapper.js.map +1 -0
  104. package/dist/src/core/living-docs/enterprise/spec-loader.d.ts +161 -0
  105. package/dist/src/core/living-docs/enterprise/spec-loader.d.ts.map +1 -0
  106. package/dist/src/core/living-docs/enterprise/spec-loader.js +470 -0
  107. package/dist/src/core/living-docs/enterprise/spec-loader.js.map +1 -0
  108. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts +31 -1
  109. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts.map +1 -1
  110. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js +626 -14
  111. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js.map +1 -1
  112. package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts +8 -0
  113. package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts.map +1 -1
  114. package/dist/src/core/living-docs/intelligent-analyzer/index.js +87 -4
  115. package/dist/src/core/living-docs/intelligent-analyzer/index.js.map +1 -1
  116. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +3 -1
  117. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -1
  118. package/dist/src/core/living-docs/living-docs-sync.js +1 -1
  119. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  120. package/dist/src/core/living-docs/operations/index.d.ts +8 -0
  121. package/dist/src/core/living-docs/operations/index.d.ts.map +1 -0
  122. package/dist/src/core/living-docs/operations/index.js +7 -0
  123. package/dist/src/core/living-docs/operations/index.js.map +1 -0
  124. package/dist/src/core/living-docs/operations/ops-generator.d.ts +53 -0
  125. package/dist/src/core/living-docs/operations/ops-generator.d.ts.map +1 -0
  126. package/dist/src/core/living-docs/operations/ops-generator.js +462 -0
  127. package/dist/src/core/living-docs/operations/ops-generator.js.map +1 -0
  128. package/dist/src/core/project/project-manager.d.ts +0 -20
  129. package/dist/src/core/project/project-manager.d.ts.map +1 -1
  130. package/dist/src/core/project/project-manager.js +0 -49
  131. package/dist/src/core/project/project-manager.js.map +1 -1
  132. package/dist/src/core/repo-structure/prompt-consolidator.d.ts +0 -8
  133. package/dist/src/core/repo-structure/prompt-consolidator.d.ts.map +1 -1
  134. package/dist/src/core/repo-structure/prompt-consolidator.js +0 -28
  135. package/dist/src/core/repo-structure/prompt-consolidator.js.map +1 -1
  136. package/dist/src/core/sync/profile-manager.d.ts +5 -5
  137. package/dist/src/core/sync/profile-manager.d.ts.map +1 -1
  138. package/dist/src/core/sync/profile-manager.js +17 -17
  139. package/dist/src/core/sync/profile-manager.js.map +1 -1
  140. package/dist/src/core/types/sync-profile.d.ts +3 -20
  141. package/dist/src/core/types/sync-profile.d.ts.map +1 -1
  142. package/dist/src/core/types/sync-profile.js +4 -10
  143. package/dist/src/core/types/sync-profile.js.map +1 -1
  144. package/dist/src/integrations/ado/ado-client-factory.d.ts +2 -2
  145. package/dist/src/integrations/ado/ado-client-factory.js +2 -2
  146. package/dist/src/utils/project-detection.d.ts +16 -16
  147. package/dist/src/utils/project-detection.d.ts.map +1 -1
  148. package/dist/src/utils/project-detection.js +39 -39
  149. package/dist/src/utils/project-detection.js.map +1 -1
  150. package/dist/src/utils/validators/ado-validator.d.ts +0 -5
  151. package/dist/src/utils/validators/ado-validator.d.ts.map +1 -1
  152. package/dist/src/utils/validators/ado-validator.js +0 -23
  153. package/dist/src/utils/validators/ado-validator.js.map +1 -1
  154. package/package.json +1 -1
  155. package/plugins/specweave/commands/living-docs.md +168 -39
  156. package/plugins/specweave-github/lib/github-feature-sync.js +1 -1
  157. package/plugins/specweave-github/lib/github-feature-sync.ts +3 -1
  158. package/plugins/specweave-github/lib/github-spec-sync.js +1 -1
  159. package/plugins/specweave-github/lib/github-spec-sync.ts +1 -1
  160. package/dist/plugins/specweave-github/lib/github-sync-bidirectional.d.ts +0 -45
  161. package/dist/plugins/specweave-github/lib/github-sync-bidirectional.d.ts.map +0 -1
  162. package/dist/plugins/specweave-github/lib/github-sync-bidirectional.js +0 -247
  163. package/dist/plugins/specweave-github/lib/github-sync-bidirectional.js.map +0 -1
  164. package/dist/src/core/brownfield/analyzer.d.ts +0 -86
  165. package/dist/src/core/brownfield/analyzer.d.ts.map +0 -1
  166. package/dist/src/core/brownfield/analyzer.js +0 -365
  167. package/dist/src/core/brownfield/analyzer.js.map +0 -1
  168. package/dist/src/core/brownfield/importer.d.ts +0 -76
  169. package/dist/src/core/brownfield/importer.d.ts.map +0 -1
  170. package/dist/src/core/brownfield/importer.js +0 -287
  171. package/dist/src/core/brownfield/importer.js.map +0 -1
  172. package/dist/src/utils/env-multi-project-parser.d.ts +0 -237
  173. package/dist/src/utils/env-multi-project-parser.d.ts.map +0 -1
  174. package/dist/src/utils/env-multi-project-parser.js +0 -426
  175. package/dist/src/utils/env-multi-project-parser.js.map +0 -1
  176. package/plugins/specweave-github/lib/github-sync-bidirectional.d.js +0 -0
  177. package/plugins/specweave-github/lib/github-sync-bidirectional.d.ts +0 -36
  178. package/plugins/specweave-github/lib/github-sync-bidirectional.d.ts.map +0 -1
  179. package/plugins/specweave-github/lib/github-sync-bidirectional.js +0 -206
  180. package/plugins/specweave-github/lib/github-sync-bidirectional.js.map +0 -1
  181. package/plugins/specweave-github/lib/github-sync-bidirectional.ts +0 -362
@@ -1,426 +0,0 @@
1
- /**
2
- * Environment Multi-Project Parser
3
- *
4
- * @deprecated (v0.34.0+) This module reads configuration from process.env.
5
- * Configuration should come from ConfigManager (config.json) instead.
6
- *
7
- * MIGRATION GUIDE:
8
- * - JIRA_DOMAIN → config.issueTracker.domain (config.json)
9
- * - AZURE_DEVOPS_ORG → config.issueTracker.organization_ado (config.json)
10
- * - AZURE_DEVOPS_PROJECT → config.issueTracker.project (config.json)
11
- * - Keep JIRA_API_TOKEN, JIRA_EMAIL, AZURE_DEVOPS_PAT, GITHUB_TOKEN in .env (secrets)
12
- *
13
- * This module will be removed in v1.0.
14
- *
15
- * Parses comma-separated project lists from .env:
16
- * - JIRA_PROJECT_KEYS=BACKEND,FRONTEND,MOBILE (multiple projects)
17
- * - GITHUB_REPOS=owner/backend-api,owner/frontend-web (multiple repos)
18
- * - AZURE_DEVOPS_PROJECT=myproject (single project only)
19
- * - AZURE_DEVOPS_TEAMS=Frontend,Backend,Mobile (multiple teams within project)
20
- *
21
- * Auto-creates sync profiles for each project/repo.
22
- *
23
- * NOTE: Azure DevOps uses ONE project per organization with multiple teams.
24
- * Multi-project support is for Jira and GitHub only.
25
- */
26
- // ============================================================================
27
- // Parsing Functions
28
- // ============================================================================
29
- /**
30
- * Parse comma-separated list from environment variable
31
- *
32
- * Handles:
33
- * - Comma separation
34
- * - Whitespace trimming
35
- * - Empty values filtering
36
- *
37
- * @example
38
- * parseCommaSeparated("BACKEND, FRONTEND, MOBILE")
39
- * // Returns: ["BACKEND", "FRONTEND", "MOBILE"]
40
- */
41
- export function parseCommaSeparated(value) {
42
- if (!value) {
43
- return [];
44
- }
45
- return value
46
- .split(',')
47
- .map((item) => item.trim())
48
- .filter((item) => item.length > 0);
49
- }
50
- /**
51
- * Parse JIRA project keys from .env
52
- *
53
- * @deprecated (v0.34.0+) Use ConfigManager for domain configuration.
54
- * JIRA_DOMAIN should be in config.json, not .env
55
- *
56
- * Reads:
57
- * - JIRA_PROJECT_KEYS (comma-separated, NEW format)
58
- * - JIRA_PROJECT_KEY (single, legacy format)
59
- * - JIRA_DOMAIN, JIRA_EMAIL, JIRA_API_TOKEN (shared credentials)
60
- *
61
- * @returns Array of JIRA project configurations
62
- *
63
- * @example
64
- * // .env:
65
- * // JIRA_PROJECT_KEYS=BACKEND,FRONTEND,MOBILE
66
- * // JIRA_DOMAIN=mycompany.atlassian.net
67
- * // JIRA_EMAIL=user@example.com
68
- * // JIRA_API_TOKEN=abc123
69
- *
70
- * parseJiraProjects()
71
- * // Returns: [
72
- * // { projectKey: "BACKEND", domain: "...", email: "...", apiToken: "..." },
73
- * // { projectKey: "FRONTEND", ... },
74
- * // { projectKey: "MOBILE", ... }
75
- * // ]
76
- */
77
- export function parseJiraProjects() {
78
- // DEPRECATED: Domain should come from ConfigManager (config.json)
79
- const domain = process.env.JIRA_DOMAIN;
80
- if (domain) {
81
- console.warn('DEPRECATED: JIRA_DOMAIN in .env - migrate to config.json (issueTracker.domain)');
82
- }
83
- const email = process.env.JIRA_EMAIL;
84
- const apiToken = process.env.JIRA_API_TOKEN;
85
- // Missing credentials → return empty array
86
- if (!domain || !email || !apiToken) {
87
- return [];
88
- }
89
- // Try new format (JIRA_PROJECT_KEYS - plural)
90
- const projectKeysStr = process.env.JIRA_PROJECT_KEYS;
91
- if (projectKeysStr) {
92
- const projectKeys = parseCommaSeparated(projectKeysStr);
93
- return projectKeys.map((projectKey) => ({
94
- projectKey,
95
- domain,
96
- email,
97
- apiToken,
98
- }));
99
- }
100
- // Fallback to legacy format (JIRA_PROJECT_KEY - singular)
101
- const legacyProjectKey = process.env.JIRA_PROJECT_KEY;
102
- if (legacyProjectKey) {
103
- return [
104
- {
105
- projectKey: legacyProjectKey.trim(),
106
- domain,
107
- email,
108
- apiToken,
109
- },
110
- ];
111
- }
112
- return [];
113
- }
114
- /**
115
- * Parse GitHub repositories from .env
116
- *
117
- * Reads:
118
- * - GITHUB_REPOS (comma-separated, format: "owner/repo,owner2/repo2")
119
- * - GITHUB_TOKEN (shared credential)
120
- *
121
- * @returns Array of GitHub repository configurations
122
- *
123
- * @example
124
- * // .env:
125
- * // GITHUB_REPOS=myorg/backend-api,myorg/frontend-web
126
- * // GITHUB_TOKEN=ghp_abc123
127
- *
128
- * parseGitHubRepos()
129
- * // Returns: [
130
- * // { owner: "myorg", repo: "backend-api", token: "..." },
131
- * // { owner: "myorg", repo: "frontend-web", token: "..." }
132
- * // ]
133
- */
134
- export function parseGitHubRepos() {
135
- const token = process.env.GITHUB_TOKEN;
136
- // Missing token → return empty array
137
- if (!token) {
138
- return [];
139
- }
140
- // Parse GITHUB_REPOS (comma-separated "owner/repo" format)
141
- const reposStr = process.env.GITHUB_REPOS;
142
- if (!reposStr) {
143
- return [];
144
- }
145
- const repos = parseCommaSeparated(reposStr);
146
- const configs = [];
147
- for (const repo of repos) {
148
- // Format: "owner/repo"
149
- const parts = repo.split('/');
150
- if (parts.length === 2) {
151
- configs.push({
152
- owner: parts[0].trim(),
153
- repo: parts[1].trim(),
154
- token,
155
- });
156
- }
157
- }
158
- return configs;
159
- }
160
- /**
161
- * Parse Azure DevOps project from .env
162
- *
163
- * @deprecated (v0.34.0+) Use ConfigManager for organization/project configuration.
164
- * AZURE_DEVOPS_ORG and AZURE_DEVOPS_PROJECT should be in config.json
165
- *
166
- * NOTE: Azure DevOps supports ONE project per organization with multiple teams.
167
- * For multi-team support, use AZURE_DEVOPS_TEAMS (comma-separated).
168
- *
169
- * Reads:
170
- * - AZURE_DEVOPS_PROJECT (single project, required)
171
- * - AZURE_DEVOPS_ORG (organization)
172
- * - AZURE_DEVOPS_PAT (credential)
173
- * - AZURE_DEVOPS_TEAMS (comma-separated teams, optional)
174
- *
175
- * @returns Array with single ADO project configuration
176
- *
177
- * @example
178
- * // .env:
179
- * // AZURE_DEVOPS_PROJECT=myproject
180
- * // AZURE_DEVOPS_ORG=easychamp
181
- * // AZURE_DEVOPS_PAT=xyz789
182
- * // AZURE_DEVOPS_TEAMS=Frontend,Backend,Mobile
183
- *
184
- * parseAdoProjects()
185
- * // Returns: [
186
- * // { organization: "easychamp", project: "myproject", pat: "..." }
187
- * // ]
188
- */
189
- export function parseAdoProjects() {
190
- // DEPRECATED: Org and project should come from ConfigManager (config.json)
191
- const organization = process.env.AZURE_DEVOPS_ORG;
192
- const project = process.env.AZURE_DEVOPS_PROJECT;
193
- if (organization || project) {
194
- console.warn('DEPRECATED: AZURE_DEVOPS_ORG/PROJECT in .env - migrate to config.json (issueTracker.organization_ado)');
195
- }
196
- const pat = process.env.AZURE_DEVOPS_PAT;
197
- // Missing credentials → return empty array
198
- if (!organization || !pat || !project) {
199
- return [];
200
- }
201
- // ADO only supports ONE project per organization
202
- return [
203
- {
204
- organization,
205
- project: project.trim(),
206
- pat,
207
- },
208
- ];
209
- }
210
- /**
211
- * Parse all multi-project configurations from .env
212
- *
213
- * @returns All detected project configurations
214
- */
215
- export function parseMultiProjectEnv() {
216
- return {
217
- jira: parseJiraProjects(),
218
- github: parseGitHubRepos(),
219
- ado: parseAdoProjects(),
220
- };
221
- }
222
- // ============================================================================
223
- // Sync Profile Generation
224
- // ============================================================================
225
- /**
226
- * Generate sync profile ID from project identifier
227
- *
228
- * Format: "{provider}-{project-id}"
229
- *
230
- * @example
231
- * generateProfileId("jira", "BACKEND")
232
- * // Returns: "jira-backend"
233
- *
234
- * @example
235
- * generateProfileId("github", "backend-api")
236
- * // Returns: "github-backend-api"
237
- */
238
- export function generateProfileId(provider, projectId) {
239
- return `${provider}-${projectId.toLowerCase().replace(/[^a-z0-9-]/g, '-')}`;
240
- }
241
- /**
242
- * Create JIRA sync profile from project configuration
243
- */
244
- export function createJiraSyncProfile(config) {
245
- const profileId = generateProfileId('jira', config.projectKey);
246
- return {
247
- id: profileId,
248
- profile: {
249
- provider: 'jira',
250
- displayName: `JIRA ${config.projectKey}`,
251
- description: `Auto-created from JIRA_PROJECT_KEYS (project: ${config.projectKey})`,
252
- config: {
253
- domain: config.domain,
254
- projectKey: config.projectKey,
255
- },
256
- timeRange: {
257
- default: '1M',
258
- max: '3M',
259
- },
260
- rateLimits: {
261
- maxItemsPerSync: 200,
262
- warnThreshold: 50,
263
- },
264
- },
265
- };
266
- }
267
- /**
268
- * Create GitHub sync profile from repository configuration
269
- */
270
- export function createGitHubSyncProfile(config) {
271
- const profileId = generateProfileId('github', config.repo);
272
- return {
273
- id: profileId,
274
- profile: {
275
- provider: 'github',
276
- displayName: `${config.owner}/${config.repo}`,
277
- description: `Auto-created from GITHUB_REPOS`,
278
- config: {
279
- owner: config.owner,
280
- repo: config.repo,
281
- },
282
- timeRange: {
283
- default: '1M',
284
- max: '6M',
285
- },
286
- rateLimits: {
287
- maxItemsPerSync: 500,
288
- warnThreshold: 100,
289
- },
290
- },
291
- };
292
- }
293
- /**
294
- * Create Azure DevOps sync profile from project configuration
295
- *
296
- * NOTE: Creates ONE profile per organization. Teams are managed within the project.
297
- */
298
- export function createAdoSyncProfile(config) {
299
- const profileId = generateProfileId('ado', config.project);
300
- return {
301
- id: profileId,
302
- profile: {
303
- provider: 'ado',
304
- displayName: `${config.organization}/${config.project}`,
305
- description: `Auto-created from AZURE_DEVOPS_PROJECT (single project with teams)`,
306
- config: {
307
- organization: config.organization,
308
- project: config.project,
309
- },
310
- timeRange: {
311
- default: '1M',
312
- max: '1Y',
313
- },
314
- rateLimits: {
315
- maxItemsPerSync: 500,
316
- warnThreshold: 100,
317
- },
318
- },
319
- };
320
- }
321
- /**
322
- * Generate all sync profiles from environment variables
323
- *
324
- * @returns Array of { id, profile } objects ready to be created
325
- *
326
- * @example
327
- * // .env:
328
- * // JIRA_PROJECT_KEYS=BACKEND,FRONTEND
329
- * // GITHUB_REPOS=myorg/backend-api
330
- *
331
- * const profiles = generateSyncProfilesFromEnv();
332
- * // Returns: [
333
- * // { id: "jira-backend", profile: { ... } },
334
- * // { id: "jira-frontend", profile: { ... } },
335
- * // { id: "github-backend-api", profile: { ... } }
336
- * // ]
337
- */
338
- export function generateSyncProfilesFromEnv() {
339
- const envConfig = parseMultiProjectEnv();
340
- const profiles = [];
341
- // JIRA profiles
342
- for (const jiraConfig of envConfig.jira) {
343
- profiles.push(createJiraSyncProfile(jiraConfig));
344
- }
345
- // GitHub profiles
346
- for (const githubConfig of envConfig.github) {
347
- profiles.push(createGitHubSyncProfile(githubConfig));
348
- }
349
- // Azure DevOps profiles
350
- for (const adoConfig of envConfig.ado) {
351
- profiles.push(createAdoSyncProfile(adoConfig));
352
- }
353
- return profiles;
354
- }
355
- // ============================================================================
356
- // Validation
357
- // ============================================================================
358
- /**
359
- * Validate JIRA project key format
360
- *
361
- * Rules:
362
- * - 2-10 characters
363
- * - Uppercase letters only
364
- * - No special characters
365
- *
366
- * @example
367
- * validateJiraProjectKey("BACKEND") // → true
368
- * validateJiraProjectKey("backend") // → "Must be uppercase"
369
- * validateJiraProjectKey("BACK") // → true
370
- * validateJiraProjectKey("B") // → "Must be 2-10 characters"
371
- */
372
- export function validateJiraProjectKey(key) {
373
- if (!key) {
374
- return 'Project key is required';
375
- }
376
- if (!/^[A-Z0-9]+$/.test(key)) {
377
- return 'Project key must be uppercase letters and numbers only (e.g., BACKEND, PROJ1)';
378
- }
379
- if (key.length < 2 || key.length > 10) {
380
- return 'Project key must be 2-10 characters';
381
- }
382
- return true;
383
- }
384
- /**
385
- * Validate GitHub repo format
386
- *
387
- * Format: "owner/repo"
388
- *
389
- * @example
390
- * validateGitHubRepo("myorg/backend-api") // → true
391
- * validateGitHubRepo("backend-api") // → "Must be owner/repo format"
392
- */
393
- export function validateGitHubRepo(repo) {
394
- if (!repo) {
395
- return 'Repository is required';
396
- }
397
- const parts = repo.split('/');
398
- if (parts.length !== 2) {
399
- return 'Repository must be in "owner/repo" format (e.g., myorg/backend-api)';
400
- }
401
- const [owner, repoName] = parts;
402
- if (!owner || !repoName) {
403
- return 'Both owner and repo name are required';
404
- }
405
- if (!/^[a-zA-Z0-9-_.]+$/.test(owner) || !/^[a-zA-Z0-9-_.]+$/.test(repoName)) {
406
- return 'Owner and repo name can only contain letters, numbers, hyphens, underscores, and dots';
407
- }
408
- return true;
409
- }
410
- /**
411
- * Validate Azure DevOps project name
412
- *
413
- * @example
414
- * validateAdoProject("backend-api") // → true
415
- * validateAdoProject("") // → "Project name is required"
416
- */
417
- export function validateAdoProject(project) {
418
- if (!project) {
419
- return 'Project name is required';
420
- }
421
- if (project.length < 2 || project.length > 64) {
422
- return 'Project name must be 2-64 characters';
423
- }
424
- return true;
425
- }
426
- //# sourceMappingURL=env-multi-project-parser.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"env-multi-project-parser.js","sourceRoot":"","sources":["../../../src/utils/env-multi-project-parser.ts"],"names":[],"mappings":"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"}
@@ -1,36 +0,0 @@
1
- /**
2
- * Bidirectional GitHub Sync
3
- *
4
- * Syncs state from GitHub back to SpecWeave.
5
- * Handles issue state changes, comments, assignees, labels, milestones.
6
- *
7
- * @module github-sync-bidirectional
8
- */
9
- export interface GitHubIssueState {
10
- number: number;
11
- title: string;
12
- body: string;
13
- state: 'open' | 'closed';
14
- labels: string[];
15
- assignees: string[];
16
- milestone?: string;
17
- comments: GitHubComment[];
18
- updated_at: string;
19
- }
20
- export interface GitHubComment {
21
- id: number;
22
- author: string;
23
- body: string;
24
- created_at: string;
25
- }
26
- export interface SyncConflict {
27
- type: 'status' | 'assignee' | 'label';
28
- githubValue: any;
29
- specweaveValue: any;
30
- resolution: 'github-wins' | 'specweave-wins' | 'prompt';
31
- }
32
- /**
33
- * Sync from GitHub to SpecWeave
34
- */
35
- export declare function syncFromGitHub(incrementId: string): Promise<void>;
36
- //# sourceMappingURL=github-sync-bidirectional.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"github-sync-bidirectional.d.ts","sourceRoot":"","sources":["github-sync-bidirectional.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;IACtC,WAAW,EAAE,GAAG,CAAC;IACjB,cAAc,EAAE,GAAG,CAAC;IACpB,UAAU,EAAE,aAAa,GAAG,gBAAgB,GAAG,QAAQ,CAAC;CACzD;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmDvE"}
@@ -1,206 +0,0 @@
1
- import * as fs from "../../../src/utils/fs-native.js";
2
- import path from "path";
3
- import { execFileNoThrow } from "../../../src/utils/execFileNoThrow.js";
4
- import {
5
- loadIncrementMetadata,
6
- detectRepo
7
- } from "./github-issue-updater.js";
8
- import { getGitHubAuthFromProject } from "../../../src/utils/auth-helpers.js";
9
- function getGhEnv() {
10
- const { token } = getGitHubAuthFromProject(process.cwd());
11
- return token ? { ...process.env, GH_TOKEN: token } : process.env;
12
- }
13
- async function syncFromGitHub(incrementId) {
14
- console.log(`
15
- \u{1F504} Syncing from GitHub for increment: ${incrementId}`);
16
- try {
17
- const metadata = await loadIncrementMetadata(incrementId);
18
- if (!metadata?.github?.issue) {
19
- console.log("\u2139\uFE0F No GitHub issue linked, nothing to sync");
20
- return;
21
- }
22
- const repoInfo = await detectRepo();
23
- if (!repoInfo) {
24
- console.log("\u26A0\uFE0F Could not detect GitHub repository");
25
- return;
26
- }
27
- const { owner, repo } = repoInfo;
28
- const issueNumber = metadata.github.issue;
29
- console.log(` Syncing from ${owner}/${repo}#${issueNumber}`);
30
- const githubState = await fetchGitHubIssueState(issueNumber, owner, repo);
31
- const conflicts = detectConflicts(metadata, githubState);
32
- if (conflicts.length === 0) {
33
- console.log("\u2705 No conflicts - GitHub and SpecWeave in sync");
34
- return;
35
- }
36
- console.log(`\u26A0\uFE0F Detected ${conflicts.length} conflict(s)`);
37
- await resolveConflicts(incrementId, metadata, githubState, conflicts);
38
- await syncComments(incrementId, githubState.comments);
39
- await updateMetadata(incrementId, githubState);
40
- console.log("\u2705 Three-permission sync complete");
41
- } catch (error) {
42
- console.error("\u274C Error syncing from GitHub:", error);
43
- throw error;
44
- }
45
- }
46
- async function fetchGitHubIssueState(issueNumber, owner, repo) {
47
- const issueResult = await execFileNoThrow("gh", [
48
- "issue",
49
- "view",
50
- String(issueNumber),
51
- "--repo",
52
- `${owner}/${repo}`,
53
- "--json",
54
- "number,title,body,state,labels,assignees,milestone,updatedAt"
55
- ], { env: getGhEnv() });
56
- if (issueResult.exitCode !== 0) {
57
- throw new Error(`Failed to fetch issue: ${issueResult.stderr}`);
58
- }
59
- const issue = JSON.parse(issueResult.stdout);
60
- const commentsResult = await execFileNoThrow("gh", [
61
- "api",
62
- `repos/${owner}/${repo}/issues/${issueNumber}/comments`,
63
- "--jq",
64
- ".[] | {id: .id, author: .user.login, body: .body, created_at: .created_at}"
65
- ], { env: getGhEnv() });
66
- let comments = [];
67
- if (commentsResult.exitCode === 0 && commentsResult.stdout.trim()) {
68
- const commentLines = commentsResult.stdout.trim().split("\n");
69
- comments = commentLines.map((line) => JSON.parse(line));
70
- }
71
- return {
72
- number: issue.number,
73
- title: issue.title,
74
- body: issue.body,
75
- state: issue.state,
76
- labels: issue.labels?.map((l) => l.name) || [],
77
- assignees: issue.assignees?.map((a) => a.login) || [],
78
- milestone: issue.milestone?.title,
79
- comments,
80
- updated_at: issue.updatedAt
81
- };
82
- }
83
- function detectConflicts(metadata, githubState) {
84
- const conflicts = [];
85
- const specweaveStatus = metadata.status;
86
- const githubStatus = githubState.state;
87
- const expectedGitHubStatus = mapSpecWeaveStatusToGitHub(specweaveStatus);
88
- if (githubStatus !== expectedGitHubStatus) {
89
- conflicts.push({
90
- type: "status",
91
- githubValue: githubStatus,
92
- specweaveValue: specweaveStatus,
93
- resolution: "prompt"
94
- // Ask user
95
- });
96
- }
97
- return conflicts;
98
- }
99
- async function resolveConflicts(incrementId, metadata, githubState, conflicts) {
100
- for (const conflict of conflicts) {
101
- console.log(`
102
- \u26A0\uFE0F Conflict detected: ${conflict.type}`);
103
- console.log(` GitHub: ${conflict.githubValue}`);
104
- console.log(` SpecWeave: ${conflict.specweaveValue}`);
105
- if (conflict.type === "status") {
106
- await resolveStatusConflict(incrementId, metadata, githubState);
107
- }
108
- }
109
- }
110
- async function resolveStatusConflict(incrementId, metadata, githubState) {
111
- const specweaveStatus = metadata.status;
112
- const githubStatus = githubState.state;
113
- if (githubStatus === "closed" && specweaveStatus === "active") {
114
- console.log(`
115
- \u26A0\uFE0F **CONFLICT**: GitHub issue closed but SpecWeave increment still active!`);
116
- console.log(` Recommendation: Run /sw:done ${incrementId} to close increment`);
117
- console.log(` Or reopen issue on GitHub if work is not complete`);
118
- }
119
- if (githubStatus === "open" && specweaveStatus === "completed") {
120
- console.log(`
121
- \u26A0\uFE0F **CONFLICT**: SpecWeave increment completed but GitHub issue still open!`);
122
- console.log(` Recommendation: Close GitHub issue #${metadata.github.issue}`);
123
- }
124
- if (githubStatus === "open" && specweaveStatus === "paused") {
125
- console.log(`
126
- \u2139\uFE0F GitHub issue open, SpecWeave increment paused (OK)`);
127
- }
128
- if (githubStatus === "open" && specweaveStatus === "abandoned") {
129
- console.log(`
130
- \u26A0\uFE0F **CONFLICT**: SpecWeave increment abandoned but GitHub issue still open!`);
131
- console.log(` Recommendation: Close GitHub issue #${metadata.github.issue} with reason`);
132
- }
133
- }
134
- async function syncComments(incrementId, comments) {
135
- if (comments.length === 0) {
136
- console.log("\u2139\uFE0F No comments to sync");
137
- return;
138
- }
139
- const commentsPath = path.join(
140
- process.cwd(),
141
- ".specweave/increments",
142
- incrementId,
143
- "logs/github-comments.md"
144
- );
145
- await fs.ensureFile(commentsPath);
146
- let existingContent = "";
147
- if (await fs.pathExists(commentsPath)) {
148
- existingContent = await fs.readFile(commentsPath, "utf-8");
149
- }
150
- const existingIds = /* @__PURE__ */ new Set();
151
- const idMatches = existingContent.matchAll(/<!-- comment-id: (\d+) -->/g);
152
- for (const match of idMatches) {
153
- existingIds.add(parseInt(match[1], 10));
154
- }
155
- const newComments = comments.filter((c) => !existingIds.has(c.id));
156
- if (newComments.length === 0) {
157
- console.log("\u2139\uFE0F All comments already synced");
158
- return;
159
- }
160
- console.log(`\u{1F4DD} Syncing ${newComments.length} new comment(s)`);
161
- const commentsMarkdown = newComments.map((comment) => `
162
- ---
163
-
164
- <!-- comment-id: ${comment.id} -->
165
-
166
- **Author**: @${comment.author}
167
- **Date**: ${new Date(comment.created_at).toLocaleString()}
168
-
169
- ${comment.body}
170
- `.trim()).join("\n\n");
171
- await fs.appendFile(
172
- commentsPath,
173
- (existingContent ? "\n\n" : "") + commentsMarkdown
174
- );
175
- console.log(`\u2705 Comments saved to: logs/github-comments.md`);
176
- }
177
- async function updateMetadata(incrementId, githubState) {
178
- const metadataPath = path.join(
179
- process.cwd(),
180
- ".specweave/increments",
181
- incrementId,
182
- "metadata.json"
183
- );
184
- const metadata = await fs.readJson(metadataPath);
185
- metadata.github = metadata.github || {};
186
- metadata.github.synced = (/* @__PURE__ */ new Date()).toISOString();
187
- metadata.github.lastUpdated = githubState.updated_at;
188
- metadata.github.state = githubState.state;
189
- await fs.writeJson(metadataPath, metadata, { spaces: 2 });
190
- console.log("\u2705 Metadata updated");
191
- }
192
- function mapSpecWeaveStatusToGitHub(status) {
193
- switch (status) {
194
- case "completed":
195
- case "abandoned":
196
- return "closed";
197
- case "active":
198
- case "paused":
199
- case "planning":
200
- default:
201
- return "open";
202
- }
203
- }
204
- export {
205
- syncFromGitHub
206
- };