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.
Files changed (37) hide show
  1. package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.d.ts +1 -1
  2. package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.d.ts.map +1 -1
  3. package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +3 -3
  4. package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js.map +1 -1
  5. package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts +9 -0
  6. package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts.map +1 -1
  7. package/dist/plugins/specweave-ado/lib/ado-spec-sync.js +43 -3
  8. package/dist/plugins/specweave-ado/lib/ado-spec-sync.js.map +1 -1
  9. package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.d.ts +1 -1
  10. package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.d.ts.map +1 -1
  11. package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.js +6 -6
  12. package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.js.map +1 -1
  13. package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.d.ts +1 -1
  14. package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.d.ts.map +1 -1
  15. package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.js +3 -3
  16. package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.js.map +1 -1
  17. package/package.json +1 -1
  18. package/plugins/specweave/lib/vendor/sync/config.d.ts +73 -0
  19. package/plugins/specweave/lib/vendor/sync/config.js +132 -0
  20. package/plugins/specweave/lib/vendor/sync/config.js.map +1 -0
  21. package/plugins/specweave/lib/vendor/sync/provider-router.d.ts +86 -0
  22. package/plugins/specweave/lib/vendor/sync/provider-router.js +147 -0
  23. package/plugins/specweave/lib/vendor/sync/provider-router.js.map +1 -0
  24. package/plugins/specweave/lib/vendor/sync/status-mapper.d.ts +120 -0
  25. package/plugins/specweave/lib/vendor/sync/status-mapper.js +164 -0
  26. package/plugins/specweave/lib/vendor/sync/status-mapper.js.map +1 -0
  27. package/plugins/specweave/lib/vendor/utils/project-detection.d.ts +250 -0
  28. package/plugins/specweave/lib/vendor/utils/project-detection.js +560 -0
  29. package/plugins/specweave/lib/vendor/utils/project-detection.js.map +1 -0
  30. package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +3 -3
  31. package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.ts +3 -3
  32. package/plugins/specweave-ado/lib/ado-spec-sync.js +41 -3
  33. package/plugins/specweave-ado/lib/ado-spec-sync.ts +47 -3
  34. package/plugins/specweave-github/lib/github-ac-checkbox-sync.js +6 -6
  35. package/plugins/specweave-github/lib/github-ac-checkbox-sync.ts +6 -6
  36. package/plugins/specweave-jira/lib/jira-ac-checkbox-sync.js +3 -3
  37. 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: us.status === "done" ? "Closed" : us.status === "in-progress" ? "Active" : "New",
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: "Closed" }
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: us.status === 'done' ? 'Closed' : us.status === 'in-progress' ? 'Active' : 'New',
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: 'Closed' }
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 "../../../src/utils/logger.js";
6
- import { autoDetectProjectIdSync } from "../../../src/utils/project-detection.js";
7
- import { deriveFeatureId } from "../../../src/utils/feature-id-derivation.js";
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 "../../../src/sync/provider-router.js";
10
+ } from "../../specweave/lib/vendor/sync/provider-router.js";
11
11
  import {
12
12
  isProviderEnabled
13
- } from "../../../src/sync/status-mapper.js";
14
- import { resolvePermissions } from "../../../src/sync/config.js";
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 '../../../src/utils/logger.js';
16
- import { autoDetectProjectIdSync } from '../../../src/utils/project-detection.js';
17
- import { deriveFeatureId } from '../../../src/utils/feature-id-derivation.js';
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 '../../../src/sync/provider-router.js';
21
+ } from '../../specweave/lib/vendor/sync/provider-router.js';
22
22
  import {
23
23
  isProviderEnabled,
24
- } from '../../../src/sync/status-mapper.js';
25
- import { resolvePermissions, SyncPreset } from '../../../src/sync/config.js';
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 "../../../src/utils/logger.js";
6
- import { autoDetectProjectIdSync } from "../../../src/utils/project-detection.js";
7
- import { deriveFeatureId } from "../../../src/utils/feature-id-derivation.js";
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 '../../../src/utils/logger.js';
19
- import { autoDetectProjectIdSync } from '../../../src/utils/project-detection.js';
20
- import { deriveFeatureId } from '../../../src/utils/feature-id-derivation.js';
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';