specweave 1.0.362 → 1.0.367

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 (50) hide show
  1. package/bin/specweave.js +1 -1
  2. package/dist/src/cli/commands/init.d.ts.map +1 -1
  3. package/dist/src/cli/commands/init.js +33 -0
  4. package/dist/src/cli/commands/init.js.map +1 -1
  5. package/dist/src/cli/commands/sync-progress.d.ts.map +1 -1
  6. package/dist/src/cli/commands/sync-progress.js +1 -0
  7. package/dist/src/cli/commands/sync-progress.js.map +1 -1
  8. package/dist/src/core/config/config-manager.d.ts.map +1 -1
  9. package/dist/src/core/config/config-manager.js +6 -0
  10. package/dist/src/core/config/config-manager.js.map +1 -1
  11. package/dist/src/core/config/types.d.ts +2 -2
  12. package/dist/src/core/config/types.d.ts.map +1 -1
  13. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts.map +1 -1
  14. package/dist/src/core/lazy-loading/llm-plugin-detector.js +69 -0
  15. package/dist/src/core/lazy-loading/llm-plugin-detector.js.map +1 -1
  16. package/dist/src/core/migration/umbrella-migrator.d.ts.map +1 -1
  17. package/dist/src/core/migration/umbrella-migrator.js +17 -1
  18. package/dist/src/core/migration/umbrella-migrator.js.map +1 -1
  19. package/dist/src/sync/external-issue-auto-creator.d.ts.map +1 -1
  20. package/dist/src/sync/external-issue-auto-creator.js +13 -1
  21. package/dist/src/sync/external-issue-auto-creator.js.map +1 -1
  22. package/dist/src/sync/sync-target-resolver.d.ts +2 -2
  23. package/dist/src/sync/sync-target-resolver.d.ts.map +1 -1
  24. package/dist/src/sync/sync-target-resolver.js +17 -4
  25. package/dist/src/sync/sync-target-resolver.js.map +1 -1
  26. package/package.json +1 -1
  27. package/plugins/SKILLS-VS-AGENTS.md +212 -119
  28. package/plugins/specweave/agents/sw-architect.md +8 -67
  29. package/plugins/specweave/agents/sw-planner.md +10 -82
  30. package/plugins/specweave/agents/sw-pm.md +9 -103
  31. package/plugins/specweave/hooks/.specweave/logs/auto-iterations.log +1 -0
  32. package/plugins/specweave/hooks/.specweave/logs/auto-stop-reasons.log +1 -0
  33. package/plugins/specweave/skills/.specweave/logs/reflect/auto-reflect.log +15 -0
  34. package/plugins/specweave/skills/.specweave/logs/reflect/reflect.log +3 -0
  35. package/plugins/specweave/skills/.specweave/logs/stop-auto.log +1 -0
  36. package/plugins/specweave/skills/brainstorm/SKILL.md +128 -26
  37. package/plugins/specweave/skills/increment/SKILL.md +50 -32
  38. package/plugins/specweave/skills/pm/SKILL.md +28 -1
  39. package/plugins/specweave/skills/pm/phases/02-spec-creation.md +17 -0
  40. package/plugins/specweave/skills/team-lead/agents/database.md +12 -12
  41. package/plugins/specweave/skills/team-lead/agents/security.md +12 -12
  42. package/plugins/specweave/skills/team-lead/agents/testing.md +12 -12
  43. package/plugins/specweave/skills/team-merge/SKILL.md +11 -0
  44. package/plugins/specweave/skills/test-aware-planner/SKILL.md +1 -1
  45. package/plugins/specweave-ado/lib/ado-multi-project-sync.js +0 -1
  46. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +180 -0
  47. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +1266 -0
  48. package/plugins/specweave-github/lib/enhanced-github-sync.js +249 -0
  49. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +150 -0
  50. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1260 -0
