specweave 0.8.17 ā 0.8.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +119 -27
- package/README.md +11 -3
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +74 -20
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/migrate-to-profiles.js +2 -2
- package/dist/cli/commands/migrate-to-profiles.js.map +1 -1
- package/dist/cli/helpers/issue-tracker/index.d.ts.map +1 -1
- package/dist/cli/helpers/issue-tracker/index.js +8 -3
- package/dist/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/cli/helpers/issue-tracker/utils.d.ts +5 -2
- package/dist/cli/helpers/issue-tracker/utils.d.ts.map +1 -1
- package/dist/cli/helpers/issue-tracker/utils.js +13 -6
- package/dist/cli/helpers/issue-tracker/utils.js.map +1 -1
- package/dist/core/sync/bidirectional-engine.d.ts +110 -0
- package/dist/core/sync/bidirectional-engine.d.ts.map +1 -0
- package/dist/core/sync/bidirectional-engine.js +356 -0
- package/dist/core/sync/bidirectional-engine.js.map +1 -0
- package/dist/utils/agents-md-compiler.js +2 -2
- package/dist/utils/env-multi-project-parser.d.ts +210 -0
- package/dist/utils/env-multi-project-parser.d.ts.map +1 -0
- package/dist/utils/env-multi-project-parser.js +406 -0
- package/dist/utils/env-multi-project-parser.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/hooks/post-first-increment.sh +79 -0
- package/plugins/specweave/skills/increment-planner/SKILL.md +50 -16
- package/plugins/specweave/skills/plugin-expert/SKILL.md +344 -0
- package/plugins/specweave/skills/specweave-framework/SKILL.md +2 -2
- package/plugins/specweave/skills/translator/SKILL.md +29 -0
- package/plugins/specweave/skills/translator/SKILL.md.bak +172 -0
- package/plugins/specweave-jira/lib/project-selector.ts +323 -0
- package/plugins/specweave-jira/lib/reorganization-detector.ts +359 -0
- package/plugins/specweave-jira/lib/setup-wizard.ts +256 -0
- package/src/templates/.gitignore.template +1 -0
- package/plugins/specweave-jira/lib/jira-client-v2.ts +0 -529
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bidirectional Sync Engine
|
|
3
|
+
*
|
|
4
|
+
* Handles synchronization between SpecWeave increments and external systems
|
|
5
|
+
* (GitHub, Jira, ADO) in both directions with conflict detection and resolution.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Bidirectional sync (to-external, from-external, both)
|
|
9
|
+
* - Conflict detection (field-level granularity)
|
|
10
|
+
* - Interactive conflict resolution
|
|
11
|
+
* - Change tracking (detect what changed since last sync)
|
|
12
|
+
* - Reorganization detection (moved issues, split/merged stories)
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'fs-extra';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import inquirer from 'inquirer';
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Bidirectional Sync Engine
|
|
19
|
+
// ============================================================================
|
|
20
|
+
export class BidirectionalSyncEngine {
|
|
21
|
+
constructor(provider, projectRoot = process.cwd()) {
|
|
22
|
+
this.provider = provider;
|
|
23
|
+
this.projectRoot = projectRoot;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Main sync orchestrator
|
|
27
|
+
*/
|
|
28
|
+
async sync(incrementId, direction, conflictResolution = 'prompt') {
|
|
29
|
+
console.log(`\nš Starting ${direction} sync for increment ${incrementId}...\n`);
|
|
30
|
+
const result = {
|
|
31
|
+
success: false,
|
|
32
|
+
direction,
|
|
33
|
+
changes: { toExternal: [], fromExternal: [], conflicts: [] },
|
|
34
|
+
conflictsResolved: 0,
|
|
35
|
+
errors: [],
|
|
36
|
+
};
|
|
37
|
+
try {
|
|
38
|
+
// Step 1: Load current state
|
|
39
|
+
const currentState = await this.loadSyncState(incrementId);
|
|
40
|
+
// Step 2: Detect changes
|
|
41
|
+
const changes = await this.detectChanges(incrementId, currentState);
|
|
42
|
+
result.changes = changes;
|
|
43
|
+
// Step 3: Handle conflicts (if bidirectional)
|
|
44
|
+
if (direction === 'bidirectional' && changes.conflicts.length > 0) {
|
|
45
|
+
console.log(`\nā ļø Detected ${changes.conflicts.length} conflicts\n`);
|
|
46
|
+
const resolved = await this.resolveConflicts(changes.conflicts, conflictResolution);
|
|
47
|
+
result.conflictsResolved = resolved.length;
|
|
48
|
+
// Apply resolutions
|
|
49
|
+
for (const conflict of resolved) {
|
|
50
|
+
if (conflict.resolution === 'local') {
|
|
51
|
+
changes.toExternal.push(conflict);
|
|
52
|
+
}
|
|
53
|
+
else if (conflict.resolution === 'external') {
|
|
54
|
+
changes.fromExternal.push(conflict);
|
|
55
|
+
}
|
|
56
|
+
else if (conflict.resolution === 'merged' && conflict.mergedValue !== undefined) {
|
|
57
|
+
// Apply merged value to both sides
|
|
58
|
+
changes.toExternal.push({
|
|
59
|
+
...conflict,
|
|
60
|
+
localValue: conflict.mergedValue,
|
|
61
|
+
});
|
|
62
|
+
changes.fromExternal.push({
|
|
63
|
+
...conflict,
|
|
64
|
+
externalValue: conflict.mergedValue,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Step 4: Apply changes based on direction
|
|
70
|
+
if (direction === 'to-external' || direction === 'bidirectional') {
|
|
71
|
+
if (changes.toExternal.length > 0) {
|
|
72
|
+
console.log(`\nš¤ Syncing ${changes.toExternal.length} changes to ${this.provider}...`);
|
|
73
|
+
await this.applyToExternal(incrementId, changes.toExternal);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (direction === 'from-external' || direction === 'bidirectional') {
|
|
77
|
+
if (changes.fromExternal.length > 0) {
|
|
78
|
+
console.log(`\nš„ Syncing ${changes.fromExternal.length} changes from ${this.provider}...`);
|
|
79
|
+
await this.applyToLocal(incrementId, changes.fromExternal);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Step 5: Update sync state
|
|
83
|
+
await this.updateSyncState(incrementId, {
|
|
84
|
+
...currentState,
|
|
85
|
+
lastSyncAt: new Date().toISOString(),
|
|
86
|
+
lastLocalChange: new Date().toISOString(),
|
|
87
|
+
lastExternalChange: new Date().toISOString(),
|
|
88
|
+
});
|
|
89
|
+
result.success = true;
|
|
90
|
+
console.log(`\nā
Sync complete!\n`);
|
|
91
|
+
this.printSyncSummary(result);
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
result.success = false;
|
|
96
|
+
result.errors.push(error.message);
|
|
97
|
+
console.error(`\nā Sync failed:`, error.message);
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// ==========================================================================
|
|
102
|
+
// Change Detection
|
|
103
|
+
// ==========================================================================
|
|
104
|
+
/**
|
|
105
|
+
* Detect changes in both local and external state
|
|
106
|
+
*/
|
|
107
|
+
async detectChanges(incrementId, currentState) {
|
|
108
|
+
const localState = await this.fetchLocalState(incrementId);
|
|
109
|
+
const externalState = await this.fetchExternalState(incrementId, currentState);
|
|
110
|
+
const toExternal = [];
|
|
111
|
+
const fromExternal = [];
|
|
112
|
+
const conflicts = [];
|
|
113
|
+
// Compare common fields
|
|
114
|
+
const fields = ['title', 'description', 'status', 'priority', 'assignee'];
|
|
115
|
+
for (const field of fields) {
|
|
116
|
+
const localValue = localState[field];
|
|
117
|
+
const externalValue = externalState[field];
|
|
118
|
+
const lastSyncValue = currentState.lastSyncAt
|
|
119
|
+
? currentState[field]
|
|
120
|
+
: undefined;
|
|
121
|
+
// Detect changes
|
|
122
|
+
const localChanged = localValue !== lastSyncValue;
|
|
123
|
+
const externalChanged = externalValue !== lastSyncValue;
|
|
124
|
+
if (localChanged && externalChanged && localValue !== externalValue) {
|
|
125
|
+
// CONFLICT: Both changed to different values
|
|
126
|
+
conflicts.push({
|
|
127
|
+
field,
|
|
128
|
+
localValue,
|
|
129
|
+
externalValue,
|
|
130
|
+
lastSyncValue,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
else if (localChanged) {
|
|
134
|
+
// Local changed, external didn't
|
|
135
|
+
toExternal.push({
|
|
136
|
+
field,
|
|
137
|
+
localValue,
|
|
138
|
+
externalValue,
|
|
139
|
+
lastSyncValue,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
else if (externalChanged) {
|
|
143
|
+
// External changed, local didn't
|
|
144
|
+
fromExternal.push({
|
|
145
|
+
field,
|
|
146
|
+
localValue,
|
|
147
|
+
externalValue,
|
|
148
|
+
lastSyncValue,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { toExternal, fromExternal, conflicts };
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Fetch local state from increment files
|
|
156
|
+
*/
|
|
157
|
+
async fetchLocalState(incrementId) {
|
|
158
|
+
const incrementPath = path.join(this.projectRoot, '.specweave', 'increments', incrementId);
|
|
159
|
+
// Read spec.md
|
|
160
|
+
const specPath = path.join(incrementPath, 'spec.md');
|
|
161
|
+
const specContent = await fs.readFile(specPath, 'utf-8');
|
|
162
|
+
// Parse frontmatter and content
|
|
163
|
+
const titleMatch = specContent.match(/^#\s+(.+)$/m);
|
|
164
|
+
const statusMatch = specContent.match(/Status:\s*(.+)$/m);
|
|
165
|
+
const priorityMatch = specContent.match(/Priority:\s*(.+)$/m);
|
|
166
|
+
return {
|
|
167
|
+
title: titleMatch ? titleMatch[1] : '',
|
|
168
|
+
description: specContent.substring(0, 500), // First 500 chars
|
|
169
|
+
status: statusMatch ? statusMatch[1].trim() : 'in-progress',
|
|
170
|
+
priority: priorityMatch ? priorityMatch[1].trim() : 'medium',
|
|
171
|
+
assignee: null,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Fetch external state from Jira/GitHub/ADO
|
|
176
|
+
*/
|
|
177
|
+
async fetchExternalState(incrementId, currentState) {
|
|
178
|
+
if (!currentState.externalKey) {
|
|
179
|
+
return {}; // No external issue yet
|
|
180
|
+
}
|
|
181
|
+
// This will be implemented by provider-specific adapters
|
|
182
|
+
// For now, return placeholder
|
|
183
|
+
return {
|
|
184
|
+
title: '',
|
|
185
|
+
description: '',
|
|
186
|
+
status: '',
|
|
187
|
+
priority: '',
|
|
188
|
+
assignee: null,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
// ==========================================================================
|
|
192
|
+
// Conflict Resolution
|
|
193
|
+
// ==========================================================================
|
|
194
|
+
/**
|
|
195
|
+
* Resolve conflicts interactively or automatically
|
|
196
|
+
*/
|
|
197
|
+
async resolveConflicts(conflicts, strategy) {
|
|
198
|
+
const resolved = [];
|
|
199
|
+
for (const conflict of conflicts) {
|
|
200
|
+
if (strategy === 'prefer-local') {
|
|
201
|
+
conflict.resolution = 'local';
|
|
202
|
+
}
|
|
203
|
+
else if (strategy === 'prefer-external') {
|
|
204
|
+
conflict.resolution = 'external';
|
|
205
|
+
}
|
|
206
|
+
else if (strategy === 'manual' || strategy === 'prompt') {
|
|
207
|
+
await this.promptConflictResolution(conflict);
|
|
208
|
+
}
|
|
209
|
+
resolved.push(conflict);
|
|
210
|
+
}
|
|
211
|
+
return resolved;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Interactive conflict resolution prompt
|
|
215
|
+
*/
|
|
216
|
+
async promptConflictResolution(conflict) {
|
|
217
|
+
console.log(`\nā ļø Conflict in field: ${conflict.field}`);
|
|
218
|
+
console.log(` Local value: ${conflict.localValue}`);
|
|
219
|
+
console.log(` ${this.provider} value: ${conflict.externalValue}`);
|
|
220
|
+
console.log(` Last sync value: ${conflict.lastSyncValue || '(unknown)'}\n`);
|
|
221
|
+
const { resolution } = await inquirer.prompt([
|
|
222
|
+
{
|
|
223
|
+
type: 'list',
|
|
224
|
+
name: 'resolution',
|
|
225
|
+
message: `How should this conflict be resolved?`,
|
|
226
|
+
choices: [
|
|
227
|
+
{
|
|
228
|
+
name: `Use local value: "${conflict.localValue}"`,
|
|
229
|
+
value: 'local',
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: `Use ${this.provider} value: "${conflict.externalValue}"`,
|
|
233
|
+
value: 'external',
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
name: 'Enter custom value (merge)',
|
|
237
|
+
value: 'merge',
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
]);
|
|
242
|
+
if (resolution === 'merge') {
|
|
243
|
+
const { mergedValue } = await inquirer.prompt([
|
|
244
|
+
{
|
|
245
|
+
type: 'input',
|
|
246
|
+
name: 'mergedValue',
|
|
247
|
+
message: `Enter merged value for ${conflict.field}:`,
|
|
248
|
+
default: conflict.localValue,
|
|
249
|
+
},
|
|
250
|
+
]);
|
|
251
|
+
conflict.resolution = 'merged';
|
|
252
|
+
conflict.mergedValue = mergedValue;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
conflict.resolution = resolution;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// ==========================================================================
|
|
259
|
+
// Apply Changes
|
|
260
|
+
// ==========================================================================
|
|
261
|
+
/**
|
|
262
|
+
* Apply changes to external system
|
|
263
|
+
*/
|
|
264
|
+
async applyToExternal(incrementId, changes) {
|
|
265
|
+
// Implementation will be provider-specific
|
|
266
|
+
// This is a placeholder
|
|
267
|
+
for (const change of changes) {
|
|
268
|
+
console.log(` ā ${change.field}: ${change.localValue}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Apply changes to local increment
|
|
273
|
+
*/
|
|
274
|
+
async applyToLocal(incrementId, changes) {
|
|
275
|
+
const incrementPath = path.join(this.projectRoot, '.specweave', 'increments', incrementId);
|
|
276
|
+
const specPath = path.join(incrementPath, 'spec.md');
|
|
277
|
+
let specContent = await fs.readFile(specPath, 'utf-8');
|
|
278
|
+
// Apply changes to spec.md
|
|
279
|
+
for (const change of changes) {
|
|
280
|
+
if (change.field === 'title') {
|
|
281
|
+
specContent = specContent.replace(/^#\s+.+$/m, `# ${change.externalValue}`);
|
|
282
|
+
}
|
|
283
|
+
else if (change.field === 'status') {
|
|
284
|
+
specContent = specContent.replace(/Status:\s*.+$/m, `Status: ${change.externalValue}`);
|
|
285
|
+
}
|
|
286
|
+
else if (change.field === 'priority') {
|
|
287
|
+
specContent = specContent.replace(/Priority:\s*.+$/m, `Priority: ${change.externalValue}`);
|
|
288
|
+
}
|
|
289
|
+
console.log(` ā ${change.field}: ${change.externalValue}`);
|
|
290
|
+
}
|
|
291
|
+
await fs.writeFile(specPath, specContent, 'utf-8');
|
|
292
|
+
}
|
|
293
|
+
// ==========================================================================
|
|
294
|
+
// Sync State Management
|
|
295
|
+
// ==========================================================================
|
|
296
|
+
/**
|
|
297
|
+
* Load sync state from metadata
|
|
298
|
+
*/
|
|
299
|
+
async loadSyncState(incrementId) {
|
|
300
|
+
const metadataPath = path.join(this.projectRoot, '.specweave', 'increments', incrementId, 'metadata.json');
|
|
301
|
+
if (!fs.existsSync(metadataPath)) {
|
|
302
|
+
return { incrementId, title: '', status: '' };
|
|
303
|
+
}
|
|
304
|
+
const metadata = await fs.readJSON(metadataPath);
|
|
305
|
+
return {
|
|
306
|
+
incrementId,
|
|
307
|
+
title: metadata.title || '',
|
|
308
|
+
status: metadata.status || '',
|
|
309
|
+
externalId: metadata.sync?.externalId,
|
|
310
|
+
externalKey: metadata.sync?.externalKey,
|
|
311
|
+
externalUrl: metadata.sync?.externalUrl,
|
|
312
|
+
lastSyncAt: metadata.sync?.lastSyncAt,
|
|
313
|
+
lastLocalChange: metadata.sync?.lastLocalChange,
|
|
314
|
+
lastExternalChange: metadata.sync?.lastExternalChange,
|
|
315
|
+
linkedItems: metadata.sync?.linkedItems || {},
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Update sync state in metadata
|
|
320
|
+
*/
|
|
321
|
+
async updateSyncState(incrementId, state) {
|
|
322
|
+
const metadataPath = path.join(this.projectRoot, '.specweave', 'increments', incrementId, 'metadata.json');
|
|
323
|
+
let metadata = {};
|
|
324
|
+
if (fs.existsSync(metadataPath)) {
|
|
325
|
+
metadata = await fs.readJSON(metadataPath);
|
|
326
|
+
}
|
|
327
|
+
metadata.sync = {
|
|
328
|
+
...metadata.sync,
|
|
329
|
+
externalId: state.externalId,
|
|
330
|
+
externalKey: state.externalKey,
|
|
331
|
+
externalUrl: state.externalUrl,
|
|
332
|
+
lastSyncAt: state.lastSyncAt,
|
|
333
|
+
lastLocalChange: state.lastLocalChange,
|
|
334
|
+
lastExternalChange: state.lastExternalChange,
|
|
335
|
+
linkedItems: state.linkedItems,
|
|
336
|
+
};
|
|
337
|
+
await fs.writeJSON(metadataPath, metadata, { spaces: 2 });
|
|
338
|
+
}
|
|
339
|
+
// ==========================================================================
|
|
340
|
+
// Helpers
|
|
341
|
+
// ==========================================================================
|
|
342
|
+
/**
|
|
343
|
+
* Print sync summary
|
|
344
|
+
*/
|
|
345
|
+
printSyncSummary(result) {
|
|
346
|
+
console.log('š Sync Summary:\n');
|
|
347
|
+
console.log(` Direction: ${result.direction}`);
|
|
348
|
+
console.log(` Changes to external: ${result.changes.toExternal.length}`);
|
|
349
|
+
console.log(` Changes from external: ${result.changes.fromExternal.length}`);
|
|
350
|
+
console.log(` Conflicts detected: ${result.changes.conflicts.length}`);
|
|
351
|
+
console.log(` Conflicts resolved: ${result.conflictsResolved}`);
|
|
352
|
+
console.log(` Errors: ${result.errors.length}`);
|
|
353
|
+
console.log('');
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
//# sourceMappingURL=bidirectional-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bidirectional-engine.js","sourceRoot":"","sources":["../../../src/core/sync/bidirectional-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -257,10 +257,10 @@ This project uses SpecWeave for spec-driven development. SpecWeave provides spec
|
|
|
257
257
|
/plugin marketplace add anton-abyzov/specweave
|
|
258
258
|
|
|
259
259
|
# Install core framework
|
|
260
|
-
/plugin install specweave
|
|
260
|
+
/plugin install specweave
|
|
261
261
|
|
|
262
262
|
# Install GitHub plugin (optional)
|
|
263
|
-
/plugin install specweave-github
|
|
263
|
+
/plugin install specweave-github
|
|
264
264
|
\`\`\`
|
|
265
265
|
|
|
266
266
|
### For Other Tools (Cursor, Copilot, etc.)
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Multi-Project Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses comma-separated project lists from .env:
|
|
5
|
+
* - JIRA_PROJECT_KEYS=BACKEND,FRONTEND,MOBILE
|
|
6
|
+
* - GITHUB_REPOS=owner/backend-api,owner/frontend-web
|
|
7
|
+
* - AZURE_DEVOPS_PROJECTS=backend-api,frontend-web
|
|
8
|
+
*
|
|
9
|
+
* Auto-creates sync profiles for each project.
|
|
10
|
+
*/
|
|
11
|
+
import { SyncProfile } from '../core/types/sync-profile.js';
|
|
12
|
+
export interface JiraProjectConfig {
|
|
13
|
+
projectKey: string;
|
|
14
|
+
domain: string;
|
|
15
|
+
email: string;
|
|
16
|
+
apiToken: string;
|
|
17
|
+
}
|
|
18
|
+
export interface GitHubRepoConfig {
|
|
19
|
+
owner: string;
|
|
20
|
+
repo: string;
|
|
21
|
+
token: string;
|
|
22
|
+
}
|
|
23
|
+
export interface AdoProjectConfig {
|
|
24
|
+
organization: string;
|
|
25
|
+
project: string;
|
|
26
|
+
pat: string;
|
|
27
|
+
}
|
|
28
|
+
export interface MultiProjectEnvConfig {
|
|
29
|
+
jira: JiraProjectConfig[];
|
|
30
|
+
github: GitHubRepoConfig[];
|
|
31
|
+
ado: AdoProjectConfig[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Parse comma-separated list from environment variable
|
|
35
|
+
*
|
|
36
|
+
* Handles:
|
|
37
|
+
* - Comma separation
|
|
38
|
+
* - Whitespace trimming
|
|
39
|
+
* - Empty values filtering
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* parseCommaSeparated("BACKEND, FRONTEND, MOBILE")
|
|
43
|
+
* // Returns: ["BACKEND", "FRONTEND", "MOBILE"]
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseCommaSeparated(value: string | undefined): string[];
|
|
46
|
+
/**
|
|
47
|
+
* Parse JIRA project keys from .env
|
|
48
|
+
*
|
|
49
|
+
* Reads:
|
|
50
|
+
* - JIRA_PROJECT_KEYS (comma-separated, NEW format)
|
|
51
|
+
* - JIRA_PROJECT_KEY (single, legacy format)
|
|
52
|
+
* - JIRA_DOMAIN, JIRA_EMAIL, JIRA_API_TOKEN (shared credentials)
|
|
53
|
+
*
|
|
54
|
+
* @returns Array of JIRA project configurations
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* // .env:
|
|
58
|
+
* // JIRA_PROJECT_KEYS=BACKEND,FRONTEND,MOBILE
|
|
59
|
+
* // JIRA_DOMAIN=mycompany.atlassian.net
|
|
60
|
+
* // JIRA_EMAIL=user@example.com
|
|
61
|
+
* // JIRA_API_TOKEN=abc123
|
|
62
|
+
*
|
|
63
|
+
* parseJiraProjects()
|
|
64
|
+
* // Returns: [
|
|
65
|
+
* // { projectKey: "BACKEND", domain: "...", email: "...", apiToken: "..." },
|
|
66
|
+
* // { projectKey: "FRONTEND", ... },
|
|
67
|
+
* // { projectKey: "MOBILE", ... }
|
|
68
|
+
* // ]
|
|
69
|
+
*/
|
|
70
|
+
export declare function parseJiraProjects(): JiraProjectConfig[];
|
|
71
|
+
/**
|
|
72
|
+
* Parse GitHub repositories from .env
|
|
73
|
+
*
|
|
74
|
+
* Reads:
|
|
75
|
+
* - GITHUB_REPOS (comma-separated, format: "owner/repo,owner2/repo2")
|
|
76
|
+
* - GITHUB_TOKEN (shared credential)
|
|
77
|
+
*
|
|
78
|
+
* @returns Array of GitHub repository configurations
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* // .env:
|
|
82
|
+
* // GITHUB_REPOS=myorg/backend-api,myorg/frontend-web
|
|
83
|
+
* // GITHUB_TOKEN=ghp_abc123
|
|
84
|
+
*
|
|
85
|
+
* parseGitHubRepos()
|
|
86
|
+
* // Returns: [
|
|
87
|
+
* // { owner: "myorg", repo: "backend-api", token: "..." },
|
|
88
|
+
* // { owner: "myorg", repo: "frontend-web", token: "..." }
|
|
89
|
+
* // ]
|
|
90
|
+
*/
|
|
91
|
+
export declare function parseGitHubRepos(): GitHubRepoConfig[];
|
|
92
|
+
/**
|
|
93
|
+
* Parse Azure DevOps projects from .env
|
|
94
|
+
*
|
|
95
|
+
* Reads:
|
|
96
|
+
* - AZURE_DEVOPS_PROJECTS (comma-separated)
|
|
97
|
+
* - AZURE_DEVOPS_ORG (shared organization)
|
|
98
|
+
* - AZURE_DEVOPS_PAT (shared credential)
|
|
99
|
+
*
|
|
100
|
+
* @returns Array of Azure DevOps project configurations
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* // .env:
|
|
104
|
+
* // AZURE_DEVOPS_PROJECTS=backend-api,frontend-web
|
|
105
|
+
* // AZURE_DEVOPS_ORG=easychamp
|
|
106
|
+
* // AZURE_DEVOPS_PAT=xyz789
|
|
107
|
+
*
|
|
108
|
+
* parseAdoProjects()
|
|
109
|
+
* // Returns: [
|
|
110
|
+
* // { organization: "easychamp", project: "backend-api", pat: "..." },
|
|
111
|
+
* // { organization: "easychamp", project: "frontend-web", pat: "..." }
|
|
112
|
+
* // ]
|
|
113
|
+
*/
|
|
114
|
+
export declare function parseAdoProjects(): AdoProjectConfig[];
|
|
115
|
+
/**
|
|
116
|
+
* Parse all multi-project configurations from .env
|
|
117
|
+
*
|
|
118
|
+
* @returns All detected project configurations
|
|
119
|
+
*/
|
|
120
|
+
export declare function parseMultiProjectEnv(): MultiProjectEnvConfig;
|
|
121
|
+
/**
|
|
122
|
+
* Generate sync profile ID from project identifier
|
|
123
|
+
*
|
|
124
|
+
* Format: "{provider}-{project-id}"
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* generateProfileId("jira", "BACKEND")
|
|
128
|
+
* // Returns: "jira-backend"
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* generateProfileId("github", "backend-api")
|
|
132
|
+
* // Returns: "github-backend-api"
|
|
133
|
+
*/
|
|
134
|
+
export declare function generateProfileId(provider: string, projectId: string): string;
|
|
135
|
+
/**
|
|
136
|
+
* Create JIRA sync profile from project configuration
|
|
137
|
+
*/
|
|
138
|
+
export declare function createJiraSyncProfile(config: JiraProjectConfig): {
|
|
139
|
+
id: string;
|
|
140
|
+
profile: Omit<SyncProfile, 'id'>;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Create GitHub sync profile from repository configuration
|
|
144
|
+
*/
|
|
145
|
+
export declare function createGitHubSyncProfile(config: GitHubRepoConfig): {
|
|
146
|
+
id: string;
|
|
147
|
+
profile: Omit<SyncProfile, 'id'>;
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* Create Azure DevOps sync profile from project configuration
|
|
151
|
+
*/
|
|
152
|
+
export declare function createAdoSyncProfile(config: AdoProjectConfig): {
|
|
153
|
+
id: string;
|
|
154
|
+
profile: Omit<SyncProfile, 'id'>;
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* Generate all sync profiles from environment variables
|
|
158
|
+
*
|
|
159
|
+
* @returns Array of { id, profile } objects ready to be created
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* // .env:
|
|
163
|
+
* // JIRA_PROJECT_KEYS=BACKEND,FRONTEND
|
|
164
|
+
* // GITHUB_REPOS=myorg/backend-api
|
|
165
|
+
*
|
|
166
|
+
* const profiles = generateSyncProfilesFromEnv();
|
|
167
|
+
* // Returns: [
|
|
168
|
+
* // { id: "jira-backend", profile: { ... } },
|
|
169
|
+
* // { id: "jira-frontend", profile: { ... } },
|
|
170
|
+
* // { id: "github-backend-api", profile: { ... } }
|
|
171
|
+
* // ]
|
|
172
|
+
*/
|
|
173
|
+
export declare function generateSyncProfilesFromEnv(): Array<{
|
|
174
|
+
id: string;
|
|
175
|
+
profile: Omit<SyncProfile, 'id'>;
|
|
176
|
+
}>;
|
|
177
|
+
/**
|
|
178
|
+
* Validate JIRA project key format
|
|
179
|
+
*
|
|
180
|
+
* Rules:
|
|
181
|
+
* - 2-10 characters
|
|
182
|
+
* - Uppercase letters only
|
|
183
|
+
* - No special characters
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* validateJiraProjectKey("BACKEND") // ā true
|
|
187
|
+
* validateJiraProjectKey("backend") // ā "Must be uppercase"
|
|
188
|
+
* validateJiraProjectKey("BACK") // ā true
|
|
189
|
+
* validateJiraProjectKey("B") // ā "Must be 2-10 characters"
|
|
190
|
+
*/
|
|
191
|
+
export declare function validateJiraProjectKey(key: string): true | string;
|
|
192
|
+
/**
|
|
193
|
+
* Validate GitHub repo format
|
|
194
|
+
*
|
|
195
|
+
* Format: "owner/repo"
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* validateGitHubRepo("myorg/backend-api") // ā true
|
|
199
|
+
* validateGitHubRepo("backend-api") // ā "Must be owner/repo format"
|
|
200
|
+
*/
|
|
201
|
+
export declare function validateGitHubRepo(repo: string): true | string;
|
|
202
|
+
/**
|
|
203
|
+
* Validate Azure DevOps project name
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* validateAdoProject("backend-api") // ā true
|
|
207
|
+
* validateAdoProject("") // ā "Project name is required"
|
|
208
|
+
*/
|
|
209
|
+
export declare function validateAdoProject(project: string): true | string;
|
|
210
|
+
//# sourceMappingURL=env-multi-project-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-multi-project-parser.d.ts","sourceRoot":"","sources":["../../src/utils/env-multi-project-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAM5D,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,iBAAiB,EAAE,CAAC;IAC1B,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,GAAG,EAAE,gBAAgB,EAAE,CAAC;CACzB;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CASvE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,IAAI,iBAAiB,EAAE,CAoCvD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,IAAI,gBAAgB,EAAE,CA8BrD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,IAAI,gBAAgB,EAAE,CAiCrD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,qBAAqB,CAM5D;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,GAAG;IAChE,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;CAClC,CAwBA;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,gBAAgB,GAAG;IACjE,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;CAClC,CAuBA;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,GAAG;IAC9D,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;CAClC,CAwBA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,2BAA2B,IAAI,KAAK,CAAC;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;CAClC,CAAC,CAoBD;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAcjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAqB9D;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAUjE"}
|