specweave 0.22.4 → 0.22.5

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.
@@ -0,0 +1,170 @@
1
+ import { AdoClientV2 } from "./ado-client-v2.js";
2
+ import { EnhancedContentBuilder } from "../../../src/core/sync/enhanced-content-builder.js";
3
+ import { SpecIncrementMapper } from "../../../src/core/sync/spec-increment-mapper.js";
4
+ import { parseSpecContent } from "../../../src/core/spec-content-sync.js";
5
+ import path from "path";
6
+ import fs from "fs/promises";
7
+ async function syncSpecToAdoWithEnhancedContent(options) {
8
+ const { specPath, organization, project, dryRun = false, verbose = false } = options;
9
+ try {
10
+ const baseSpec = await parseSpecContent(specPath);
11
+ if (!baseSpec) {
12
+ return {
13
+ success: false,
14
+ action: "error",
15
+ error: "Failed to parse spec content"
16
+ };
17
+ }
18
+ if (verbose) {
19
+ console.log(`\u{1F4C4} Parsed spec: ${baseSpec.identifier.compact}`);
20
+ }
21
+ const specId = baseSpec.identifier.full || baseSpec.identifier.compact;
22
+ const rootDir = await findSpecWeaveRoot(specPath);
23
+ const mapper = new SpecIncrementMapper(rootDir);
24
+ const mapping = await mapper.mapSpecToIncrements(specId);
25
+ if (verbose) {
26
+ console.log(`\u{1F517} Found ${mapping.increments.length} related increments`);
27
+ }
28
+ const taskMapping = buildTaskMapping(mapping.increments, organization, project);
29
+ const architectureDocs = await findArchitectureDocs(rootDir, specId);
30
+ const enhancedSpec = {
31
+ ...baseSpec,
32
+ summary: baseSpec.description,
33
+ taskMapping,
34
+ architectureDocs
35
+ };
36
+ const builder = new EnhancedContentBuilder();
37
+ const description = builder.buildExternalDescription(enhancedSpec);
38
+ if (verbose) {
39
+ console.log(`\u{1F4DD} Generated description: ${description.length} characters`);
40
+ }
41
+ if (dryRun) {
42
+ console.log("\u{1F50D} DRY RUN - Would create/update feature with:");
43
+ console.log(` Title: ${baseSpec.title}`);
44
+ console.log(` Description length: ${description.length}`);
45
+ return {
46
+ success: true,
47
+ action: "no-change",
48
+ tasksLinked: taskMapping?.tasks.length || 0
49
+ };
50
+ }
51
+ if (!organization || !project) {
52
+ return {
53
+ success: false,
54
+ action: "error",
55
+ error: "Azure DevOps organization/project not specified"
56
+ };
57
+ }
58
+ const profile = {
59
+ provider: "ado",
60
+ displayName: `${organization}/${project}`,
61
+ config: {
62
+ organization,
63
+ project
64
+ },
65
+ timeRange: { default: "1M", max: "6M" }
66
+ };
67
+ const pat = process.env.AZURE_DEVOPS_PAT || "";
68
+ const client = new AdoClientV2(profile, pat);
69
+ const existingFeature = await findExistingFeature(client, baseSpec.identifier.compact);
70
+ let result;
71
+ if (existingFeature) {
72
+ await client.updateWorkItem(existingFeature.id, {
73
+ title: `[${baseSpec.identifier.compact}] ${baseSpec.title}`,
74
+ description
75
+ });
76
+ result = {
77
+ success: true,
78
+ action: "updated",
79
+ featureId: existingFeature.id,
80
+ featureUrl: `https://dev.azure.com/${organization}/${project}/_workitems/edit/${existingFeature.id}`,
81
+ tasksLinked: taskMapping?.tasks.length || 0
82
+ };
83
+ } else {
84
+ const feature = await client.createEpic({
85
+ title: `[${baseSpec.identifier.compact}] ${baseSpec.title}`,
86
+ description,
87
+ tags: ["spec", "external-tool-sync"]
88
+ });
89
+ result = {
90
+ success: true,
91
+ action: "created",
92
+ featureId: feature.id,
93
+ featureUrl: `https://dev.azure.com/${organization}/${project}/_workitems/edit/${feature.id}`,
94
+ tasksLinked: taskMapping?.tasks.length || 0
95
+ };
96
+ }
97
+ if (verbose) {
98
+ console.log(`\u2705 ${result.action === "created" ? "Created" : "Updated"} feature #${result.featureId}`);
99
+ }
100
+ return result;
101
+ } catch (error) {
102
+ return {
103
+ success: false,
104
+ action: "error",
105
+ error: error.message
106
+ };
107
+ }
108
+ }
109
+ async function findSpecWeaveRoot(specPath) {
110
+ let currentDir = path.dirname(specPath);
111
+ while (true) {
112
+ const specweaveDir = path.join(currentDir, ".specweave");
113
+ try {
114
+ await fs.access(specweaveDir);
115
+ return currentDir;
116
+ } catch {
117
+ const parentDir = path.dirname(currentDir);
118
+ if (parentDir === currentDir) {
119
+ throw new Error(".specweave directory not found");
120
+ }
121
+ currentDir = parentDir;
122
+ }
123
+ }
124
+ }
125
+ function buildTaskMapping(increments, organization, project) {
126
+ if (increments.length === 0) return void 0;
127
+ const firstIncrement = increments[0];
128
+ const tasks = firstIncrement.tasks.map((task) => ({
129
+ id: task.id,
130
+ title: task.title,
131
+ userStories: task.userStories
132
+ }));
133
+ return {
134
+ incrementId: firstIncrement.id,
135
+ tasks,
136
+ tasksUrl: `https://dev.azure.com/${organization}/${project}/_git/repo?path=/.specweave/increments/${firstIncrement.id}/tasks.md`
137
+ };
138
+ }
139
+ async function findArchitectureDocs(rootDir, specId) {
140
+ const docs = [];
141
+ const archDir = path.join(rootDir, ".specweave/docs/internal/architecture");
142
+ try {
143
+ const adrDir = path.join(archDir, "adr");
144
+ try {
145
+ const adrs = await fs.readdir(adrDir);
146
+ const relatedAdrs = adrs.filter((file) => file.includes(specId.replace("spec-", "")));
147
+ for (const adr of relatedAdrs) {
148
+ docs.push({
149
+ type: "adr",
150
+ path: path.join(adrDir, adr),
151
+ title: adr.replace(".md", "").replace(/-/g, " ")
152
+ });
153
+ }
154
+ } catch {
155
+ }
156
+ } catch {
157
+ }
158
+ return docs;
159
+ }
160
+ async function findExistingFeature(client, specId) {
161
+ try {
162
+ const features = await client.queryWorkItems(`[System.Title] Contains '[${specId}]' AND [System.WorkItemType] = 'Feature'`);
163
+ return features[0] || null;
164
+ } catch {
165
+ return null;
166
+ }
167
+ }
168
+ export {
169
+ syncSpecToAdoWithEnhancedContent
170
+ };
@@ -1,6 +1,6 @@
1
- import { EnhancedContentBuilder } from "../../../src/core/sync/enhanced-content-builder.js";
2
- import { SpecIncrementMapper } from "../../../src/core/sync/spec-increment-mapper.js";
3
- import { parseSpecContent } from "../../../src/core/spec-content-sync.js";
1
+ import { EnhancedContentBuilder } from "../../../dist/src/core/sync/enhanced-content-builder.js";
2
+ import { SpecIncrementMapper } from "../../../dist/src/core/sync/spec-increment-mapper.js";
3
+ import { parseSpecContent } from "../../../dist/src/core/spec-content-sync.js";
4
4
  import * as path from "path";
5
5
  import * as fs from "fs/promises";
6
6
  async function syncSpecToJiraWithEnhancedContent(options) {