specweave 1.0.459 → 1.0.461
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/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.d.ts +1 -1
- package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +3 -3
- package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts +9 -0
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.js +43 -3
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.d.ts +1 -1
- package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.js +6 -6
- package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.d.ts +1 -1
- package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.js +3 -3
- package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/lib/vendor/sync/config.d.ts +73 -0
- package/plugins/specweave/lib/vendor/sync/config.js +132 -0
- package/plugins/specweave/lib/vendor/sync/config.js.map +1 -0
- package/plugins/specweave/lib/vendor/sync/provider-router.d.ts +86 -0
- package/plugins/specweave/lib/vendor/sync/provider-router.js +147 -0
- package/plugins/specweave/lib/vendor/sync/provider-router.js.map +1 -0
- package/plugins/specweave/lib/vendor/sync/status-mapper.d.ts +120 -0
- package/plugins/specweave/lib/vendor/sync/status-mapper.js +164 -0
- package/plugins/specweave/lib/vendor/sync/status-mapper.js.map +1 -0
- package/plugins/specweave/lib/vendor/utils/project-detection.d.ts +250 -0
- package/plugins/specweave/lib/vendor/utils/project-detection.js +560 -0
- package/plugins/specweave/lib/vendor/utils/project-detection.js.map +1 -0
- package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +3 -3
- package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.ts +3 -3
- package/plugins/specweave-ado/lib/ado-spec-sync.js +41 -3
- package/plugins/specweave-ado/lib/ado-spec-sync.ts +47 -3
- package/plugins/specweave-github/lib/github-ac-checkbox-sync.js +6 -6
- package/plugins/specweave-github/lib/github-ac-checkbox-sync.ts +6 -6
- package/plugins/specweave-jira/lib/jira-ac-checkbox-sync.js +3 -3
- package/plugins/specweave-jira/lib/jira-ac-checkbox-sync.ts +3 -3
|
@@ -4,6 +4,14 @@ import axios from "axios";
|
|
|
4
4
|
class AdoSpecSync {
|
|
5
5
|
constructor(config, projectRoot = process.cwd(), projectId) {
|
|
6
6
|
this.availableTypes = null;
|
|
7
|
+
/**
|
|
8
|
+
* Resolve a work item state name for the given type.
|
|
9
|
+
* Basic process (Issue type) uses: "To Do", "Doing", "Done"
|
|
10
|
+
* Agile/Scrum process uses: "New", "Active", "Closed" / "Resolved" / "Done"
|
|
11
|
+
*
|
|
12
|
+
* Maps canonical states: 'New' → todo, 'Active' → in-progress, 'Closed' → done
|
|
13
|
+
*/
|
|
14
|
+
this.stateCache = /* @__PURE__ */ new Map();
|
|
7
15
|
this.specManager = new SpecMetadataManager(projectRoot, projectId);
|
|
8
16
|
this.config = config;
|
|
9
17
|
this.client = axios.create({
|
|
@@ -50,6 +58,31 @@ class AdoSpecSync {
|
|
|
50
58
|
}
|
|
51
59
|
return desiredType;
|
|
52
60
|
}
|
|
61
|
+
async resolveWorkItemState(workItemType, canonicalState) {
|
|
62
|
+
if (!this.stateCache.has(workItemType)) {
|
|
63
|
+
try {
|
|
64
|
+
const resp = await this.client.get(
|
|
65
|
+
`/wit/workitemtypes/${encodeURIComponent(workItemType)}/states?api-version=7.0`,
|
|
66
|
+
{ headers: { "Accept": "application/json" } }
|
|
67
|
+
);
|
|
68
|
+
const states = new Set(resp.data.value.map((s) => s.name));
|
|
69
|
+
this.stateCache.set(workItemType, states);
|
|
70
|
+
} catch {
|
|
71
|
+
this.stateCache.set(workItemType, /* @__PURE__ */ new Set(["New", "Active", "Resolved", "Closed"]));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const validStates = this.stateCache.get(workItemType);
|
|
75
|
+
const candidates = {
|
|
76
|
+
"New": ["To Do", "New", "Open"],
|
|
77
|
+
"Active": ["Doing", "Active", "In Progress", "In-Progress"],
|
|
78
|
+
"Closed": ["Done", "Closed", "Resolved", "Completed"]
|
|
79
|
+
};
|
|
80
|
+
for (const candidate of candidates[canonicalState]) {
|
|
81
|
+
if (validStates.has(candidate)) return candidate;
|
|
82
|
+
}
|
|
83
|
+
const arr = [...validStates];
|
|
84
|
+
return canonicalState === "Closed" ? arr[arr.length - 1] ?? canonicalState : arr[0] ?? canonicalState;
|
|
85
|
+
}
|
|
53
86
|
/**
|
|
54
87
|
* Sync spec to ADO Feature (CREATE or UPDATE)
|
|
55
88
|
*/
|
|
@@ -268,10 +301,13 @@ class AdoSpecSync {
|
|
|
268
301
|
const storyDescription = this.generateStoryDescription(us);
|
|
269
302
|
const existingStory = await this.findStoryByTitle(us.id);
|
|
270
303
|
if (existingStory) {
|
|
304
|
+
const resolvedStoryType = await this.resolveWorkItemType("User Story");
|
|
305
|
+
const canonicalState = us.status === "done" ? "Closed" : us.status === "in-progress" ? "Active" : "New";
|
|
306
|
+
const resolvedState = await this.resolveWorkItemState(resolvedStoryType, canonicalState);
|
|
271
307
|
await this.updateStory(existingStory.id, {
|
|
272
308
|
title: storyTitle,
|
|
273
309
|
description: storyDescription,
|
|
274
|
-
state:
|
|
310
|
+
state: resolvedState,
|
|
275
311
|
parentId: featureId
|
|
276
312
|
});
|
|
277
313
|
updated.push(us.id);
|
|
@@ -291,10 +327,12 @@ class AdoSpecSync {
|
|
|
291
327
|
const allDone = spec.metadata.userStories.every((us) => us.status === "done");
|
|
292
328
|
if (allDone && spec.metadata.userStories.length > 0 && created.length === 0) {
|
|
293
329
|
try {
|
|
330
|
+
const epicType = await this.resolveWorkItemType("Feature");
|
|
331
|
+
const epicClosedState = await this.resolveWorkItemState(epicType, "Closed");
|
|
294
332
|
await this.client.patch(`/wit/workitems/${featureId}?api-version=7.0`, [
|
|
295
|
-
{ op: "replace", path: "/fields/System.State", value:
|
|
333
|
+
{ op: "replace", path: "/fields/System.State", value: epicClosedState }
|
|
296
334
|
], { headers: { "Content-Type": "application/json-patch+json" } });
|
|
297
|
-
console.log(` \u2705 All stories done \u2014 closed parent Epic #${featureId}`);
|
|
335
|
+
console.log(` \u2705 All stories done \u2014 closed parent Epic #${featureId} (state: ${epicClosedState})`);
|
|
298
336
|
} catch {
|
|
299
337
|
}
|
|
300
338
|
}
|
|
@@ -116,6 +116,45 @@ export class AdoSpecSync {
|
|
|
116
116
|
return desiredType; // Let ADO reject if truly unavailable
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Resolve a work item state name for the given type.
|
|
121
|
+
* Basic process (Issue type) uses: "To Do", "Doing", "Done"
|
|
122
|
+
* Agile/Scrum process uses: "New", "Active", "Closed" / "Resolved" / "Done"
|
|
123
|
+
*
|
|
124
|
+
* Maps canonical states: 'New' → todo, 'Active' → in-progress, 'Closed' → done
|
|
125
|
+
*/
|
|
126
|
+
private stateCache = new Map<string, Set<string>>();
|
|
127
|
+
private async resolveWorkItemState(workItemType: string, canonicalState: 'New' | 'Active' | 'Closed'): Promise<string> {
|
|
128
|
+
if (!this.stateCache.has(workItemType)) {
|
|
129
|
+
try {
|
|
130
|
+
const resp = await this.client.get(
|
|
131
|
+
`/wit/workitemtypes/${encodeURIComponent(workItemType)}/states?api-version=7.0`,
|
|
132
|
+
{ headers: { 'Accept': 'application/json' } }
|
|
133
|
+
);
|
|
134
|
+
const states = new Set<string>(resp.data.value.map((s: { name: string }) => s.name));
|
|
135
|
+
this.stateCache.set(workItemType, states);
|
|
136
|
+
} catch {
|
|
137
|
+
// Fallback: assume Agile states
|
|
138
|
+
this.stateCache.set(workItemType, new Set(['New', 'Active', 'Resolved', 'Closed']));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const validStates = this.stateCache.get(workItemType)!;
|
|
142
|
+
|
|
143
|
+
// Preferred state names per canonical state, in priority order
|
|
144
|
+
const candidates: Record<string, string[]> = {
|
|
145
|
+
'New': ['To Do', 'New', 'Open'],
|
|
146
|
+
'Active': ['Doing', 'Active', 'In Progress', 'In-Progress'],
|
|
147
|
+
'Closed': ['Done', 'Closed', 'Resolved', 'Completed'],
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
for (const candidate of candidates[canonicalState]) {
|
|
151
|
+
if (validStates.has(candidate)) return candidate;
|
|
152
|
+
}
|
|
153
|
+
// Last resort: return first available state for 'New', last for 'Closed'
|
|
154
|
+
const arr = [...validStates];
|
|
155
|
+
return canonicalState === 'Closed' ? (arr[arr.length - 1] ?? canonicalState) : (arr[0] ?? canonicalState);
|
|
156
|
+
}
|
|
157
|
+
|
|
119
158
|
/**
|
|
120
159
|
* Sync spec to ADO Feature (CREATE or UPDATE)
|
|
121
160
|
*/
|
|
@@ -391,11 +430,14 @@ export class AdoSpecSync {
|
|
|
391
430
|
const existingStory = await this.findStoryByTitle(us.id);
|
|
392
431
|
|
|
393
432
|
if (existingStory) {
|
|
433
|
+
const resolvedStoryType = await this.resolveWorkItemType('User Story');
|
|
434
|
+
const canonicalState = us.status === 'done' ? 'Closed' : us.status === 'in-progress' ? 'Active' : 'New';
|
|
435
|
+
const resolvedState = await this.resolveWorkItemState(resolvedStoryType, canonicalState);
|
|
394
436
|
// UPDATE existing story (also re-apply parent to fix orphaned stories)
|
|
395
437
|
await this.updateStory(existingStory.id, {
|
|
396
438
|
title: storyTitle,
|
|
397
439
|
description: storyDescription,
|
|
398
|
-
state:
|
|
440
|
+
state: resolvedState,
|
|
399
441
|
parentId: featureId
|
|
400
442
|
});
|
|
401
443
|
|
|
@@ -420,10 +462,12 @@ export class AdoSpecSync {
|
|
|
420
462
|
const allDone = spec.metadata.userStories.every(us => us.status === 'done');
|
|
421
463
|
if (allDone && spec.metadata.userStories.length > 0 && created.length === 0) {
|
|
422
464
|
try {
|
|
465
|
+
const epicType = await this.resolveWorkItemType('Feature');
|
|
466
|
+
const epicClosedState = await this.resolveWorkItemState(epicType, 'Closed');
|
|
423
467
|
await this.client.patch(`/wit/workitems/${featureId}?api-version=7.0`, [
|
|
424
|
-
{ op: 'replace', path: '/fields/System.State', value:
|
|
468
|
+
{ op: 'replace', path: '/fields/System.State', value: epicClosedState }
|
|
425
469
|
], { headers: { 'Content-Type': 'application/json-patch+json' } });
|
|
426
|
-
console.log(` ✅ All stories done — closed parent Epic #${featureId}`);
|
|
470
|
+
console.log(` ✅ All stories done — closed parent Epic #${featureId} (state: ${epicClosedState})`);
|
|
427
471
|
} catch {
|
|
428
472
|
// Non-blocking — state transition may not be valid for all ADO project configs
|
|
429
473
|
}
|
|
@@ -2,16 +2,16 @@ import { promises as fs, existsSync } from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import yaml from "yaml";
|
|
4
4
|
import { GitHubClientV2 } from "./github-client-v2.js";
|
|
5
|
-
import { consoleLogger } from "
|
|
6
|
-
import { autoDetectProjectIdSync } from "
|
|
7
|
-
import { deriveFeatureId } from "
|
|
5
|
+
import { consoleLogger } from "../../specweave/lib/vendor/utils/logger.js";
|
|
6
|
+
import { autoDetectProjectIdSync } from "../../specweave/lib/vendor/utils/project-detection.js";
|
|
7
|
+
import { deriveFeatureId } from "../../specweave/lib/vendor/utils/feature-id-derivation.js";
|
|
8
8
|
import {
|
|
9
9
|
ProviderRouter
|
|
10
|
-
} from "
|
|
10
|
+
} from "../../specweave/lib/vendor/sync/provider-router.js";
|
|
11
11
|
import {
|
|
12
12
|
isProviderEnabled
|
|
13
|
-
} from "
|
|
14
|
-
import { resolvePermissions } from "
|
|
13
|
+
} from "../../specweave/lib/vendor/sync/status-mapper.js";
|
|
14
|
+
import { resolvePermissions } from "../../specweave/lib/vendor/sync/config.js";
|
|
15
15
|
class GitHubACCheckboxSync {
|
|
16
16
|
constructor(options) {
|
|
17
17
|
this.projectRoot = options.projectRoot;
|
|
@@ -12,17 +12,17 @@ import { promises as fs, existsSync } from 'fs';
|
|
|
12
12
|
import path from 'path';
|
|
13
13
|
import yaml from 'yaml';
|
|
14
14
|
import { GitHubClientV2 } from './github-client-v2.js';
|
|
15
|
-
import { Logger, consoleLogger } from '
|
|
16
|
-
import { autoDetectProjectIdSync } from '
|
|
17
|
-
import { deriveFeatureId } from '
|
|
15
|
+
import { Logger, consoleLogger } from '../../specweave/lib/vendor/utils/logger.js';
|
|
16
|
+
import { autoDetectProjectIdSync } from '../../specweave/lib/vendor/utils/project-detection.js';
|
|
17
|
+
import { deriveFeatureId } from '../../specweave/lib/vendor/utils/feature-id-derivation.js';
|
|
18
18
|
import {
|
|
19
19
|
ProviderRouter,
|
|
20
20
|
GitHubRepoConfig,
|
|
21
|
-
} from '
|
|
21
|
+
} from '../../specweave/lib/vendor/sync/provider-router.js';
|
|
22
22
|
import {
|
|
23
23
|
isProviderEnabled,
|
|
24
|
-
} from '
|
|
25
|
-
import { resolvePermissions, SyncPreset } from '
|
|
24
|
+
} from '../../specweave/lib/vendor/sync/status-mapper.js';
|
|
25
|
+
import { resolvePermissions, SyncPreset } from '../../specweave/lib/vendor/sync/config.js';
|
|
26
26
|
import type { SpecWeaveConfig } from '../../../src/core/config/types.js';
|
|
27
27
|
import type { LivingDocsUSFile } from '../../../src/types/living-docs-us-file.js';
|
|
28
28
|
|
|
@@ -2,9 +2,9 @@ import { promises as fs, existsSync } from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import yaml from "yaml";
|
|
4
4
|
import axios from "axios";
|
|
5
|
-
import { consoleLogger } from "
|
|
6
|
-
import { autoDetectProjectIdSync } from "
|
|
7
|
-
import { deriveFeatureId } from "
|
|
5
|
+
import { consoleLogger } from "../../specweave/lib/vendor/utils/logger.js";
|
|
6
|
+
import { autoDetectProjectIdSync } from "../../specweave/lib/vendor/utils/project-detection.js";
|
|
7
|
+
import { deriveFeatureId } from "../../specweave/lib/vendor/utils/feature-id-derivation.js";
|
|
8
8
|
import { getApiBaseUrl } from "./jira-deployment-detector.js";
|
|
9
9
|
import { GitHubACCheckboxSync } from "../../specweave-github/lib/github-ac-checkbox-sync.js";
|
|
10
10
|
class JiraACCheckboxSync {
|
|
@@ -15,9 +15,9 @@ import { promises as fs, existsSync } from 'fs';
|
|
|
15
15
|
import path from 'path';
|
|
16
16
|
import yaml from 'yaml';
|
|
17
17
|
import axios, { AxiosInstance } from 'axios';
|
|
18
|
-
import { Logger, consoleLogger } from '
|
|
19
|
-
import { autoDetectProjectIdSync } from '
|
|
20
|
-
import { deriveFeatureId } from '
|
|
18
|
+
import { Logger, consoleLogger } from '../../specweave/lib/vendor/utils/logger.js';
|
|
19
|
+
import { autoDetectProjectIdSync } from '../../specweave/lib/vendor/utils/project-detection.js';
|
|
20
|
+
import { deriveFeatureId } from '../../specweave/lib/vendor/utils/feature-id-derivation.js';
|
|
21
21
|
import { detectDeploymentType, getApiBaseUrl } from './jira-deployment-detector.js';
|
|
22
22
|
import type { SpecWeaveConfig } from '../../../src/core/config/types.js';
|
|
23
23
|
import type { LivingDocsUSFile } from '../../../src/types/living-docs-us-file.js';
|