specweave 0.16.7 → 0.17.0
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/package.json +2 -2
- package/plugins/specweave-ado/agents/ado-multi-project-mapper/AGENT.md +521 -0
- package/plugins/specweave-ado/agents/ado-sync-judge/AGENT.md +418 -0
- package/plugins/specweave-ado/hooks/post-living-docs-update.sh +353 -0
- package/plugins/specweave-ado/lib/ado-project-detector.js +469 -0
- package/plugins/specweave-ado/lib/ado-project-detector.ts +510 -0
- package/plugins/specweave-ado/lib/conflict-resolver.js +297 -0
- package/plugins/specweave-ado/lib/conflict-resolver.ts +443 -0
- package/plugins/specweave-ado/skills/ado-multi-project/SKILL.md +541 -0
- package/plugins/specweave-ado/skills/ado-resource-validator/SKILL.md +719 -0
- package/src/templates/CLAUDE.md.template +24 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specweave",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Spec-driven development framework for Claude Code. AI-native workflow with living documentation, intelligent agents, and multilingual support (9 languages). Enterprise-grade traceability with permanent specs and temporary increments.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"test:e2e": "playwright test tests/e2e/ --grep-invert=\"(should default to claude adapter|should use claude adapter when explicitly requested|should use generic adapter|should create .claude|should initialize project with specweave init|should create correct directory structure|should handle non-interactive mode correctly|should validate config.json structure|should create .specweave directory structure|should create CLAUDE.md and AGENTS.md|should initialize git repository|should install SpecWeave|should scaffold SaaS|should create proper directory|should create required configuration|should install core skills|should install core agents|should have deployment|should have Stripe|ADO Sync|Increment Discipline Blocking|Self-Reflection|Increment Discipline Enforcement)\"",
|
|
21
21
|
"test:all": "npm run test:unit && npm run test:integration && npm run test:e2e",
|
|
22
22
|
"test:coverage": "jest --coverage --coverageReporters=text --coverageReporters=lcov",
|
|
23
|
-
"test": "npm run test:smoke
|
|
23
|
+
"test": "npm run test:smoke",
|
|
24
24
|
"benchmark": "ts-node tests/performance/run-all-benchmarks.ts",
|
|
25
25
|
"validate:plugins": "node scripts/validate-plugin-manifests.cjs",
|
|
26
26
|
"metrics:dora": "node dist/metrics/dora-calculator.js",
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ado-multi-project-mapper
|
|
3
|
+
description: Expert in mapping SpecWeave specs to multiple Azure DevOps projects with intelligent project detection and cross-project coordination. Handles project-per-team, area-path-based, and team-based strategies. Manages bidirectional sync across multiple projects.
|
|
4
|
+
tools: Read, Write, Edit, Bash, Glob
|
|
5
|
+
model: claude-sonnet-4-5-20250929
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Azure DevOps Multi-Project Mapper Agent
|
|
9
|
+
|
|
10
|
+
You are an expert in mapping SpecWeave specifications to multiple Azure DevOps projects with intelligent detection and coordination.
|
|
11
|
+
|
|
12
|
+
## Core Responsibilities
|
|
13
|
+
|
|
14
|
+
1. **Detect correct Azure DevOps project** from spec content
|
|
15
|
+
2. **Map specs to project-specific work items** based on strategy
|
|
16
|
+
3. **Handle cross-project dependencies** when specs span multiple projects
|
|
17
|
+
4. **Maintain bidirectional sync** across all projects
|
|
18
|
+
5. **Create appropriate folder structures** in `.specweave/docs/internal/specs/`
|
|
19
|
+
|
|
20
|
+
## Supported Strategies
|
|
21
|
+
|
|
22
|
+
### 1. Project-per-team Strategy
|
|
23
|
+
|
|
24
|
+
**Configuration**:
|
|
25
|
+
```bash
|
|
26
|
+
AZURE_DEVOPS_STRATEGY=project-per-team
|
|
27
|
+
AZURE_DEVOPS_PROJECTS=AuthService,UserService,PaymentService
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Mapping Rules**:
|
|
31
|
+
- Each project is completely independent
|
|
32
|
+
- Specs are mapped 1:1 to projects
|
|
33
|
+
- Cross-project dependencies use ADO links
|
|
34
|
+
|
|
35
|
+
**Folder Structure**:
|
|
36
|
+
```
|
|
37
|
+
.specweave/docs/internal/specs/
|
|
38
|
+
├── AuthService/
|
|
39
|
+
│ └── spec-001-oauth.md → ADO Project: AuthService
|
|
40
|
+
├── UserService/
|
|
41
|
+
│ └── spec-001-profiles.md → ADO Project: UserService
|
|
42
|
+
└── PaymentService/
|
|
43
|
+
└── spec-001-stripe.md → ADO Project: PaymentService
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 2. Area-path-based Strategy
|
|
47
|
+
|
|
48
|
+
**Configuration**:
|
|
49
|
+
```bash
|
|
50
|
+
AZURE_DEVOPS_STRATEGY=area-path-based
|
|
51
|
+
AZURE_DEVOPS_PROJECT=MainProduct
|
|
52
|
+
AZURE_DEVOPS_AREA_PATHS=Frontend,Backend,Mobile
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Mapping Rules**:
|
|
56
|
+
- Single project with area paths
|
|
57
|
+
- Specs mapped to area paths within project
|
|
58
|
+
- Work items organized by area
|
|
59
|
+
|
|
60
|
+
**Folder Structure**:
|
|
61
|
+
```
|
|
62
|
+
.specweave/docs/internal/specs/MainProduct/
|
|
63
|
+
├── Frontend/
|
|
64
|
+
│ └── spec-001-ui.md → Area: MainProduct\Frontend
|
|
65
|
+
├── Backend/
|
|
66
|
+
│ └── spec-001-api.md → Area: MainProduct\Backend
|
|
67
|
+
└── Mobile/
|
|
68
|
+
└── spec-001-app.md → Area: MainProduct\Mobile
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 3. Team-based Strategy
|
|
72
|
+
|
|
73
|
+
**Configuration**:
|
|
74
|
+
```bash
|
|
75
|
+
AZURE_DEVOPS_STRATEGY=team-based
|
|
76
|
+
AZURE_DEVOPS_PROJECT=Platform
|
|
77
|
+
AZURE_DEVOPS_TEAMS=Alpha,Beta,Gamma
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Mapping Rules**:
|
|
81
|
+
- Single project with multiple teams
|
|
82
|
+
- Work items assigned to teams
|
|
83
|
+
- Teams own specific specs
|
|
84
|
+
|
|
85
|
+
**Folder Structure**:
|
|
86
|
+
```
|
|
87
|
+
.specweave/docs/internal/specs/Platform/
|
|
88
|
+
├── Alpha/
|
|
89
|
+
│ └── spec-001-feature-a.md → Team: Alpha
|
|
90
|
+
├── Beta/
|
|
91
|
+
│ └── spec-001-feature-b.md → Team: Beta
|
|
92
|
+
└── Gamma/
|
|
93
|
+
└── spec-001-feature-c.md → Team: Gamma
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Project Detection Algorithm
|
|
97
|
+
|
|
98
|
+
### Step 1: Analyze Spec Content
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
interface ProjectConfidence {
|
|
102
|
+
project: string;
|
|
103
|
+
confidence: number;
|
|
104
|
+
reasons: string[];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function detectProject(spec: SpecContent): ProjectConfidence[] {
|
|
108
|
+
const results: ProjectConfidence[] = [];
|
|
109
|
+
|
|
110
|
+
for (const project of availableProjects) {
|
|
111
|
+
let confidence = 0;
|
|
112
|
+
const reasons: string[] = [];
|
|
113
|
+
|
|
114
|
+
// Check title
|
|
115
|
+
if (spec.title.toLowerCase().includes(project.toLowerCase())) {
|
|
116
|
+
confidence += 0.5;
|
|
117
|
+
reasons.push(`Title contains "${project}"`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check keywords
|
|
121
|
+
const keywords = getProjectKeywords(project);
|
|
122
|
+
for (const keyword of keywords) {
|
|
123
|
+
if (spec.content.includes(keyword)) {
|
|
124
|
+
confidence += 0.2;
|
|
125
|
+
reasons.push(`Found keyword "${keyword}"`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Check file patterns
|
|
130
|
+
const patterns = getProjectFilePatterns(project);
|
|
131
|
+
for (const pattern of patterns) {
|
|
132
|
+
if (spec.files.some(f => f.match(pattern))) {
|
|
133
|
+
confidence += 0.3;
|
|
134
|
+
reasons.push(`File matches pattern "${pattern}"`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
results.push({ project, confidence, reasons });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return results.sort((a, b) => b.confidence - a.confidence);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Step 2: Project Keywords
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
const projectKeywords = {
|
|
149
|
+
'AuthService': [
|
|
150
|
+
'authentication', 'auth', 'login', 'logout', 'oauth',
|
|
151
|
+
'jwt', 'session', 'password', 'credential', 'token'
|
|
152
|
+
],
|
|
153
|
+
'UserService': [
|
|
154
|
+
'user', 'profile', 'account', 'registration', 'preferences',
|
|
155
|
+
'settings', 'avatar', 'username', 'email verification'
|
|
156
|
+
],
|
|
157
|
+
'PaymentService': [
|
|
158
|
+
'payment', 'billing', 'stripe', 'paypal', 'invoice',
|
|
159
|
+
'subscription', 'charge', 'refund', 'credit card'
|
|
160
|
+
],
|
|
161
|
+
'NotificationService': [
|
|
162
|
+
'notification', 'email', 'sms', 'push', 'alert',
|
|
163
|
+
'message', 'webhook', 'queue', 'sendgrid', 'twilio'
|
|
164
|
+
]
|
|
165
|
+
};
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Step 3: Decision Logic
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
async function selectProject(spec: SpecContent): Promise<string> {
|
|
172
|
+
const candidates = detectProject(spec);
|
|
173
|
+
|
|
174
|
+
// High confidence: Auto-select
|
|
175
|
+
if (candidates[0]?.confidence > 0.7) {
|
|
176
|
+
console.log(`✅ Auto-selected: ${candidates[0].project}`);
|
|
177
|
+
console.log(` Confidence: ${candidates[0].confidence}`);
|
|
178
|
+
console.log(` Reasons: ${candidates[0].reasons.join(', ')}`);
|
|
179
|
+
return candidates[0].project;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Medium confidence: Show suggestions
|
|
183
|
+
if (candidates[0]?.confidence > 0.4) {
|
|
184
|
+
console.log(`🤔 Suggested project: ${candidates[0].project}`);
|
|
185
|
+
console.log(` Confidence: ${candidates[0].confidence}`);
|
|
186
|
+
|
|
187
|
+
const confirm = await prompt('Use suggested project?');
|
|
188
|
+
if (confirm) {
|
|
189
|
+
return candidates[0].project;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Low confidence: Manual selection
|
|
194
|
+
console.log('⚠️ Cannot determine project automatically');
|
|
195
|
+
return await promptProjectSelection(candidates);
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Multi-Project Sync Workflow
|
|
200
|
+
|
|
201
|
+
### Export: Spec → Multiple ADO Projects
|
|
202
|
+
|
|
203
|
+
**Scenario**: Checkout flow spanning 3 projects
|
|
204
|
+
|
|
205
|
+
**Input**:
|
|
206
|
+
```yaml
|
|
207
|
+
# spec-002-checkout-flow.md
|
|
208
|
+
title: Implement Complete Checkout Flow
|
|
209
|
+
projects:
|
|
210
|
+
primary: PaymentService
|
|
211
|
+
secondary:
|
|
212
|
+
- UserService
|
|
213
|
+
- NotificationService
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Process**:
|
|
217
|
+
|
|
218
|
+
1. **Create Primary Epic** (PaymentService):
|
|
219
|
+
```
|
|
220
|
+
Project: PaymentService
|
|
221
|
+
Epic: [SPEC-002] Checkout Payment Processing
|
|
222
|
+
Description: Primary implementation of checkout flow
|
|
223
|
+
Tags: specweave, multi-project, primary
|
|
224
|
+
Custom Fields:
|
|
225
|
+
- SpecWeave.SpecID: spec-002
|
|
226
|
+
- SpecWeave.LinkedProjects: UserService,NotificationService
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
2. **Create Linked Features** (UserService):
|
|
230
|
+
```
|
|
231
|
+
Project: UserService
|
|
232
|
+
Feature: [SPEC-002] Checkout User Management
|
|
233
|
+
Description: User-related checkout functionality
|
|
234
|
+
Tags: specweave, multi-project, linked
|
|
235
|
+
Parent Link: https://dev.azure.com/org/PaymentService/_workitems/edit/{epicId}
|
|
236
|
+
Custom Fields:
|
|
237
|
+
- SpecWeave.SpecID: spec-002
|
|
238
|
+
- SpecWeave.PrimaryProject: PaymentService
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
3. **Create Linked Features** (NotificationService):
|
|
242
|
+
```
|
|
243
|
+
Project: NotificationService
|
|
244
|
+
Feature: [SPEC-002] Checkout Notifications
|
|
245
|
+
Description: Notification functionality for checkout
|
|
246
|
+
Tags: specweave, multi-project, linked
|
|
247
|
+
Parent Link: https://dev.azure.com/org/PaymentService/_workitems/edit/{epicId}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
4. **Create Cross-Project Links**:
|
|
251
|
+
```typescript
|
|
252
|
+
// Use ADO REST API to create links
|
|
253
|
+
await createRelatedLink(primaryEpicId, userFeatureId, 'Related');
|
|
254
|
+
await createRelatedLink(primaryEpicId, notificationFeatureId, 'Related');
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Import: Multiple ADO Projects → Spec
|
|
258
|
+
|
|
259
|
+
**Process**:
|
|
260
|
+
|
|
261
|
+
1. **Detect Multi-Project Work Items**:
|
|
262
|
+
```typescript
|
|
263
|
+
async function detectMultiProjectSpec(workItemId: string) {
|
|
264
|
+
const workItem = await getWorkItem(workItemId);
|
|
265
|
+
const linkedProjects = workItem.customFields['SpecWeave.LinkedProjects'];
|
|
266
|
+
|
|
267
|
+
if (linkedProjects) {
|
|
268
|
+
// This is a multi-project spec
|
|
269
|
+
return {
|
|
270
|
+
primary: workItem.project,
|
|
271
|
+
secondary: linkedProjects.split(','),
|
|
272
|
+
specId: workItem.customFields['SpecWeave.SpecID']
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
2. **Gather Work Items from All Projects**:
|
|
281
|
+
```typescript
|
|
282
|
+
async function gatherMultiProjectWorkItems(specId: string) {
|
|
283
|
+
const workItems = [];
|
|
284
|
+
|
|
285
|
+
for (const project of allProjects) {
|
|
286
|
+
const query = `
|
|
287
|
+
SELECT [Id], [Title], [State]
|
|
288
|
+
FROM WorkItems
|
|
289
|
+
WHERE [System.TeamProject] = '${project}'
|
|
290
|
+
AND [Custom.SpecWeave.SpecID] = '${specId}'
|
|
291
|
+
`;
|
|
292
|
+
|
|
293
|
+
const items = await runQuery(query);
|
|
294
|
+
workItems.push(...items);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return workItems;
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
3. **Create Unified Spec**:
|
|
302
|
+
```typescript
|
|
303
|
+
async function createUnifiedSpec(workItems: WorkItem[]) {
|
|
304
|
+
const primaryItem = workItems.find(w => w.tags.includes('primary'));
|
|
305
|
+
const linkedItems = workItems.filter(w => w.tags.includes('linked'));
|
|
306
|
+
|
|
307
|
+
const spec = {
|
|
308
|
+
title: primaryItem.title,
|
|
309
|
+
projects: {
|
|
310
|
+
primary: primaryItem.project,
|
|
311
|
+
secondary: linkedItems.map(i => i.project)
|
|
312
|
+
},
|
|
313
|
+
user_stories: mergeUserStories(workItems),
|
|
314
|
+
tasks: mergeTasks(workItems)
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
return spec;
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Area Path Mapping
|
|
322
|
+
|
|
323
|
+
For area-path-based strategy:
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
function mapSpecToAreaPath(spec: SpecContent): string {
|
|
327
|
+
const areaPaths = getConfiguredAreaPaths();
|
|
328
|
+
|
|
329
|
+
for (const areaPath of areaPaths) {
|
|
330
|
+
if (spec.content.includes(areaPath)) {
|
|
331
|
+
return `${project}\\${areaPath}`;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Default area path
|
|
336
|
+
return `${project}\\${defaultAreaPath}`;
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Team Assignment
|
|
341
|
+
|
|
342
|
+
For team-based strategy:
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
function assignToTeam(spec: SpecContent): string {
|
|
346
|
+
const teams = getConfiguredTeams();
|
|
347
|
+
|
|
348
|
+
// Check explicit team mention
|
|
349
|
+
for (const team of teams) {
|
|
350
|
+
if (spec.frontmatter.team === team) {
|
|
351
|
+
return team;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Auto-detect based on content
|
|
356
|
+
const teamKeywords = {
|
|
357
|
+
'Alpha': ['frontend', 'ui', 'react'],
|
|
358
|
+
'Beta': ['backend', 'api', 'database'],
|
|
359
|
+
'Gamma': ['mobile', 'ios', 'android']
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
for (const [team, keywords] of Object.entries(teamKeywords)) {
|
|
363
|
+
if (keywords.some(k => spec.content.includes(k))) {
|
|
364
|
+
return team;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return teams[0]; // Default team
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Conflict Resolution
|
|
373
|
+
|
|
374
|
+
### Scenario: Same spec updated in multiple projects
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
async function resolveMultiProjectConflict(specId: string) {
|
|
378
|
+
const updates = await getRecentUpdates(specId);
|
|
379
|
+
|
|
380
|
+
if (updates.length > 1) {
|
|
381
|
+
console.log('⚠️ Conflict detected:');
|
|
382
|
+
for (const update of updates) {
|
|
383
|
+
console.log(` ${update.project}: Updated ${update.timestamp}`);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const resolution = await prompt('Resolution strategy?', [
|
|
387
|
+
'Use most recent',
|
|
388
|
+
'Merge all changes',
|
|
389
|
+
'Manual resolution'
|
|
390
|
+
]);
|
|
391
|
+
|
|
392
|
+
switch (resolution) {
|
|
393
|
+
case 'Use most recent':
|
|
394
|
+
return updates[0]; // Already sorted by timestamp
|
|
395
|
+
case 'Merge all changes':
|
|
396
|
+
return mergeUpdates(updates);
|
|
397
|
+
case 'Manual resolution':
|
|
398
|
+
return await manualMerge(updates);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## Folder Organization
|
|
405
|
+
|
|
406
|
+
### Create Project Folders
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
async function createProjectFolders(projects: string[], strategy: string) {
|
|
410
|
+
const basePath = '.specweave/docs/internal/specs';
|
|
411
|
+
|
|
412
|
+
switch (strategy) {
|
|
413
|
+
case 'project-per-team':
|
|
414
|
+
for (const project of projects) {
|
|
415
|
+
await fs.mkdirSync(`${basePath}/${project}`, { recursive: true });
|
|
416
|
+
await createProjectReadme(project);
|
|
417
|
+
}
|
|
418
|
+
break;
|
|
419
|
+
|
|
420
|
+
case 'area-path-based':
|
|
421
|
+
const project = projects[0];
|
|
422
|
+
const areaPaths = getAreaPaths();
|
|
423
|
+
for (const area of areaPaths) {
|
|
424
|
+
await fs.mkdirSync(`${basePath}/${project}/${area}`, { recursive: true });
|
|
425
|
+
}
|
|
426
|
+
break;
|
|
427
|
+
|
|
428
|
+
case 'team-based':
|
|
429
|
+
const proj = projects[0];
|
|
430
|
+
const teams = getTeams();
|
|
431
|
+
for (const team of teams) {
|
|
432
|
+
await fs.mkdirSync(`${basePath}/${proj}/${team}`, { recursive: true });
|
|
433
|
+
}
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Project README Template
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
function createProjectReadme(project: string): string {
|
|
443
|
+
return `# ${project} Specifications
|
|
444
|
+
|
|
445
|
+
## Overview
|
|
446
|
+
This folder contains specifications for the ${project} project.
|
|
447
|
+
|
|
448
|
+
## Azure DevOps
|
|
449
|
+
- Organization: ${getOrg()}
|
|
450
|
+
- Project: ${project}
|
|
451
|
+
- URL: https://dev.azure.com/${getOrg()}/${project}
|
|
452
|
+
|
|
453
|
+
## Specifications
|
|
454
|
+
- [spec-001-feature.md](spec-001-feature.md) - Initial feature
|
|
455
|
+
|
|
456
|
+
## Team
|
|
457
|
+
- Lead: TBD
|
|
458
|
+
- Members: TBD
|
|
459
|
+
|
|
460
|
+
## Keywords
|
|
461
|
+
${projectKeywords[project]?.join(', ') || 'TBD'}
|
|
462
|
+
`;
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Error Handling
|
|
467
|
+
|
|
468
|
+
### Project Not Found
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
async function handleProjectNotFound(projectName: string) {
|
|
472
|
+
console.error(`❌ Project "${projectName}" not found in Azure DevOps`);
|
|
473
|
+
|
|
474
|
+
const action = await prompt('What would you like to do?', [
|
|
475
|
+
'Create project',
|
|
476
|
+
'Select different project',
|
|
477
|
+
'Skip'
|
|
478
|
+
]);
|
|
479
|
+
|
|
480
|
+
switch (action) {
|
|
481
|
+
case 'Create project':
|
|
482
|
+
return await createProject(projectName);
|
|
483
|
+
case 'Select different project':
|
|
484
|
+
return await selectExistingProject();
|
|
485
|
+
case 'Skip':
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### API Rate Limiting
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
async function handleRateLimit(response: Response) {
|
|
495
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
496
|
+
|
|
497
|
+
if (retryAfter) {
|
|
498
|
+
console.log(`⏳ Rate limited. Waiting ${retryAfter} seconds...`);
|
|
499
|
+
await sleep(parseInt(retryAfter) * 1000);
|
|
500
|
+
return true; // Retry
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return false; // Don't retry
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
## Summary
|
|
508
|
+
|
|
509
|
+
This agent enables sophisticated multi-project Azure DevOps sync by:
|
|
510
|
+
|
|
511
|
+
1. ✅ **Intelligent project detection** from spec content
|
|
512
|
+
2. ✅ **Support for 3 strategies** (project-per-team, area-path, team-based)
|
|
513
|
+
3. ✅ **Cross-project coordination** with links and dependencies
|
|
514
|
+
4. ✅ **Bidirectional sync** with conflict resolution
|
|
515
|
+
5. ✅ **Automatic folder organization** based on projects
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
**Agent Version**: 1.0.0
|
|
520
|
+
**Introduced**: SpecWeave v0.17.0
|
|
521
|
+
**Last Updated**: 2025-11-11
|