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.
- package/bin/fix-marketplace-errors.sh +55 -7
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.js +3 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-sync.js +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-sync.js.map +1 -1
- package/dist/src/cli/commands/import-docs.d.ts +3 -0
- package/dist/src/cli/commands/import-docs.d.ts.map +1 -1
- package/dist/src/cli/commands/import-docs.js +110 -91
- package/dist/src/cli/commands/import-docs.js.map +1 -1
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +16 -7
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/living-docs.js +4 -4
- package/dist/src/cli/commands/living-docs.js.map +1 -1
- package/dist/src/cli/commands/sync-spec-commits.js +1 -1
- package/dist/src/cli/commands/sync-spec-commits.js.map +1 -1
- package/dist/src/cli/commands/sync-spec-content.js +1 -1
- package/dist/src/cli/commands/sync-spec-content.js.map +1 -1
- package/dist/src/cli/helpers/github/increment-profile-selector.js +1 -1
- package/dist/src/cli/helpers/github/increment-profile-selector.js.map +1 -1
- package/dist/src/cli/helpers/github/profile-manager.d.ts +5 -5
- package/dist/src/cli/helpers/github/profile-manager.d.ts.map +1 -1
- package/dist/src/cli/helpers/github/profile-manager.js +24 -24
- package/dist/src/cli/helpers/github/profile-manager.js.map +1 -1
- package/dist/src/cli/helpers/init/brownfield-analysis.js +15 -15
- package/dist/src/cli/helpers/init/brownfield-analysis.js.map +1 -1
- package/dist/src/cli/helpers/init/config-detection.js +1 -1
- package/dist/src/cli/helpers/init/config-detection.js.map +1 -1
- package/dist/src/cli/helpers/init/living-docs-preflight.js +7 -7
- package/dist/src/cli/helpers/init/living-docs-preflight.js.map +1 -1
- package/dist/src/cli/helpers/init/plugin-installer.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/plugin-installer.js +61 -9
- package/dist/src/cli/helpers/init/plugin-installer.js.map +1 -1
- package/dist/src/cli/helpers/init/sync-profile-helpers.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/sync-profile-helpers.js +2 -4
- package/dist/src/cli/helpers/init/sync-profile-helpers.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado-area-selection.d.ts +1 -4
- package/dist/src/cli/helpers/issue-tracker/ado-area-selection.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado-area-selection.js +0 -32
- package/dist/src/cli/helpers/issue-tracker/ado-area-selection.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.js +3 -13
- package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.js +0 -1
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/sync-config-writer.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js +0 -7
- package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/types.d.ts +1 -5
- package/dist/src/cli/helpers/issue-tracker/types.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/types.js.map +1 -1
- package/dist/src/cli/workers/import-worker.js +5 -111
- package/dist/src/cli/workers/import-worker.js.map +1 -1
- package/dist/src/core/background/types.d.ts +3 -0
- package/dist/src/core/background/types.d.ts.map +1 -1
- package/dist/src/core/config/types.d.ts +0 -5
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
- package/dist/src/core/increment/increment-archiver.js +0 -6
- package/dist/src/core/increment/increment-archiver.js.map +1 -1
- package/dist/src/core/increment/increment-utils.d.ts +0 -4
- package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
- package/dist/src/core/increment/increment-utils.js +0 -6
- package/dist/src/core/increment/increment-utils.js.map +1 -1
- package/dist/src/core/living-docs/cross-project-sync.d.ts +0 -13
- package/dist/src/core/living-docs/cross-project-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/cross-project-sync.js +0 -25
- package/dist/src/core/living-docs/cross-project-sync.js.map +1 -1
- package/dist/src/core/living-docs/delivery/delivery-generator.d.ts +58 -0
- package/dist/src/core/living-docs/delivery/delivery-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/delivery/delivery-generator.js +501 -0
- package/dist/src/core/living-docs/delivery/delivery-generator.js.map +1 -0
- package/dist/src/core/living-docs/delivery/index.d.ts +8 -0
- package/dist/src/core/living-docs/delivery/index.d.ts.map +1 -0
- package/dist/src/core/living-docs/delivery/index.js +7 -0
- package/dist/src/core/living-docs/delivery/index.js.map +1 -0
- package/dist/src/core/living-docs/diagrams/index.d.ts +8 -0
- package/dist/src/core/living-docs/diagrams/index.d.ts.map +1 -0
- package/dist/src/core/living-docs/diagrams/index.js +7 -0
- package/dist/src/core/living-docs/diagrams/index.js.map +1 -0
- package/dist/src/core/living-docs/diagrams/mermaid-generator.d.ts +103 -0
- package/dist/src/core/living-docs/diagrams/mermaid-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/diagrams/mermaid-generator.js +515 -0
- package/dist/src/core/living-docs/diagrams/mermaid-generator.js.map +1 -0
- package/dist/src/core/living-docs/enterprise/enterprise-generator.d.ts +85 -0
- package/dist/src/core/living-docs/enterprise/enterprise-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/enterprise/enterprise-generator.js +556 -0
- package/dist/src/core/living-docs/enterprise/enterprise-generator.js.map +1 -0
- package/dist/src/core/living-docs/enterprise/history-analyzer.d.ts +91 -0
- package/dist/src/core/living-docs/enterprise/history-analyzer.d.ts.map +1 -0
- package/dist/src/core/living-docs/enterprise/history-analyzer.js +321 -0
- package/dist/src/core/living-docs/enterprise/history-analyzer.js.map +1 -0
- package/dist/src/core/living-docs/enterprise/index.d.ts +18 -0
- package/dist/src/core/living-docs/enterprise/index.d.ts.map +1 -0
- package/dist/src/core/living-docs/enterprise/index.js +14 -0
- package/dist/src/core/living-docs/enterprise/index.js.map +1 -0
- package/dist/src/core/living-docs/enterprise/relationship-mapper.d.ts +58 -0
- package/dist/src/core/living-docs/enterprise/relationship-mapper.d.ts.map +1 -0
- package/dist/src/core/living-docs/enterprise/relationship-mapper.js +227 -0
- package/dist/src/core/living-docs/enterprise/relationship-mapper.js.map +1 -0
- package/dist/src/core/living-docs/enterprise/spec-loader.d.ts +161 -0
- package/dist/src/core/living-docs/enterprise/spec-loader.d.ts.map +1 -0
- package/dist/src/core/living-docs/enterprise/spec-loader.js +470 -0
- package/dist/src/core/living-docs/enterprise/spec-loader.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts +31 -1
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js +626 -14
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts +8 -0
- package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/index.js +87 -4
- package/dist/src/core/living-docs/intelligent-analyzer/index.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +3 -1
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/living-docs/operations/index.d.ts +8 -0
- package/dist/src/core/living-docs/operations/index.d.ts.map +1 -0
- package/dist/src/core/living-docs/operations/index.js +7 -0
- package/dist/src/core/living-docs/operations/index.js.map +1 -0
- package/dist/src/core/living-docs/operations/ops-generator.d.ts +53 -0
- package/dist/src/core/living-docs/operations/ops-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/operations/ops-generator.js +462 -0
- package/dist/src/core/living-docs/operations/ops-generator.js.map +1 -0
- package/dist/src/core/project/project-manager.d.ts +0 -20
- package/dist/src/core/project/project-manager.d.ts.map +1 -1
- package/dist/src/core/project/project-manager.js +0 -49
- package/dist/src/core/project/project-manager.js.map +1 -1
- package/dist/src/core/repo-structure/prompt-consolidator.d.ts +0 -8
- package/dist/src/core/repo-structure/prompt-consolidator.d.ts.map +1 -1
- package/dist/src/core/repo-structure/prompt-consolidator.js +0 -28
- package/dist/src/core/repo-structure/prompt-consolidator.js.map +1 -1
- package/dist/src/core/sync/profile-manager.d.ts +5 -5
- package/dist/src/core/sync/profile-manager.d.ts.map +1 -1
- package/dist/src/core/sync/profile-manager.js +17 -17
- package/dist/src/core/sync/profile-manager.js.map +1 -1
- package/dist/src/core/types/sync-profile.d.ts +3 -20
- package/dist/src/core/types/sync-profile.d.ts.map +1 -1
- package/dist/src/core/types/sync-profile.js +4 -10
- package/dist/src/core/types/sync-profile.js.map +1 -1
- package/dist/src/integrations/ado/ado-client-factory.d.ts +2 -2
- package/dist/src/integrations/ado/ado-client-factory.js +2 -2
- package/dist/src/utils/project-detection.d.ts +16 -16
- package/dist/src/utils/project-detection.d.ts.map +1 -1
- package/dist/src/utils/project-detection.js +39 -39
- package/dist/src/utils/project-detection.js.map +1 -1
- package/dist/src/utils/validators/ado-validator.d.ts +0 -5
- package/dist/src/utils/validators/ado-validator.d.ts.map +1 -1
- package/dist/src/utils/validators/ado-validator.js +0 -23
- package/dist/src/utils/validators/ado-validator.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/commands/living-docs.md +168 -39
- package/plugins/specweave-github/lib/github-feature-sync.js +1 -1
- package/plugins/specweave-github/lib/github-feature-sync.ts +3 -1
- package/plugins/specweave-github/lib/github-spec-sync.js +1 -1
- package/plugins/specweave-github/lib/github-spec-sync.ts +1 -1
- package/dist/plugins/specweave-github/lib/github-sync-bidirectional.d.ts +0 -45
- package/dist/plugins/specweave-github/lib/github-sync-bidirectional.d.ts.map +0 -1
- package/dist/plugins/specweave-github/lib/github-sync-bidirectional.js +0 -247
- package/dist/plugins/specweave-github/lib/github-sync-bidirectional.js.map +0 -1
- package/dist/src/core/brownfield/analyzer.d.ts +0 -86
- package/dist/src/core/brownfield/analyzer.d.ts.map +0 -1
- package/dist/src/core/brownfield/analyzer.js +0 -365
- package/dist/src/core/brownfield/analyzer.js.map +0 -1
- package/dist/src/core/brownfield/importer.d.ts +0 -76
- package/dist/src/core/brownfield/importer.d.ts.map +0 -1
- package/dist/src/core/brownfield/importer.js +0 -287
- package/dist/src/core/brownfield/importer.js.map +0 -1
- package/dist/src/utils/env-multi-project-parser.d.ts +0 -237
- package/dist/src/utils/env-multi-project-parser.d.ts.map +0 -1
- package/dist/src/utils/env-multi-project-parser.js +0 -426
- package/dist/src/utils/env-multi-project-parser.js.map +0 -1
- package/plugins/specweave-github/lib/github-sync-bidirectional.d.js +0 -0
- package/plugins/specweave-github/lib/github-sync-bidirectional.d.ts +0 -36
- package/plugins/specweave-github/lib/github-sync-bidirectional.d.ts.map +0 -1
- package/plugins/specweave-github/lib/github-sync-bidirectional.js +0 -206
- package/plugins/specweave-github/lib/github-sync-bidirectional.js.map +0 -1
- 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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"}
|
|
File without changes
|
|
@@ -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
|
-
};
|