@@ -0,0 +1,249 @@
1
+ import { GitHubClientV2 } from "./github-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 { LabelDetector } from "../../../src/core/sync/label-detector.js";
6
+ import path from "path";
7
+ import fs from "fs/promises";
8
+ async function syncSpecWithEnhancedContent(options) {
9
+ const { specPath, owner, repo, dryRun = false, verbose = false } = options;
10
+ try {
11
+ const baseSpec = await parseSpecContent(specPath);
12
+ if (!baseSpec) {
13
+ return {
14
+ success: false,
15
+ action: "error",
16
+ error: "Failed to parse spec content"
17
+ };
18
+ }
19
+ if (verbose) {
20
+ console.log(`\u{1F4C4} Parsed spec: ${baseSpec.identifier.compact}`);
21
+ }
22
+ const specId = baseSpec.identifier.full || baseSpec.identifier.compact;
23
+ const rootDir = await findSpecWeaveRoot(specPath);
24
+ const mapper = new SpecIncrementMapper(rootDir);
25
+ const mapping = await mapper.mapSpecToIncrements(specId);
26
+ if (verbose) {
27
+ console.log(`\u{1F517} Found ${mapping.increments.length} related increments`);
28
+ console.log(`\u{1F4CB} Mapped ${Object.keys(mapping.userStoryMappings).length} user stories to tasks`);
29
+ }
30
+ const defaultBranch = (owner && repo) ? await getDefaultBranch(owner, repo) : null;
31
+ const taskMapping = buildTaskMapping(mapping.increments, owner, repo, defaultBranch);
32
+ const architectureDocs = await findArchitectureDocs(rootDir, specId);
33
+ const sourceLinks = buildSourceLinks(mapping.increments[0]?.id, owner, repo, defaultBranch);
34
+ const enhancedSpec = {
35
+ ...baseSpec,
36
+ summary: baseSpec.description,
37
+ taskMapping,
38
+ architectureDocs,
39
+ sourceLinks
40
+ };
41
+ const builder = new EnhancedContentBuilder();
42
+ const originalBuildExternal = builder.buildExternalDescription.bind(builder);
43
+ const description = (() => {
44
+ const sections = [];
45
+ sections.push(builder.buildSummarySection(enhancedSpec));
46
+ if (enhancedSpec.userStories && enhancedSpec.userStories.length > 0) {
47
+ sections.push(builder.buildUserStoriesSection(enhancedSpec.userStories));
48
+ }
49
+ if (enhancedSpec.taskMapping) {
50
+ sections.push(builder.buildTasksSection(enhancedSpec.taskMapping, {
51
+ showCheckboxes: true,
52
+ showProgressBar: true,
53
+ showCompletionStatus: true,
54
+ provider: "github"
55
+ }));
56
+ }
57
+ if (enhancedSpec.architectureDocs && enhancedSpec.architectureDocs.length > 0) {
58
+ sections.push(builder.buildArchitectureSection(enhancedSpec.architectureDocs));
59
+ }
60
+ if (enhancedSpec.sourceLinks) {
61
+ sections.push(builder.buildSourceLinksSection(enhancedSpec.sourceLinks));
62
+ }
63
+ return sections.filter((s) => s.length > 0).join("\n\n---\n\n");
64
+ })();
65
+ if (verbose) {
66
+ console.log(`\u{1F4DD} Generated description: ${description.length} characters`);
67
+ }
68
+ if (dryRun) {
69
+ console.log("\u{1F50D} DRY RUN - Would create/update issue with:");
70
+ console.log(` Title: ${baseSpec.title}`);
71
+ console.log(` Description length: ${description.length}`);
72
+ console.log(` Tasks linked: ${taskMapping?.tasks.length || 0}`);
73
+ return {
74
+ success: true,
75
+ action: "no-change",
76
+ tasksLinked: taskMapping?.tasks.length || 0
77
+ };
78
+ }
79
+ if (!owner || !repo) {
80
+ return {
81
+ success: false,
82
+ action: "error",
83
+ error: "GitHub owner/repo not specified"
84
+ };
85
+ }
86
+ const client = GitHubClientV2.fromRepo(owner, repo);
87
+ const labelDetector = new LabelDetector(void 0, false);
88
+ const detection = labelDetector.detectType(
89
+ await fs.readFile(specPath, "utf-8"),
90
+ mapping.increments[0]?.id
91
+ );
92
+ const githubLabels = labelDetector.getGitHubLabels(detection.type);
93
+ const allLabels = ["spec", ...githubLabels];
94
+ if (verbose) {
95
+ console.log(`\u{1F3F7}\uFE0F Detected type: ${detection.type} (${detection.confidence}% confidence)`);
96
+ console.log(` Labels: ${allLabels.join(", ")}`);
97
+ }
98
+ const existingIssue = await findExistingIssue(client, baseSpec.identifier.compact);
99
+ let result;
100
+ if (existingIssue) {
101
+ await client.updateIssueBody(existingIssue.number, description);
102
+ await client.addLabels(existingIssue.number, allLabels);
103
+ result = {
104
+ success: true,
105
+ action: "updated",
106
+ issueNumber: existingIssue.number,
107
+ issueUrl: existingIssue.html_url,
108
+ tasksLinked: taskMapping?.tasks.length || 0
109
+ };
110
+ } else {
111
+ const issue = await client.createEpicIssue(
112
+ `[${baseSpec.project === "_features" ? baseSpec.identifier.display : baseSpec.identifier.compact}] ${baseSpec.title}`,
113
+ description,
114
+ void 0,
115
+ allLabels
116
+ // Apply labels at creation
117
+ );
118
+ result = {
119
+ success: true,
120
+ action: "created",
121
+ issueNumber: issue.number,
122
+ issueUrl: issue.html_url,
123
+ tasksLinked: taskMapping?.tasks.length || 0
124
+ };
125
+ await mapper.updateSpecWithIncrementLinks(specId, mapping.increments[0]?.id);
126
+ }
127
+ if (verbose) {
128
+ console.log(`\u2705 ${result.action === "created" ? "Created" : "Updated"} issue #${result.issueNumber}`);
129
+ console.log(` URL: ${result.issueUrl}`);
130
+ console.log(` Tasks linked: ${result.tasksLinked}`);
131
+ }
132
+ return result;
133
+ } catch (error) {
134
+ return {
135
+ success: false,
136
+ action: "error",
137
+ error: error.message
138
+ };
139
+ }
140
+ }
141
+ async function findSpecWeaveRoot(specPath) {
142
+ let currentDir = path.dirname(specPath);
143
+ while (true) {
144
+ const specweaveDir = path.join(currentDir, ".specweave");
145
+ try {
146
+ await fs.access(specweaveDir);
147
+ return currentDir;
148
+ } catch {
149
+ const parentDir = path.dirname(currentDir);
150
+ if (parentDir === currentDir) {
151
+ throw new Error(".specweave directory not found");
152
+ }
153
+ currentDir = parentDir;
154
+ }
155
+ }
156
+ }
157
+ // Cache for default branch per repo per session
158
+ const defaultBranchCache = new Map();
159
+
160
+ async function getDefaultBranch(owner, repo) {
161
+ const cacheKey = `${owner}/${repo}`;
162
+ if (defaultBranchCache.has(cacheKey)) {
163
+ return defaultBranchCache.get(cacheKey);
164
+ }
165
+ try {
166
+ const { execFileSync } = await import("child_process");
167
+ const result = execFileSync("gh", [
168
+ "repo", "view", `${owner}/${repo}`,
169
+ "--json", "defaultBranchRef",
170
+ "--jq", ".defaultBranchRef.name"
171
+ ], { encoding: "utf-8", timeout: 10000 }).trim();
172
+ if (result) {
173
+ defaultBranchCache.set(cacheKey, result);
174
+ return result;
175
+ }
176
+ } catch {
177
+ // On failure, return null — callers omit branch segment
178
+ }
179
+ defaultBranchCache.set(cacheKey, null);
180
+ return null;
181
+ }
182
+
183
+ function buildTaskMapping(increments, owner, repo, defaultBranch) {
184
+ if (increments.length === 0) return void 0;
185
+ const firstIncrement = increments[0];
186
+ const tasks = firstIncrement.tasks.map((task) => ({
187
+ id: task.id,
188
+ title: task.title,
189
+ userStories: task.userStories,
190
+ githubIssue: task.githubIssue
191
+ }));
192
+ const branchSegment = defaultBranch ? `/blob/${defaultBranch}` : "";
193
+ return {
194
+ incrementId: firstIncrement.id,
195
+ tasks,
196
+ tasksUrl: `https://github.com/${owner}/${repo}${branchSegment}/.specweave/increments/${firstIncrement.id}/tasks.md`
197
+ };
198
+ }
199
+ async function findArchitectureDocs(rootDir, specId) {
200
+ const docs = [];
201
+ const archDir = path.join(rootDir, ".specweave/docs/internal/architecture");
202
+ try {
203
+ const adrDir = path.join(archDir, "adr");
204
+ try {
205
+ const adrs = await fs.readdir(adrDir);
206
+ const relatedAdrs = adrs.filter((file) => file.includes(specId.replace("spec-", "")));
207
+ for (const adr of relatedAdrs) {
208
+ docs.push({
209
+ type: "adr",
210
+ path: path.join(adrDir, adr),
211
+ title: adr.replace(".md", "").replace(/-/g, " ")
212
+ });
213
+ }
214
+ } catch {
215
+ }
216
+ const hlds = await fs.readdir(archDir);
217
+ const relatedHlds = hlds.filter((file) => file.includes("hld") && file.includes(specId.replace("spec-", "")));
218
+ for (const hld of relatedHlds) {
219
+ docs.push({
220
+ type: "hld",
221
+ path: path.join(archDir, hld),
222
+ title: hld.replace(".md", "").replace(/-/g, " ")
223
+ });
224
+ }
225
+ } catch {
226
+ }
227
+ return docs;
228
+ }
229
+ function buildSourceLinks(incrementId, owner, repo, defaultBranch) {
230
+ if (!incrementId) return void 0;
231
+ const branchSegment = defaultBranch ? `/blob/${defaultBranch}` : "";
232
+ const baseUrl = `https://github.com/${owner}/${repo}${branchSegment}/.specweave`;
233
+ return {
234
+ spec: `${baseUrl}/docs/internal/specs/default/spec-${incrementId.replace(/^\d+-/, "")}.md`,
235
+ plan: `${baseUrl}/increments/${incrementId}/plan.md`,
236
+ tasks: `${baseUrl}/increments/${incrementId}/tasks.md`
237
+ };
238
+ }
239
+ async function findExistingIssue(client, specId) {
240
+ try {
241
+ const issues = await client.listIssuesInTimeRange("ALL");
242
+ return issues.find((issue) => issue.title.includes(`[${specId}]`) && issue.labels?.some((l) => l.name === "spec")) || null;
243
+ } catch {
244
+ return null;
245
+ }
246
+ }
247
+ export {
248
+ syncSpecWithEnhancedContent
249
+ };
@@ -0,0 +1,150 @@
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
+ import { readIssueKey } from "./metadata-paths.js";
5
+ import * as path from "path";
6
+ import * as fs from "fs/promises";
7
+ async function syncSpecToJiraWithEnhancedContent(options) {
8
+ const { specPath, domain, 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);
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 epic with:");
43
+ console.log(` Summary: ${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 (!dryRun && (!domain || !project)) {
52
+ return {
53
+ success: false,
54
+ action: "error",
55
+ error: "JIRA domain/project not specified (required for actual sync)"
56
+ };
57
+ }
58
+ const result = {
59
+ success: true,
60
+ action: dryRun ? "no-change" : "created",
61
+ // Assume create if not dry run
62
+ tasksLinked: taskMapping?.tasks.length || 0
63
+ };
64
+ if (domain && project && !dryRun) {
65
+ // Read real JIRA key from metadata if available
66
+ const rootDir = await findSpecWeaveRoot(specPath);
67
+ const metadataPath = path.join(rootDir, '.specweave', 'increments', specId, 'metadata.json');
68
+ let realKey = null;
69
+ try {
70
+ const meta = JSON.parse(await fs.readFile(metadataPath, 'utf-8'));
71
+ realKey = readIssueKey(meta);
72
+ } catch { /* no metadata */ }
73
+
74
+ if (realKey) {
75
+ result.epicKey = realKey;
76
+ result.epicUrl = `https://${domain}/browse/${realKey}`;
77
+ } else {
78
+ // No real JIRA key — return null instead of placeholder
79
+ result.epicKey = null;
80
+ result.epicUrl = null;
81
+ if (verbose) {
82
+ console.log(`\u26A0\uFE0F JIRA API integration not implemented in this file`);
83
+ console.log(` Use jira-spec-sync.ts for actual JIRA synchronization`);
84
+ }
85
+ }
86
+ }
87
+ return result;
88
+ } catch (error) {
89
+ return {
90
+ success: false,
91
+ action: "error",
92
+ error: error.message
93
+ };
94
+ }
95
+ }
96
+ async function findSpecWeaveRoot(specPath) {
97
+ let currentDir = path.dirname(specPath);
98
+ while (true) {
99
+ const specweaveDir = path.join(currentDir, ".specweave");
100
+ try {
101
+ await fs.access(specweaveDir);
102
+ return currentDir;
103
+ } catch {
104
+ const parentDir = path.dirname(currentDir);
105
+ if (parentDir === currentDir) {
106
+ throw new Error(".specweave directory not found");
107
+ }
108
+ currentDir = parentDir;
109
+ }
110
+ }
111
+ }
112
+ function buildTaskMapping(increments) {
113
+ if (increments.length === 0) return void 0;
114
+ const firstIncrement = increments[0];
115
+ const tasks = firstIncrement.tasks.map((task) => ({
116
+ id: task.id,
117
+ title: task.title,
118
+ userStories: task.userStories
119
+ }));
120
+ return {
121
+ incrementId: firstIncrement.id,
122
+ tasks,
123
+ tasksUrl: `tasks.md`
124
+ // JIRA doesn't support external links in same way
125
+ };
126
+ }
127
+ async function findArchitectureDocs(rootDir, specId) {
128
+ const docs = [];
129
+ const archDir = path.join(rootDir, ".specweave/docs/internal/architecture");
130
+ try {
131
+ const adrDir = path.join(archDir, "adr");
132
+ try {
133
+ const adrs = await fs.readdir(adrDir);
134
+ const relatedAdrs = adrs.filter((file) => file.includes(specId.replace("spec-", "")));
135
+ for (const adr of relatedAdrs) {
136
+ docs.push({
137
+ type: "adr",
138
+ path: path.join(adrDir, adr),
139
+ title: adr.replace(".md", "").replace(/-/g, " ")
140
+ });
141
+ }
142
+ } catch {
143
+ }
144
+ } catch {
145
+ }
146
+ return docs;
147
+ }
148
+ export {
149
+ syncSpecToJiraWithEnhancedContent
150
+ };