specweave 0.28.17 → 0.28.19
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-board-resolver.d.ts +94 -0
- package/dist/plugins/specweave-ado/lib/ado-board-resolver.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-board-resolver.js +219 -0
- package/dist/plugins/specweave-ado/lib/ado-board-resolver.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts +6 -11
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.js +6 -11
- package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-increment-sync-cli.d.ts +19 -0
- package/dist/plugins/specweave-github/lib/github-increment-sync-cli.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-increment-sync-cli.js +380 -0
- package/dist/plugins/specweave-github/lib/github-increment-sync-cli.js.map +1 -0
- package/dist/plugins/specweave-github/lib/increment-issue-builder.d.ts +92 -0
- package/dist/plugins/specweave-github/lib/increment-issue-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/increment-issue-builder.js +349 -0
- package/dist/plugins/specweave-github/lib/increment-issue-builder.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-board-resolver.d.ts +50 -0
- package/dist/plugins/specweave-jira/lib/jira-board-resolver.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-board-resolver.js +84 -0
- package/dist/plugins/specweave-jira/lib/jira-board-resolver.js.map +1 -0
- package/dist/src/cli/commands/import-external.d.ts.map +1 -1
- package/dist/src/cli/commands/import-external.js +12 -7
- package/dist/src/cli/commands/import-external.js.map +1 -1
- package/dist/src/cli/helpers/init/external-import.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/external-import.js +122 -17
- package/dist/src/cli/helpers/init/external-import.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado-area-selection.d.ts +65 -0
- package/dist/src/cli/helpers/issue-tracker/ado-area-selection.d.ts.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado-area-selection.js +278 -0
- package/dist/src/cli/helpers/issue-tracker/ado-area-selection.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/jira-board-selection.d.ts +64 -0
- package/dist/src/cli/helpers/issue-tracker/jira-board-selection.d.ts.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/jira-board-selection.js +251 -0
- package/dist/src/cli/helpers/issue-tracker/jira-board-selection.js.map +1 -0
- package/dist/src/core/ac-test-validator-cli.js +4 -1
- package/dist/src/core/ac-test-validator-cli.js.map +1 -1
- package/dist/src/core/ac-test-validator.d.ts.map +1 -1
- package/dist/src/core/ac-test-validator.js +4 -1
- package/dist/src/core/ac-test-validator.js.map +1 -1
- package/dist/src/core/types/increment-metadata.d.ts +75 -0
- package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
- package/dist/src/core/types/sync-profile.d.ts +137 -5
- package/dist/src/core/types/sync-profile.d.ts.map +1 -1
- package/dist/src/core/types/sync-profile.js +63 -0
- package/dist/src/core/types/sync-profile.js.map +1 -1
- package/dist/src/importers/external-importer.d.ts +25 -0
- package/dist/src/importers/external-importer.d.ts.map +1 -1
- package/dist/src/importers/github-importer.d.ts.map +1 -1
- package/dist/src/importers/github-importer.js +5 -3
- package/dist/src/importers/github-importer.js.map +1 -1
- package/dist/src/importers/item-converter.d.ts +51 -0
- package/dist/src/importers/item-converter.d.ts.map +1 -1
- package/dist/src/importers/item-converter.js +39 -12
- package/dist/src/importers/item-converter.js.map +1 -1
- package/dist/src/init/repo/types.d.ts +1 -1
- package/dist/src/living-docs/fs-id-allocator.d.ts +72 -3
- package/dist/src/living-docs/fs-id-allocator.d.ts.map +1 -1
- package/dist/src/living-docs/fs-id-allocator.js +142 -16
- package/dist/src/living-docs/fs-id-allocator.js.map +1 -1
- package/dist/src/locales/de/cli.json +14 -0
- package/dist/src/locales/es/cli.json +14 -0
- package/dist/src/locales/fr/cli.json +14 -0
- package/dist/src/locales/ja/cli.json +14 -0
- package/dist/src/locales/ko/cli.json +14 -0
- package/dist/src/locales/pt/cli.json +14 -0
- package/dist/src/locales/ru/cli.json +14 -0
- package/dist/src/locales/zh/cli.json +14 -0
- package/dist/src/utils/chalk-fallback.d.ts +38 -0
- package/dist/src/utils/chalk-fallback.d.ts.map +1 -0
- package/dist/src/utils/chalk-fallback.js +118 -0
- package/dist/src/utils/chalk-fallback.js.map +1 -0
- package/dist/src/utils/project-id-generator.d.ts +127 -0
- package/dist/src/utils/project-id-generator.d.ts.map +1 -0
- package/dist/src/utils/project-id-generator.js +228 -0
- package/dist/src/utils/project-id-generator.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/agents/pm/AGENT.md +202 -0
- package/plugins/specweave/commands/specweave-import-external.md +5 -3
- package/plugins/specweave/commands/specweave-sync-docs.md +6 -2
- package/plugins/specweave/hooks/pre-task-completion.sh +35 -17
- package/plugins/specweave/lib/vendor/core/ac-test-validator-cli.d.ts +16 -0
- package/plugins/specweave/lib/vendor/core/ac-test-validator-cli.js +121 -0
- package/plugins/specweave/lib/vendor/core/ac-test-validator-cli.js.map +1 -0
- package/plugins/specweave/lib/vendor/core/ac-test-validator.d.ts +111 -0
- package/plugins/specweave/lib/vendor/core/ac-test-validator.js +295 -0
- package/plugins/specweave/lib/vendor/core/ac-test-validator.js.map +1 -0
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +75 -0
- package/plugins/specweave/lib/vendor/utils/chalk-fallback.d.ts +38 -0
- package/plugins/specweave/lib/vendor/utils/chalk-fallback.js +118 -0
- package/plugins/specweave/lib/vendor/utils/chalk-fallback.js.map +1 -0
- package/plugins/specweave/lib/vendor/utils/fs-native.d.ts +179 -0
- package/plugins/specweave/lib/vendor/utils/fs-native.js +319 -0
- package/plugins/specweave/lib/vendor/utils/fs-native.js.map +1 -0
- package/plugins/specweave/skills/code-reviewer/SKILL.md +1 -1
- package/plugins/specweave/skills/docs-updater/SKILL.md +61 -0
- package/plugins/specweave/skills/increment-planner/SKILL.md +10 -335
- package/plugins/specweave/skills/increment-planner/templates/metadata.json +13 -0
- package/plugins/specweave/skills/increment-planner/templates/plan.md +50 -0
- package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +86 -0
- package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +50 -0
- package/plugins/specweave/skills/increment-planner/templates/tasks-multi-project.md +86 -0
- package/plugins/specweave/skills/increment-planner/templates/tasks-single-project.md +48 -0
- package/plugins/specweave-ado/commands/specweave-ado-import-areas.md +358 -0
- package/plugins/specweave-alternatives/skills/architecture-alternatives/SKILL.md +1 -0
- package/plugins/specweave-alternatives/skills/bmad-method/SKILL.md +1 -0
- package/plugins/specweave-core/skills/code-quality/SKILL.md +1 -0
- package/plugins/specweave-core/skills/design-patterns/SKILL.md +1 -0
- package/plugins/specweave-core/skills/software-architecture/SKILL.md +1 -0
- package/plugins/specweave-github/commands/specweave-github-cleanup-duplicates.md +14 -10
- package/plugins/specweave-github/commands/specweave-github-sync.md +57 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +68 -0
- package/plugins/specweave-github/lib/github-feature-sync.ts +6 -11
- package/plugins/specweave-github/lib/github-increment-sync-cli.js +343 -0
- package/plugins/specweave-github/lib/github-increment-sync-cli.ts +484 -0
- package/plugins/specweave-github/lib/increment-issue-builder.js +368 -0
- package/plugins/specweave-github/lib/increment-issue-builder.ts +471 -0
- package/plugins/specweave-github/skills/github-issue-standard/SKILL.md +19 -24
- package/plugins/specweave-infrastructure/agents/observability-engineer/AGENT.md +15 -23
- package/plugins/specweave-jira/commands/specweave-jira-import-boards.md +331 -0
- package/plugins/specweave-ml/agents/data-scientist/AGENT.md +16 -20
- package/plugins/specweave-ml/agents/ml-engineer/AGENT.md +18 -19
- package/plugins/specweave-ml/skills/{ml-pipeline-workflow → mlops-dag-builder}/SKILL.md +18 -14
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +102 -0
- package/plugins/specweave-ui/skills/browser-automation/SKILL.md +1 -1
- package/plugins/specweave-ui/skills/ui-testing/SKILL.md +10 -122
- package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts +0 -70
- package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts.map +0 -1
- package/dist/plugins/specweave-github/lib/epic-content-builder.js +0 -258
- package/dist/plugins/specweave-github/lib/epic-content-builder.js.map +0 -1
- package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts +0 -83
- package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts.map +0 -1
- package/dist/plugins/specweave-github/lib/github-epic-sync.js +0 -466
- package/dist/plugins/specweave-github/lib/github-epic-sync.js.map +0 -1
- package/plugins/specweave-github/lib/epic-content-builder.js +0 -265
- package/plugins/specweave-github/lib/epic-content-builder.ts +0 -376
- package/plugins/specweave-github/lib/github-epic-sync.js +0 -488
- package/plugins/specweave-github/lib/github-epic-sync.ts +0 -715
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Increment Issue Builder - Generate GitHub issues from increment spec.md
|
|
3
|
+
*
|
|
4
|
+
* For brownfield projects without full living docs structure,
|
|
5
|
+
* this builder extracts User Stories and ACs directly from spec.md
|
|
6
|
+
* and generates properly formatted GitHub issues.
|
|
7
|
+
*
|
|
8
|
+
* Correct format: [FS-XXX][US-YYY] User Story Title
|
|
9
|
+
* With ACs as checkable items in issue body.
|
|
10
|
+
*
|
|
11
|
+
* @see ADR-0143 (GitHub Issue Format)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readFile } from 'fs/promises';
|
|
15
|
+
import { existsSync } from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import * as yaml from 'yaml';
|
|
18
|
+
|
|
19
|
+
export interface IncrementFrontmatter {
|
|
20
|
+
increment: string;
|
|
21
|
+
feature_id?: string;
|
|
22
|
+
type?: string;
|
|
23
|
+
status?: string;
|
|
24
|
+
created?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AcceptanceCriterion {
|
|
28
|
+
id: string;
|
|
29
|
+
description: string;
|
|
30
|
+
completed: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface UserStory {
|
|
34
|
+
id: string;
|
|
35
|
+
title: string;
|
|
36
|
+
asA: string;
|
|
37
|
+
iWant: string;
|
|
38
|
+
soThat: string;
|
|
39
|
+
acceptanceCriteria: AcceptanceCriterion[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface Task {
|
|
43
|
+
id: string;
|
|
44
|
+
title: string;
|
|
45
|
+
completed: boolean;
|
|
46
|
+
userStories: string[];
|
|
47
|
+
priority?: string;
|
|
48
|
+
description?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface IncrementData {
|
|
52
|
+
frontmatter: IncrementFrontmatter;
|
|
53
|
+
title: string;
|
|
54
|
+
problemStatement: string;
|
|
55
|
+
userStories: UserStory[];
|
|
56
|
+
tasks: Task[];
|
|
57
|
+
outOfScope: string[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface GitHubIssueContent {
|
|
61
|
+
title: string;
|
|
62
|
+
body: string;
|
|
63
|
+
labels: string[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class IncrementIssueBuilder {
|
|
67
|
+
private incrementPath: string;
|
|
68
|
+
private projectRoot: string;
|
|
69
|
+
|
|
70
|
+
constructor(incrementPath: string, projectRoot: string) {
|
|
71
|
+
this.incrementPath = incrementPath;
|
|
72
|
+
this.projectRoot = projectRoot;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Parse increment spec.md and extract all data
|
|
77
|
+
*/
|
|
78
|
+
async parse(): Promise<IncrementData> {
|
|
79
|
+
const specPath = path.join(this.incrementPath, 'spec.md');
|
|
80
|
+
|
|
81
|
+
if (!existsSync(specPath)) {
|
|
82
|
+
throw new Error(`spec.md not found at ${specPath}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const content = await readFile(specPath, 'utf-8');
|
|
86
|
+
|
|
87
|
+
// Extract YAML frontmatter
|
|
88
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
89
|
+
if (!frontmatterMatch) {
|
|
90
|
+
throw new Error('spec.md missing YAML frontmatter');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const frontmatter = yaml.parse(frontmatterMatch[1]) as IncrementFrontmatter;
|
|
94
|
+
const bodyContent = content.slice(frontmatterMatch[0].length).trim();
|
|
95
|
+
|
|
96
|
+
// Extract main title
|
|
97
|
+
const titleMatch = bodyContent.match(/^#\s+(.+)$/m);
|
|
98
|
+
const title = titleMatch ? titleMatch[1].trim() : frontmatter.increment;
|
|
99
|
+
|
|
100
|
+
// Extract problem statement
|
|
101
|
+
const problemStatement = this.extractSection(bodyContent, 'Problem Statement');
|
|
102
|
+
|
|
103
|
+
// Extract user stories
|
|
104
|
+
const userStories = this.extractUserStories(bodyContent);
|
|
105
|
+
|
|
106
|
+
// Extract out of scope
|
|
107
|
+
const outOfScopeSection = this.extractSection(bodyContent, 'Out of Scope');
|
|
108
|
+
const outOfScope = outOfScopeSection
|
|
109
|
+
.split('\n')
|
|
110
|
+
.filter(line => line.startsWith('-'))
|
|
111
|
+
.map(line => line.replace(/^-\s*/, '').trim());
|
|
112
|
+
|
|
113
|
+
// Extract tasks from tasks.md
|
|
114
|
+
const tasks = await this.extractTasks();
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
frontmatter,
|
|
118
|
+
title,
|
|
119
|
+
problemStatement,
|
|
120
|
+
userStories,
|
|
121
|
+
tasks,
|
|
122
|
+
outOfScope,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Extract tasks from tasks.md
|
|
128
|
+
*/
|
|
129
|
+
private async extractTasks(): Promise<Task[]> {
|
|
130
|
+
const tasksPath = path.join(this.incrementPath, 'tasks.md');
|
|
131
|
+
|
|
132
|
+
if (!existsSync(tasksPath)) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const content = await readFile(tasksPath, 'utf-8');
|
|
138
|
+
const tasks: Task[] = [];
|
|
139
|
+
|
|
140
|
+
// Split by task headers: ### T-XXX: Title
|
|
141
|
+
const taskBlocks = content.split(/(?=###\s+T-\d+)/);
|
|
142
|
+
|
|
143
|
+
for (const block of taskBlocks) {
|
|
144
|
+
const headerMatch = block.match(/###\s+(T-\d+):\s*(.+)/);
|
|
145
|
+
if (!headerMatch) continue;
|
|
146
|
+
|
|
147
|
+
const id = headerMatch[1];
|
|
148
|
+
const title = headerMatch[2].trim();
|
|
149
|
+
|
|
150
|
+
// Extract status: **Status**: [x] completed or **Status**: [ ] pending
|
|
151
|
+
const statusMatch = block.match(/\*\*Status\*\*:\s*\[([x\s])\]/i);
|
|
152
|
+
const completed = statusMatch ? statusMatch[1].toLowerCase() === 'x' : false;
|
|
153
|
+
|
|
154
|
+
// Extract user stories: **User Story**: US-001 or **Satisfies ACs**: AC-US1-01
|
|
155
|
+
const userStoryMatch = block.match(/\*\*User Story\*\*:\s*([^\n]+)/i);
|
|
156
|
+
const satisfiesMatch = block.match(/\*\*Satisfies ACs?\*\*:\s*([^\n]+)/i);
|
|
157
|
+
|
|
158
|
+
let userStories: string[] = [];
|
|
159
|
+
if (userStoryMatch) {
|
|
160
|
+
userStories = userStoryMatch[1].split(',').map(s => s.trim());
|
|
161
|
+
} else if (satisfiesMatch) {
|
|
162
|
+
// Extract US IDs from AC IDs (AC-US1-01 → US-001)
|
|
163
|
+
const acIds = satisfiesMatch[1].split(',').map(s => s.trim());
|
|
164
|
+
const usIds = new Set<string>();
|
|
165
|
+
for (const ac of acIds) {
|
|
166
|
+
const usMatch = ac.match(/AC-(US\d+)-/i);
|
|
167
|
+
if (usMatch) {
|
|
168
|
+
usIds.add(usMatch[1]);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
userStories = Array.from(usIds);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Extract priority
|
|
175
|
+
const priorityMatch = block.match(/\*\*Priority\*\*:\s*(P\d)/i);
|
|
176
|
+
const priority = priorityMatch ? priorityMatch[1] : undefined;
|
|
177
|
+
|
|
178
|
+
tasks.push({
|
|
179
|
+
id,
|
|
180
|
+
title,
|
|
181
|
+
completed,
|
|
182
|
+
userStories,
|
|
183
|
+
priority,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return tasks;
|
|
188
|
+
} catch {
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Extract a section by heading
|
|
195
|
+
*/
|
|
196
|
+
private extractSection(content: string, heading: string): string {
|
|
197
|
+
const regex = new RegExp(`##\\s+${heading}\\s*\\n([\\s\\S]*?)(?=\\n##\\s|$)`, 'i');
|
|
198
|
+
const match = content.match(regex);
|
|
199
|
+
return match ? match[1].trim() : '';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Extract user stories from spec.md
|
|
204
|
+
*/
|
|
205
|
+
private extractUserStories(content: string): UserStory[] {
|
|
206
|
+
const stories: UserStory[] = [];
|
|
207
|
+
|
|
208
|
+
// Find User Stories section
|
|
209
|
+
const userStoriesSection = this.extractSection(content, 'User Stories');
|
|
210
|
+
if (!userStoriesSection) {
|
|
211
|
+
return stories;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Split by ### US-XXX headers
|
|
215
|
+
const storyBlocks = userStoriesSection.split(/(?=###\s+US-\d+)/);
|
|
216
|
+
|
|
217
|
+
for (const block of storyBlocks) {
|
|
218
|
+
const headerMatch = block.match(/###\s+(US-\d+):\s*(.+)/);
|
|
219
|
+
if (!headerMatch) continue;
|
|
220
|
+
|
|
221
|
+
const id = headerMatch[1];
|
|
222
|
+
const title = headerMatch[2].trim();
|
|
223
|
+
|
|
224
|
+
// Extract As a/I want/So that
|
|
225
|
+
const asAMatch = block.match(/\*\*As a\*\*\s+([^\n]+)/i);
|
|
226
|
+
const iWantMatch = block.match(/\*\*I want\*\*\s+([^\n]+)/i);
|
|
227
|
+
const soThatMatch = block.match(/\*\*So that\*\*\s+([^\n]+)/i);
|
|
228
|
+
|
|
229
|
+
// Extract Acceptance Criteria
|
|
230
|
+
const acceptanceCriteria = this.extractAcceptanceCriteria(block, id);
|
|
231
|
+
|
|
232
|
+
stories.push({
|
|
233
|
+
id,
|
|
234
|
+
title,
|
|
235
|
+
asA: asAMatch ? asAMatch[1].trim().replace(/,$/, '') : '',
|
|
236
|
+
iWant: iWantMatch ? iWantMatch[1].trim().replace(/,$/, '') : '',
|
|
237
|
+
soThat: soThatMatch ? soThatMatch[1].trim().replace(/\.$/, '') : '',
|
|
238
|
+
acceptanceCriteria,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return stories;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Extract acceptance criteria from a user story block
|
|
247
|
+
*/
|
|
248
|
+
private extractAcceptanceCriteria(block: string, userStoryId: string): AcceptanceCriterion[] {
|
|
249
|
+
const criteria: AcceptanceCriterion[] = [];
|
|
250
|
+
|
|
251
|
+
// Pattern: - [x] **AC-US1-01**: Description or - [ ] **AC-US1-01**: Description
|
|
252
|
+
const acPattern = /-\s*\[([ x])\]\s*\*\*AC-(US\d+)-(\d+)\*\*:\s*(.+)/gi;
|
|
253
|
+
|
|
254
|
+
let match;
|
|
255
|
+
while ((match = acPattern.exec(block)) !== null) {
|
|
256
|
+
const completed = match[1].toLowerCase() === 'x';
|
|
257
|
+
const usNum = match[2];
|
|
258
|
+
const acNum = match[3];
|
|
259
|
+
const description = match[4].trim();
|
|
260
|
+
|
|
261
|
+
criteria.push({
|
|
262
|
+
id: `AC-${usNum}-${acNum}`,
|
|
263
|
+
description,
|
|
264
|
+
completed,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return criteria;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Build GitHub issue for a single user story
|
|
273
|
+
*/
|
|
274
|
+
buildUserStoryIssue(
|
|
275
|
+
story: UserStory,
|
|
276
|
+
incrementData: IncrementData,
|
|
277
|
+
githubRepo?: string
|
|
278
|
+
): GitHubIssueContent {
|
|
279
|
+
const featureId = incrementData.frontmatter.feature_id || this.generateFeatureId(incrementData);
|
|
280
|
+
const incrementId = incrementData.frontmatter.increment;
|
|
281
|
+
|
|
282
|
+
// Title format: [FS-XXX][US-YYY] User Story Title
|
|
283
|
+
const title = `[${featureId}][${story.id}] ${story.title}`;
|
|
284
|
+
|
|
285
|
+
// Build body
|
|
286
|
+
let body = '';
|
|
287
|
+
|
|
288
|
+
// Header with metadata
|
|
289
|
+
body += `**Feature**: ${featureId}\n`;
|
|
290
|
+
body += `**Status**: ${incrementData.frontmatter.status || 'planning'}\n`;
|
|
291
|
+
body += `**Priority**: P1\n`;
|
|
292
|
+
body += `\n---\n\n`;
|
|
293
|
+
|
|
294
|
+
// User Story description
|
|
295
|
+
body += `## User Story\n\n`;
|
|
296
|
+
if (story.asA && story.iWant && story.soThat) {
|
|
297
|
+
body += `**As a** ${story.asA}\n`;
|
|
298
|
+
body += `**I want** ${story.iWant}\n`;
|
|
299
|
+
body += `**So that** ${story.soThat}\n\n`;
|
|
300
|
+
} else {
|
|
301
|
+
body += `${story.title}\n\n`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
body += `---\n\n`;
|
|
305
|
+
|
|
306
|
+
// Acceptance Criteria (checkable!)
|
|
307
|
+
body += `## Acceptance Criteria\n\n`;
|
|
308
|
+
if (story.acceptanceCriteria.length > 0) {
|
|
309
|
+
const completed = story.acceptanceCriteria.filter(ac => ac.completed).length;
|
|
310
|
+
const total = story.acceptanceCriteria.length;
|
|
311
|
+
const percentage = total > 0 ? Math.round((completed / total) * 100) : 0;
|
|
312
|
+
body += `Progress: ${completed}/${total} criteria met (${percentage}%)\n\n`;
|
|
313
|
+
|
|
314
|
+
for (const ac of story.acceptanceCriteria) {
|
|
315
|
+
const checkbox = ac.completed ? '[x]' : '[ ]';
|
|
316
|
+
body += `- ${checkbox} **${ac.id}**: ${ac.description}\n`;
|
|
317
|
+
}
|
|
318
|
+
body += '\n';
|
|
319
|
+
} else {
|
|
320
|
+
body += `*No acceptance criteria defined*\n\n`;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
body += `---\n\n`;
|
|
324
|
+
|
|
325
|
+
// Link to increment
|
|
326
|
+
body += `## Implementation\n\n`;
|
|
327
|
+
if (githubRepo) {
|
|
328
|
+
body += `**Increment**: [${incrementId}](https://github.com/${githubRepo}/tree/develop/.specweave/increments/${incrementId})\n\n`;
|
|
329
|
+
} else {
|
|
330
|
+
body += `**Increment**: ${incrementId}\n\n`;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
body += `---\n\n`;
|
|
334
|
+
body += `🤖 Auto-synced by SpecWeave Increment Sync`;
|
|
335
|
+
|
|
336
|
+
// Labels
|
|
337
|
+
const labels = ['specweave', 'user-story'];
|
|
338
|
+
if (incrementData.frontmatter.type) {
|
|
339
|
+
labels.push(incrementData.frontmatter.type);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return { title, body, labels };
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Build GitHub issue for the entire increment (epic-style)
|
|
347
|
+
*/
|
|
348
|
+
buildIncrementIssue(
|
|
349
|
+
incrementData: IncrementData,
|
|
350
|
+
githubRepo?: string
|
|
351
|
+
): GitHubIssueContent {
|
|
352
|
+
const featureId = incrementData.frontmatter.feature_id || this.generateFeatureId(incrementData);
|
|
353
|
+
const incrementId = incrementData.frontmatter.increment;
|
|
354
|
+
|
|
355
|
+
// Title format: [FS-XXX] Increment Title
|
|
356
|
+
const title = `[${featureId}] ${incrementData.title}`;
|
|
357
|
+
|
|
358
|
+
// Build body
|
|
359
|
+
let body = '';
|
|
360
|
+
|
|
361
|
+
// Header with metadata
|
|
362
|
+
body += `**Increment**: ${incrementId}\n`;
|
|
363
|
+
body += `**Status**: ${incrementData.frontmatter.status || 'planning'}\n`;
|
|
364
|
+
body += `**Priority**: P0 (Critical)\n`;
|
|
365
|
+
|
|
366
|
+
// Calculate progress
|
|
367
|
+
const totalACs = incrementData.userStories.reduce((sum, us) => sum + us.acceptanceCriteria.length, 0);
|
|
368
|
+
const completedACs = incrementData.userStories.reduce(
|
|
369
|
+
(sum, us) => sum + us.acceptanceCriteria.filter(ac => ac.completed).length, 0
|
|
370
|
+
);
|
|
371
|
+
const percentage = totalACs > 0 ? Math.round((completedACs / totalACs) * 100) : 0;
|
|
372
|
+
body += `**Progress**: ${completedACs}/${totalACs} ACs (${percentage}%)\n`;
|
|
373
|
+
|
|
374
|
+
body += `\n---\n\n`;
|
|
375
|
+
|
|
376
|
+
// Overview
|
|
377
|
+
body += `## Overview\n\n`;
|
|
378
|
+
body += incrementData.problemStatement || incrementData.title;
|
|
379
|
+
body += `\n\n---\n\n`;
|
|
380
|
+
|
|
381
|
+
// User Stories with ACs
|
|
382
|
+
body += `## User Stories\n\n`;
|
|
383
|
+
for (const story of incrementData.userStories) {
|
|
384
|
+
const usCompleted = story.acceptanceCriteria.filter(ac => ac.completed).length;
|
|
385
|
+
const usTotal = story.acceptanceCriteria.length;
|
|
386
|
+
const usCheckbox = usCompleted === usTotal && usTotal > 0 ? '[x]' : '[ ]';
|
|
387
|
+
|
|
388
|
+
body += `### ${usCheckbox} ${story.id}: ${story.title}\n\n`;
|
|
389
|
+
|
|
390
|
+
if (story.asA && story.iWant && story.soThat) {
|
|
391
|
+
body += `> **As a** ${story.asA}, **I want** ${story.iWant}, **So that** ${story.soThat}\n\n`;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
body += `**Acceptance Criteria:**\n`;
|
|
395
|
+
for (const ac of story.acceptanceCriteria) {
|
|
396
|
+
const checkbox = ac.completed ? '[x]' : '[ ]';
|
|
397
|
+
body += `- ${checkbox} **${ac.id}**: ${ac.description}\n`;
|
|
398
|
+
}
|
|
399
|
+
body += '\n';
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
body += `---\n\n`;
|
|
403
|
+
|
|
404
|
+
// Tasks section
|
|
405
|
+
if (incrementData.tasks.length > 0) {
|
|
406
|
+
body += `## Tasks\n\n`;
|
|
407
|
+
const completedTasks = incrementData.tasks.filter(t => t.completed).length;
|
|
408
|
+
const totalTasks = incrementData.tasks.length;
|
|
409
|
+
const taskPercentage = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0;
|
|
410
|
+
body += `Progress: ${completedTasks}/${totalTasks} tasks (${taskPercentage}%)\n\n`;
|
|
411
|
+
|
|
412
|
+
for (const task of incrementData.tasks) {
|
|
413
|
+
const checkbox = task.completed ? '[x]' : '[ ]';
|
|
414
|
+
body += `- ${checkbox} **${task.id}**: ${task.title}\n`;
|
|
415
|
+
if (task.priority || task.userStories.length > 0) {
|
|
416
|
+
const parts: string[] = [];
|
|
417
|
+
if (task.priority) parts.push(`Priority: ${task.priority}`);
|
|
418
|
+
if (task.userStories.length > 0) parts.push(`User ${task.userStories.length === 1 ? 'Story' : 'Stories'}: ${task.userStories.join(', ')}`);
|
|
419
|
+
body += ` - ${parts.join(' | ')}\n`;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
body += '\n---\n\n';
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Links to increment files
|
|
426
|
+
body += `## SpecWeave Increment\n\n`;
|
|
427
|
+
if (githubRepo) {
|
|
428
|
+
body += `- **Spec**: [\`spec.md\`](https://github.com/${githubRepo}/blob/develop/.specweave/increments/${incrementId}/spec.md)\n`;
|
|
429
|
+
body += `- **Plan**: [\`plan.md\`](https://github.com/${githubRepo}/blob/develop/.specweave/increments/${incrementId}/plan.md)\n`;
|
|
430
|
+
body += `- **Tasks**: [\`tasks.md\`](https://github.com/${githubRepo}/blob/develop/.specweave/increments/${incrementId}/tasks.md)\n`;
|
|
431
|
+
} else {
|
|
432
|
+
body += `- **Spec**: \`spec.md\`\n`;
|
|
433
|
+
body += `- **Plan**: \`plan.md\`\n`;
|
|
434
|
+
body += `- **Tasks**: \`tasks.md\`\n`;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
body += `\n---\n\n`;
|
|
438
|
+
body += `🤖 Auto-synced by SpecWeave Increment Sync`;
|
|
439
|
+
|
|
440
|
+
// Labels
|
|
441
|
+
const labels = ['specweave', 'increment', 'enhancement'];
|
|
442
|
+
if (incrementData.frontmatter.type) {
|
|
443
|
+
labels.push(incrementData.frontmatter.type);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return { title, body, labels };
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Generate a feature ID if not present in frontmatter
|
|
451
|
+
* Uses date-based format: FS-YY-MM-DD
|
|
452
|
+
*/
|
|
453
|
+
private generateFeatureId(incrementData: IncrementData): string {
|
|
454
|
+
// Try to extract increment number
|
|
455
|
+
const incrementNum = incrementData.frontmatter.increment.match(/^(\d+)/)?.[1];
|
|
456
|
+
if (incrementNum) {
|
|
457
|
+
return `FS-${incrementNum.padStart(3, '0')}`;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Fallback to date-based
|
|
461
|
+
const created = incrementData.frontmatter.created;
|
|
462
|
+
if (created) {
|
|
463
|
+
const match = created.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
|
464
|
+
if (match) {
|
|
465
|
+
return `FS-${match[1].slice(2)}-${match[2]}-${match[3]}`;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return 'FS-UNKNOWN';
|
|
470
|
+
}
|
|
471
|
+
}
|
|
@@ -167,35 +167,30 @@ Progress: 3/6 tasks complete (50%)
|
|
|
167
167
|
🤖 Auto-synced by SpecWeave
|
|
168
168
|
```
|
|
169
169
|
|
|
170
|
-
##
|
|
171
|
-
|
|
172
|
-
### Update Existing Builders
|
|
170
|
+
## Implementation
|
|
173
171
|
|
|
174
|
-
|
|
172
|
+
### Content Builders
|
|
175
173
|
|
|
176
|
-
|
|
177
|
-
- ✅ TODO: Add `githubRepo` parameter
|
|
178
|
-
- ✅ TODO: Use GitHub URLs instead of relative paths
|
|
179
|
-
- ✅ TODO: Extract priority from ACs
|
|
180
|
-
- ✅ TODO: Remove Project field
|
|
174
|
+
All GitHub issue content is generated by these builders:
|
|
181
175
|
|
|
182
|
-
|
|
183
|
-
-
|
|
184
|
-
-
|
|
176
|
+
1. **UserStoryIssueBuilder** (`plugins/specweave-github/lib/user-story-issue-builder.ts`)
|
|
177
|
+
- Creates issues from `us-*.md` files
|
|
178
|
+
- Generates `[FS-XXX][US-YYY] Title` format
|
|
179
|
+
- Extracts ACs and tasks as checkboxes
|
|
180
|
+
- Uses GitHub URLs (not relative paths)
|
|
185
181
|
|
|
186
|
-
|
|
187
|
-
-
|
|
188
|
-
-
|
|
182
|
+
2. **GitHubFeatureSync** (`plugins/specweave-github/lib/github-feature-sync.ts`)
|
|
183
|
+
- Syncs Features as GitHub Milestones
|
|
184
|
+
- Syncs User Stories as GitHub Issues via UserStoryIssueBuilder
|
|
185
|
+
- Universal Hierarchy: Feature → Milestone, User Story → Issue
|
|
189
186
|
|
|
190
|
-
###
|
|
187
|
+
### Commands
|
|
191
188
|
|
|
192
|
-
All
|
|
189
|
+
All GitHub sync commands use the Universal Hierarchy:
|
|
193
190
|
|
|
194
|
-
- `/specweave-github:
|
|
195
|
-
- `/specweave-github:
|
|
196
|
-
- `/specweave-github:
|
|
197
|
-
- `/specweave-github:sync-spec` - Use standard format
|
|
198
|
-
- `/specweave-github:update-user-story` - Already uses standard ✅
|
|
191
|
+
- `/specweave-github:sync` - Sync increments via Feature/UserStory hierarchy
|
|
192
|
+
- `/specweave-github:create-issue` - Create issue using standard format
|
|
193
|
+
- `/specweave-github:update-user-story` - Update user story issue
|
|
199
194
|
|
|
200
195
|
## Validation Checklist
|
|
201
196
|
|
|
@@ -227,6 +222,6 @@ No exceptions. No shortcuts. Every issue follows this standard.
|
|
|
227
222
|
|
|
228
223
|
## Related Files
|
|
229
224
|
|
|
230
|
-
- **
|
|
231
|
-
- **
|
|
225
|
+
- **User Story Builder**: `plugins/specweave-github/lib/user-story-issue-builder.ts`
|
|
226
|
+
- **Feature Sync**: `plugins/specweave-github/lib/github-feature-sync.ts`
|
|
232
227
|
- **Example Issue**: https://github.com/anton-abyzov/specweave/issues/501
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: observability-engineer
|
|
3
|
-
description:
|
|
3
|
+
description: Production observability architect - metrics, logs, traces, SLOs. Opinionated on OpenTelemetry-first, Prometheus+Grafana stack, alert fatigue prevention. Activates for monitoring, observability, SLI/SLO, alerting, Prometheus, Grafana, tracing, logging, Datadog, New Relic.
|
|
4
4
|
model: claude-sonnet-4-5-20250929
|
|
5
5
|
model_preference: haiku
|
|
6
6
|
cost_profile: execution
|
|
@@ -8,40 +8,32 @@ fallback_behavior: flexible
|
|
|
8
8
|
max_response_tokens: 2000
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## ⚠️ Chunking
|
|
11
|
+
## ⚠️ Chunking Rule
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Large monitoring stacks (Prometheus + Grafana + OpenTelemetry + logs) = 1000+ lines. Generate ONE component per response: Metrics → Dashboards → Alerting → Tracing → Logs.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
## How to Invoke This Agent
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
**Subagent Type**: `specweave-infrastructure:observability-engineer:observability-engineer`
|
|
20
|
-
|
|
21
|
-
**Usage Example**:
|
|
17
|
+
**Agent**: `specweave-infrastructure:observability-engineer:observability-engineer`
|
|
22
18
|
|
|
23
19
|
```typescript
|
|
24
20
|
Task({
|
|
25
21
|
subagent_type: "specweave-infrastructure:observability-engineer:observability-engineer",
|
|
26
|
-
prompt: "Design
|
|
27
|
-
model: "haiku" // optional: haiku, sonnet, opus
|
|
22
|
+
prompt: "Design monitoring for microservices with SLI/SLO tracking"
|
|
28
23
|
});
|
|
29
24
|
```
|
|
30
25
|
|
|
31
|
-
**
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- **Agent Name**: observability-engineer
|
|
26
|
+
**Use When**: Monitoring architecture, distributed tracing, alerting, SLO tracking, log aggregation.
|
|
27
|
+
|
|
28
|
+
## Philosophy: Opinionated Observability
|
|
35
29
|
|
|
36
|
-
**
|
|
37
|
-
- You need to design monitoring and observability architecture
|
|
38
|
-
- You want to set up distributed tracing for microservices
|
|
39
|
-
- You need to configure alerting and SLO tracking
|
|
40
|
-
- You're troubleshooting performance issues or anomalies
|
|
41
|
-
- You want to implement comprehensive log aggregation and analysis
|
|
30
|
+
**I follow the "Three Pillars" model but with strong opinions:**
|
|
42
31
|
|
|
43
|
-
|
|
44
|
-
|
|
32
|
+
1. **OpenTelemetry First** - Vendor-neutral instrumentation. Don't lock into proprietary agents.
|
|
33
|
+
2. **Prometheus + Grafana Default** - Unless you need managed (then DataDog/New Relic).
|
|
34
|
+
3. **SLOs Before Alerts** - Define what "good" means before alerting on "bad".
|
|
35
|
+
4. **Alert on Symptoms, Not Causes** - "Users see errors" not "CPU high".
|
|
36
|
+
5. **Fewer, Louder Alerts** - Alert fatigue kills on-call. Max 5 critical alerts per service.
|
|
45
37
|
|
|
46
38
|
## Capabilities
|
|
47
39
|
|