specweave 0.28.17 → 0.28.20

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 (204) hide show
  1. package/dist/plugins/specweave-ado/lib/ado-board-resolver.d.ts +94 -0
  2. package/dist/plugins/specweave-ado/lib/ado-board-resolver.d.ts.map +1 -0
  3. package/dist/plugins/specweave-ado/lib/ado-board-resolver.js +219 -0
  4. package/dist/plugins/specweave-ado/lib/ado-board-resolver.js.map +1 -0
  5. package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts +16 -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 +63 -3
  8. package/dist/plugins/specweave-ado/lib/ado-spec-sync.js.map +1 -1
  9. package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts +12 -3
  10. package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts.map +1 -1
  11. package/dist/plugins/specweave-ado/lib/ado-status-sync.js +37 -3
  12. package/dist/plugins/specweave-ado/lib/ado-status-sync.js.map +1 -1
  13. package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts +6 -11
  14. package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -1
  15. package/dist/plugins/specweave-github/lib/github-feature-sync.js +6 -11
  16. package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
  17. package/dist/plugins/specweave-github/lib/github-increment-sync-cli.d.ts +21 -0
  18. package/dist/plugins/specweave-github/lib/github-increment-sync-cli.d.ts.map +1 -0
  19. package/dist/plugins/specweave-github/lib/github-increment-sync-cli.js +445 -0
  20. package/dist/plugins/specweave-github/lib/github-increment-sync-cli.js.map +1 -0
  21. package/dist/plugins/specweave-github/lib/github-status-sync.d.ts +10 -0
  22. package/dist/plugins/specweave-github/lib/github-status-sync.d.ts.map +1 -1
  23. package/dist/plugins/specweave-github/lib/github-status-sync.js +40 -2
  24. package/dist/plugins/specweave-github/lib/github-status-sync.js.map +1 -1
  25. package/dist/plugins/specweave-github/lib/increment-issue-builder.d.ts +94 -0
  26. package/dist/plugins/specweave-github/lib/increment-issue-builder.d.ts.map +1 -0
  27. package/dist/plugins/specweave-github/lib/increment-issue-builder.js +369 -0
  28. package/dist/plugins/specweave-github/lib/increment-issue-builder.js.map +1 -0
  29. package/dist/plugins/specweave-jira/lib/jira-board-resolver.d.ts +50 -0
  30. package/dist/plugins/specweave-jira/lib/jira-board-resolver.d.ts.map +1 -0
  31. package/dist/plugins/specweave-jira/lib/jira-board-resolver.js +84 -0
  32. package/dist/plugins/specweave-jira/lib/jira-board-resolver.js.map +1 -0
  33. package/dist/plugins/specweave-jira/lib/jira-spec-sync.d.ts +12 -0
  34. package/dist/plugins/specweave-jira/lib/jira-spec-sync.d.ts.map +1 -1
  35. package/dist/plugins/specweave-jira/lib/jira-spec-sync.js +57 -5
  36. package/dist/plugins/specweave-jira/lib/jira-spec-sync.js.map +1 -1
  37. package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts +5 -1
  38. package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts.map +1 -1
  39. package/dist/plugins/specweave-jira/lib/jira-status-sync.js +12 -4
  40. package/dist/plugins/specweave-jira/lib/jira-status-sync.js.map +1 -1
  41. package/dist/src/cli/commands/import-external.d.ts.map +1 -1
  42. package/dist/src/cli/commands/import-external.js +12 -7
  43. package/dist/src/cli/commands/import-external.js.map +1 -1
  44. package/dist/src/cli/helpers/init/external-import.d.ts.map +1 -1
  45. package/dist/src/cli/helpers/init/external-import.js +308 -36
  46. package/dist/src/cli/helpers/init/external-import.js.map +1 -1
  47. package/dist/src/cli/helpers/init/jira-ado-auto-detect.d.ts +115 -0
  48. package/dist/src/cli/helpers/init/jira-ado-auto-detect.d.ts.map +1 -0
  49. package/dist/src/cli/helpers/init/jira-ado-auto-detect.js +590 -0
  50. package/dist/src/cli/helpers/init/jira-ado-auto-detect.js.map +1 -0
  51. package/dist/src/cli/helpers/issue-tracker/ado-area-selection.d.ts +65 -0
  52. package/dist/src/cli/helpers/issue-tracker/ado-area-selection.d.ts.map +1 -0
  53. package/dist/src/cli/helpers/issue-tracker/ado-area-selection.js +278 -0
  54. package/dist/src/cli/helpers/issue-tracker/ado-area-selection.js.map +1 -0
  55. package/dist/src/cli/helpers/issue-tracker/jira-board-selection.d.ts +64 -0
  56. package/dist/src/cli/helpers/issue-tracker/jira-board-selection.d.ts.map +1 -0
  57. package/dist/src/cli/helpers/issue-tracker/jira-board-selection.js +251 -0
  58. package/dist/src/cli/helpers/issue-tracker/jira-board-selection.js.map +1 -0
  59. package/dist/src/config/types.d.ts +6 -6
  60. package/dist/src/core/ac-test-validator-cli.js +4 -1
  61. package/dist/src/core/ac-test-validator-cli.js.map +1 -1
  62. package/dist/src/core/ac-test-validator.d.ts.map +1 -1
  63. package/dist/src/core/ac-test-validator.js +4 -1
  64. package/dist/src/core/ac-test-validator.js.map +1 -1
  65. package/dist/src/core/background/index.d.ts +11 -0
  66. package/dist/src/core/background/index.d.ts.map +1 -0
  67. package/dist/src/core/background/index.js +11 -0
  68. package/dist/src/core/background/index.js.map +1 -0
  69. package/dist/src/core/background/job-manager.d.ts +65 -0
  70. package/dist/src/core/background/job-manager.d.ts.map +1 -0
  71. package/dist/src/core/background/job-manager.js +192 -0
  72. package/dist/src/core/background/job-manager.js.map +1 -0
  73. package/dist/src/core/background/types.d.ts +59 -0
  74. package/dist/src/core/background/types.d.ts.map +1 -0
  75. package/dist/src/core/background/types.js +8 -0
  76. package/dist/src/core/background/types.js.map +1 -0
  77. package/dist/src/core/repo-structure/multi-repo-configurator.d.ts +25 -0
  78. package/dist/src/core/repo-structure/multi-repo-configurator.d.ts.map +1 -0
  79. package/dist/src/core/repo-structure/multi-repo-configurator.js +614 -0
  80. package/dist/src/core/repo-structure/multi-repo-configurator.js.map +1 -0
  81. package/dist/src/core/repo-structure/repo-initializer.d.ts +40 -0
  82. package/dist/src/core/repo-structure/repo-initializer.d.ts.map +1 -0
  83. package/dist/src/core/repo-structure/repo-initializer.js +252 -0
  84. package/dist/src/core/repo-structure/repo-initializer.js.map +1 -0
  85. package/dist/src/core/repo-structure/repo-structure-manager.d.ts +3 -37
  86. package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
  87. package/dist/src/core/repo-structure/repo-structure-manager.js +23 -803
  88. package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
  89. package/dist/src/core/types/increment-metadata.d.ts +75 -0
  90. package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
  91. package/dist/src/core/types/spec-metadata.d.ts +2 -0
  92. package/dist/src/core/types/spec-metadata.d.ts.map +1 -1
  93. package/dist/src/core/types/sync-profile.d.ts +137 -5
  94. package/dist/src/core/types/sync-profile.d.ts.map +1 -1
  95. package/dist/src/core/types/sync-profile.js +63 -0
  96. package/dist/src/core/types/sync-profile.js.map +1 -1
  97. package/dist/src/importers/external-importer.d.ts +25 -0
  98. package/dist/src/importers/external-importer.d.ts.map +1 -1
  99. package/dist/src/importers/github-importer.d.ts.map +1 -1
  100. package/dist/src/importers/github-importer.js +5 -3
  101. package/dist/src/importers/github-importer.js.map +1 -1
  102. package/dist/src/importers/import-coordinator.d.ts +20 -0
  103. package/dist/src/importers/import-coordinator.d.ts.map +1 -1
  104. package/dist/src/importers/import-coordinator.js.map +1 -1
  105. package/dist/src/importers/item-converter.d.ts +51 -0
  106. package/dist/src/importers/item-converter.d.ts.map +1 -1
  107. package/dist/src/importers/item-converter.js +39 -12
  108. package/dist/src/importers/item-converter.js.map +1 -1
  109. package/dist/src/init/architecture/types.d.ts +2 -2
  110. package/dist/src/init/compliance/types.d.ts +1 -1
  111. package/dist/src/init/repo/types.d.ts +1 -1
  112. package/dist/src/living-docs/fs-id-allocator.d.ts +72 -3
  113. package/dist/src/living-docs/fs-id-allocator.d.ts.map +1 -1
  114. package/dist/src/living-docs/fs-id-allocator.js +142 -16
  115. package/dist/src/living-docs/fs-id-allocator.js.map +1 -1
  116. package/dist/src/locales/de/cli.json +14 -0
  117. package/dist/src/locales/es/cli.json +14 -0
  118. package/dist/src/locales/fr/cli.json +14 -0
  119. package/dist/src/locales/ja/cli.json +14 -0
  120. package/dist/src/locales/ko/cli.json +14 -0
  121. package/dist/src/locales/pt/cli.json +14 -0
  122. package/dist/src/locales/ru/cli.json +14 -0
  123. package/dist/src/locales/zh/cli.json +14 -0
  124. package/dist/src/utils/chalk-fallback.d.ts +38 -0
  125. package/dist/src/utils/chalk-fallback.d.ts.map +1 -0
  126. package/dist/src/utils/chalk-fallback.js +118 -0
  127. package/dist/src/utils/chalk-fallback.js.map +1 -0
  128. package/dist/src/utils/project-id-generator.d.ts +127 -0
  129. package/dist/src/utils/project-id-generator.d.ts.map +1 -0
  130. package/dist/src/utils/project-id-generator.js +228 -0
  131. package/dist/src/utils/project-id-generator.js.map +1 -0
  132. package/package.json +1 -1
  133. package/plugins/specweave/agents/pm/AGENT.md +202 -0
  134. package/plugins/specweave/commands/specweave-import-external.md +5 -3
  135. package/plugins/specweave/commands/specweave-jobs.md +160 -0
  136. package/plugins/specweave/commands/specweave-sync-docs.md +6 -2
  137. package/plugins/specweave/hooks/pre-task-completion.sh +35 -17
  138. package/plugins/specweave/lib/vendor/core/ac-test-validator-cli.d.ts +16 -0
  139. package/plugins/specweave/lib/vendor/core/ac-test-validator-cli.js +121 -0
  140. package/plugins/specweave/lib/vendor/core/ac-test-validator-cli.js.map +1 -0
  141. package/plugins/specweave/lib/vendor/core/ac-test-validator.d.ts +111 -0
  142. package/plugins/specweave/lib/vendor/core/ac-test-validator.js +295 -0
  143. package/plugins/specweave/lib/vendor/core/ac-test-validator.js.map +1 -0
  144. package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +75 -0
  145. package/plugins/specweave/lib/vendor/utils/chalk-fallback.d.ts +38 -0
  146. package/plugins/specweave/lib/vendor/utils/chalk-fallback.js +118 -0
  147. package/plugins/specweave/lib/vendor/utils/chalk-fallback.js.map +1 -0
  148. package/plugins/specweave/lib/vendor/utils/fs-native.d.ts +179 -0
  149. package/plugins/specweave/lib/vendor/utils/fs-native.js +319 -0
  150. package/plugins/specweave/lib/vendor/utils/fs-native.js.map +1 -0
  151. package/plugins/specweave/skills/code-reviewer/SKILL.md +1 -1
  152. package/plugins/specweave/skills/docs-updater/SKILL.md +61 -0
  153. package/plugins/specweave/skills/increment-planner/SKILL.md +10 -335
  154. package/plugins/specweave/skills/increment-planner/templates/metadata.json +13 -0
  155. package/plugins/specweave/skills/increment-planner/templates/plan.md +50 -0
  156. package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +86 -0
  157. package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +50 -0
  158. package/plugins/specweave/skills/increment-planner/templates/tasks-multi-project.md +86 -0
  159. package/plugins/specweave/skills/increment-planner/templates/tasks-single-project.md +48 -0
  160. package/plugins/specweave-ado/commands/specweave-ado-import-areas.md +358 -0
  161. package/plugins/specweave-ado/lib/ado-spec-sync.js +59 -3
  162. package/plugins/specweave-ado/lib/ado-spec-sync.ts +72 -3
  163. package/plugins/specweave-ado/lib/ado-status-sync.js +35 -3
  164. package/plugins/specweave-ado/lib/ado-status-sync.ts +48 -4
  165. package/plugins/specweave-alternatives/skills/architecture-alternatives/SKILL.md +1 -0
  166. package/plugins/specweave-alternatives/skills/bmad-method/SKILL.md +1 -0
  167. package/plugins/specweave-core/skills/code-quality/SKILL.md +1 -0
  168. package/plugins/specweave-core/skills/design-patterns/SKILL.md +1 -0
  169. package/plugins/specweave-core/skills/software-architecture/SKILL.md +1 -0
  170. package/plugins/specweave-github/commands/specweave-github-cleanup-duplicates.md +14 -10
  171. package/plugins/specweave-github/commands/specweave-github-sync.md +57 -0
  172. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +74 -0
  173. package/plugins/specweave-github/lib/github-feature-sync.ts +6 -11
  174. package/plugins/specweave-github/lib/github-increment-sync-cli.js +456 -0
  175. package/plugins/specweave-github/lib/github-increment-sync-cli.ts +588 -0
  176. package/plugins/specweave-github/lib/github-status-sync.js +37 -1
  177. package/plugins/specweave-github/lib/github-status-sync.ts +60 -4
  178. package/plugins/specweave-github/lib/increment-issue-builder.js +389 -0
  179. package/plugins/specweave-github/lib/increment-issue-builder.ts +502 -0
  180. package/plugins/specweave-github/skills/github-issue-standard/SKILL.md +19 -24
  181. package/plugins/specweave-infrastructure/agents/observability-engineer/AGENT.md +15 -23
  182. package/plugins/specweave-jira/commands/specweave-jira-import-boards.md +331 -0
  183. package/plugins/specweave-jira/lib/jira-spec-sync.js +53 -5
  184. package/plugins/specweave-jira/lib/jira-spec-sync.ts +87 -7
  185. package/plugins/specweave-jira/lib/jira-status-sync.js +9 -3
  186. package/plugins/specweave-jira/lib/jira-status-sync.ts +15 -6
  187. package/plugins/specweave-ml/agents/data-scientist/AGENT.md +16 -20
  188. package/plugins/specweave-ml/agents/ml-engineer/AGENT.md +18 -19
  189. package/plugins/specweave-ml/skills/{ml-pipeline-workflow → mlops-dag-builder}/SKILL.md +18 -14
  190. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +111 -0
  191. package/plugins/specweave-ui/skills/browser-automation/SKILL.md +1 -1
  192. package/plugins/specweave-ui/skills/ui-testing/SKILL.md +10 -122
  193. package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts +0 -70
  194. package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts.map +0 -1
  195. package/dist/plugins/specweave-github/lib/epic-content-builder.js +0 -258
  196. package/dist/plugins/specweave-github/lib/epic-content-builder.js.map +0 -1
  197. package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts +0 -83
  198. package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts.map +0 -1
  199. package/dist/plugins/specweave-github/lib/github-epic-sync.js +0 -466
  200. package/dist/plugins/specweave-github/lib/github-epic-sync.js.map +0 -1
  201. package/plugins/specweave-github/lib/epic-content-builder.js +0 -265
  202. package/plugins/specweave-github/lib/epic-content-builder.ts +0 -376
  203. package/plugins/specweave-github/lib/github-epic-sync.js +0 -488
  204. package/plugins/specweave-github/lib/github-epic-sync.ts +0 -715
@@ -0,0 +1,502 @@
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
+ priority?: string;
26
+ }
27
+
28
+ export interface AcceptanceCriterion {
29
+ id: string;
30
+ description: string;
31
+ completed: boolean;
32
+ }
33
+
34
+ export interface UserStory {
35
+ id: string;
36
+ title: string;
37
+ asA: string;
38
+ iWant: string;
39
+ soThat: string;
40
+ acceptanceCriteria: AcceptanceCriterion[];
41
+ priority?: string;
42
+ }
43
+
44
+ export interface Task {
45
+ id: string;
46
+ title: string;
47
+ completed: boolean;
48
+ userStories: string[];
49
+ priority?: string;
50
+ description?: string;
51
+ }
52
+
53
+ export interface IncrementData {
54
+ frontmatter: IncrementFrontmatter;
55
+ title: string;
56
+ problemStatement: string;
57
+ userStories: UserStory[];
58
+ tasks: Task[];
59
+ outOfScope: string[];
60
+ }
61
+
62
+ export interface GitHubIssueContent {
63
+ title: string;
64
+ body: string;
65
+ labels: string[];
66
+ }
67
+
68
+ export class IncrementIssueBuilder {
69
+ private incrementPath: string;
70
+ private projectRoot: string;
71
+
72
+ constructor(incrementPath: string, projectRoot: string) {
73
+ this.incrementPath = incrementPath;
74
+ this.projectRoot = projectRoot;
75
+ }
76
+
77
+ /**
78
+ * Parse increment spec.md and extract all data
79
+ */
80
+ async parse(): Promise<IncrementData> {
81
+ const specPath = path.join(this.incrementPath, 'spec.md');
82
+
83
+ if (!existsSync(specPath)) {
84
+ throw new Error(`spec.md not found at ${specPath}`);
85
+ }
86
+
87
+ const content = await readFile(specPath, 'utf-8');
88
+
89
+ // Extract YAML frontmatter
90
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
91
+ if (!frontmatterMatch) {
92
+ throw new Error('spec.md missing YAML frontmatter');
93
+ }
94
+
95
+ const frontmatter = yaml.parse(frontmatterMatch[1]) as IncrementFrontmatter;
96
+ const bodyContent = content.slice(frontmatterMatch[0].length).trim();
97
+
98
+ // Extract main title
99
+ const titleMatch = bodyContent.match(/^#\s+(.+)$/m);
100
+ const title = titleMatch ? titleMatch[1].trim() : frontmatter.increment;
101
+
102
+ // Extract problem statement
103
+ const problemStatement = this.extractSection(bodyContent, 'Problem Statement');
104
+
105
+ // Extract user stories
106
+ const userStories = this.extractUserStories(bodyContent);
107
+
108
+ // Extract out of scope
109
+ const outOfScopeSection = this.extractSection(bodyContent, 'Out of Scope');
110
+ const outOfScope = outOfScopeSection
111
+ .split('\n')
112
+ .filter(line => line.startsWith('-'))
113
+ .map(line => line.replace(/^-\s*/, '').trim());
114
+
115
+ // Extract tasks from tasks.md
116
+ const tasks = await this.extractTasks();
117
+
118
+ return {
119
+ frontmatter,
120
+ title,
121
+ problemStatement,
122
+ userStories,
123
+ tasks,
124
+ outOfScope,
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Extract tasks from tasks.md
130
+ */
131
+ private async extractTasks(): Promise<Task[]> {
132
+ const tasksPath = path.join(this.incrementPath, 'tasks.md');
133
+
134
+ if (!existsSync(tasksPath)) {
135
+ return [];
136
+ }
137
+
138
+ try {
139
+ const content = await readFile(tasksPath, 'utf-8');
140
+ const tasks: Task[] = [];
141
+
142
+ // Split by task headers: ### T-XXX: Title
143
+ const taskBlocks = content.split(/(?=###\s+T-\d+)/);
144
+
145
+ for (const block of taskBlocks) {
146
+ const headerMatch = block.match(/###\s+(T-\d+):\s*(.+)/);
147
+ if (!headerMatch) continue;
148
+
149
+ const id = headerMatch[1];
150
+ const title = headerMatch[2].trim();
151
+
152
+ // Extract status: **Status**: [x] completed or **Status**: [ ] pending
153
+ const statusMatch = block.match(/\*\*Status\*\*:\s*\[([x\s])\]/i);
154
+ const completed = statusMatch ? statusMatch[1].toLowerCase() === 'x' : false;
155
+
156
+ // Extract user stories: **User Story**: US-001 or **Satisfies ACs**: AC-US1-01
157
+ const userStoryMatch = block.match(/\*\*User Story\*\*:\s*([^\n]+)/i);
158
+ const satisfiesMatch = block.match(/\*\*Satisfies ACs?\*\*:\s*([^\n]+)/i);
159
+
160
+ let userStories: string[] = [];
161
+ if (userStoryMatch) {
162
+ userStories = userStoryMatch[1].split(',').map(s => s.trim());
163
+ } else if (satisfiesMatch) {
164
+ // Extract US IDs from AC IDs (AC-US1-01 → US-001)
165
+ const acIds = satisfiesMatch[1].split(',').map(s => s.trim());
166
+ const usIds = new Set<string>();
167
+ for (const ac of acIds) {
168
+ const usMatch = ac.match(/AC-(US\d+)-/i);
169
+ if (usMatch) {
170
+ usIds.add(usMatch[1]);
171
+ }
172
+ }
173
+ userStories = Array.from(usIds);
174
+ }
175
+
176
+ // Extract priority
177
+ const priorityMatch = block.match(/\*\*Priority\*\*:\s*(P\d)/i);
178
+ const priority = priorityMatch ? priorityMatch[1] : undefined;
179
+
180
+ tasks.push({
181
+ id,
182
+ title,
183
+ completed,
184
+ userStories,
185
+ priority,
186
+ });
187
+ }
188
+
189
+ return tasks;
190
+ } catch {
191
+ return [];
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Extract a section by heading
197
+ */
198
+ private extractSection(content: string, heading: string): string {
199
+ const regex = new RegExp(`##\\s+${heading}\\s*\\n([\\s\\S]*?)(?=\\n##\\s|$)`, 'i');
200
+ const match = content.match(regex);
201
+ return match ? match[1].trim() : '';
202
+ }
203
+
204
+ /**
205
+ * Extract user stories from spec.md
206
+ */
207
+ private extractUserStories(content: string): UserStory[] {
208
+ const stories: UserStory[] = [];
209
+
210
+ // Find User Stories section
211
+ const userStoriesSection = this.extractSection(content, 'User Stories');
212
+ if (!userStoriesSection) {
213
+ return stories;
214
+ }
215
+
216
+ // Split by ### US-XXX headers
217
+ const storyBlocks = userStoriesSection.split(/(?=###\s+US-\d+)/);
218
+
219
+ for (const block of storyBlocks) {
220
+ const headerMatch = block.match(/###\s+(US-\d+):\s*(.+)/);
221
+ if (!headerMatch) continue;
222
+
223
+ const id = headerMatch[1];
224
+ const title = headerMatch[2].trim();
225
+
226
+ // Extract As a/I want/So that
227
+ const asAMatch = block.match(/\*\*As a\*\*\s+([^\n]+)/i);
228
+ const iWantMatch = block.match(/\*\*I want\*\*\s+([^\n]+)/i);
229
+ const soThatMatch = block.match(/\*\*So that\*\*\s+([^\n]+)/i);
230
+
231
+ // Extract Acceptance Criteria
232
+ const acceptanceCriteria = this.extractAcceptanceCriteria(block, id);
233
+
234
+ stories.push({
235
+ id,
236
+ title,
237
+ asA: asAMatch ? asAMatch[1].trim().replace(/,$/, '') : '',
238
+ iWant: iWantMatch ? iWantMatch[1].trim().replace(/,$/, '') : '',
239
+ soThat: soThatMatch ? soThatMatch[1].trim().replace(/\.$/, '') : '',
240
+ acceptanceCriteria,
241
+ });
242
+ }
243
+
244
+ return stories;
245
+ }
246
+
247
+ /**
248
+ * Extract acceptance criteria from a user story block
249
+ */
250
+ private extractAcceptanceCriteria(block: string, userStoryId: string): AcceptanceCriterion[] {
251
+ const criteria: AcceptanceCriterion[] = [];
252
+
253
+ // Pattern: - [x] **AC-US1-01**: Description or - [ ] **AC-US1-01**: Description
254
+ const acPattern = /-\s*\[([ x])\]\s*\*\*AC-(US\d+)-(\d+)\*\*:\s*(.+)/gi;
255
+
256
+ let match;
257
+ while ((match = acPattern.exec(block)) !== null) {
258
+ const completed = match[1].toLowerCase() === 'x';
259
+ const usNum = match[2];
260
+ const acNum = match[3];
261
+ const description = match[4].trim();
262
+
263
+ criteria.push({
264
+ id: `AC-${usNum}-${acNum}`,
265
+ description,
266
+ completed,
267
+ });
268
+ }
269
+
270
+ return criteria;
271
+ }
272
+
273
+ /**
274
+ * Build GitHub issue for a single user story
275
+ */
276
+ buildUserStoryIssue(
277
+ story: UserStory,
278
+ incrementData: IncrementData,
279
+ githubRepo?: string
280
+ ): GitHubIssueContent {
281
+ const featureId = incrementData.frontmatter.feature_id || this.generateFeatureId(incrementData);
282
+ const incrementId = incrementData.frontmatter.increment;
283
+
284
+ // Title format: [FS-XXX][US-YYY] User Story Title
285
+ const title = `[${featureId}][${story.id}] ${story.title}`;
286
+
287
+ // Build body
288
+ let body = '';
289
+
290
+ // Header with metadata
291
+ body += `**Feature**: ${featureId}\n`;
292
+ body += `**Status**: ${incrementData.frontmatter.status || 'planning'}\n`;
293
+ body += `**Priority**: P1\n`;
294
+ body += `\n---\n\n`;
295
+
296
+ // User Story description
297
+ body += `## User Story\n\n`;
298
+ if (story.asA && story.iWant && story.soThat) {
299
+ body += `**As a** ${story.asA}\n`;
300
+ body += `**I want** ${story.iWant}\n`;
301
+ body += `**So that** ${story.soThat}\n\n`;
302
+ } else {
303
+ body += `${story.title}\n\n`;
304
+ }
305
+
306
+ body += `---\n\n`;
307
+
308
+ // Acceptance Criteria (checkable!)
309
+ body += `## Acceptance Criteria\n\n`;
310
+ if (story.acceptanceCriteria.length > 0) {
311
+ const completed = story.acceptanceCriteria.filter(ac => ac.completed).length;
312
+ const total = story.acceptanceCriteria.length;
313
+ const percentage = total > 0 ? Math.round((completed / total) * 100) : 0;
314
+ body += `Progress: ${completed}/${total} criteria met (${percentage}%)\n\n`;
315
+
316
+ for (const ac of story.acceptanceCriteria) {
317
+ const checkbox = ac.completed ? '[x]' : '[ ]';
318
+ body += `- ${checkbox} **${ac.id}**: ${ac.description}\n`;
319
+ }
320
+ body += '\n';
321
+ } else {
322
+ body += `*No acceptance criteria defined*\n\n`;
323
+ }
324
+
325
+ body += `---\n\n`;
326
+
327
+ // Tasks for this user story
328
+ const storyTasks = incrementData.tasks.filter(t =>
329
+ t.userStories.includes(story.id) ||
330
+ t.userStories.some(us => us.toUpperCase() === story.id.toUpperCase())
331
+ );
332
+
333
+ if (storyTasks.length > 0) {
334
+ body += `## Tasks\n\n`;
335
+ const completedTasks = storyTasks.filter(t => t.completed).length;
336
+ body += `Progress: ${completedTasks}/${storyTasks.length} tasks\n\n`;
337
+
338
+ for (const task of storyTasks) {
339
+ const checkbox = task.completed ? '[x]' : '[ ]';
340
+ body += `- ${checkbox} **${task.id}**: ${task.title}\n`;
341
+ }
342
+ body += '\n---\n\n';
343
+ }
344
+
345
+ // Link to increment
346
+ body += `## Implementation\n\n`;
347
+ if (githubRepo) {
348
+ body += `**Increment**: [${incrementId}](https://github.com/${githubRepo}/tree/develop/.specweave/increments/${incrementId})\n\n`;
349
+ } else {
350
+ body += `**Increment**: ${incrementId}\n\n`;
351
+ }
352
+
353
+ body += `---\n\n`;
354
+ body += `🤖 Auto-synced by SpecWeave Increment Sync`;
355
+
356
+ // Labels
357
+ const labels = ['specweave', 'user-story'];
358
+
359
+ // Add type label if available
360
+ if (incrementData.frontmatter.type) {
361
+ labels.push(incrementData.frontmatter.type.toLowerCase());
362
+ }
363
+
364
+ // Add priority label (from story or default to p2)
365
+ const priority = story.priority?.toLowerCase() || incrementData.frontmatter.priority?.toLowerCase() || 'p2';
366
+ labels.push(priority);
367
+
368
+ return { title, body, labels };
369
+ }
370
+
371
+ /**
372
+ * Build GitHub issue for the entire increment (epic-style)
373
+ */
374
+ buildIncrementIssue(
375
+ incrementData: IncrementData,
376
+ githubRepo?: string
377
+ ): GitHubIssueContent {
378
+ const featureId = incrementData.frontmatter.feature_id || this.generateFeatureId(incrementData);
379
+ const incrementId = incrementData.frontmatter.increment;
380
+
381
+ // Title format: [FS-XXX] Increment Title
382
+ const title = `[${featureId}] ${incrementData.title}`;
383
+
384
+ // Build body
385
+ let body = '';
386
+
387
+ // Header with metadata
388
+ body += `**Increment**: ${incrementId}\n`;
389
+ body += `**Status**: ${incrementData.frontmatter.status || 'planning'}\n`;
390
+ body += `**Priority**: P0 (Critical)\n`;
391
+
392
+ // Calculate progress
393
+ const totalACs = incrementData.userStories.reduce((sum, us) => sum + us.acceptanceCriteria.length, 0);
394
+ const completedACs = incrementData.userStories.reduce(
395
+ (sum, us) => sum + us.acceptanceCriteria.filter(ac => ac.completed).length, 0
396
+ );
397
+ const percentage = totalACs > 0 ? Math.round((completedACs / totalACs) * 100) : 0;
398
+ body += `**Progress**: ${completedACs}/${totalACs} ACs (${percentage}%)\n`;
399
+
400
+ body += `\n---\n\n`;
401
+
402
+ // Overview
403
+ body += `## Overview\n\n`;
404
+ body += incrementData.problemStatement || incrementData.title;
405
+ body += `\n\n---\n\n`;
406
+
407
+ // User Stories with ACs
408
+ body += `## User Stories\n\n`;
409
+ for (const story of incrementData.userStories) {
410
+ const usCompleted = story.acceptanceCriteria.filter(ac => ac.completed).length;
411
+ const usTotal = story.acceptanceCriteria.length;
412
+ const usCheckbox = usCompleted === usTotal && usTotal > 0 ? '[x]' : '[ ]';
413
+
414
+ body += `### ${usCheckbox} ${story.id}: ${story.title}\n\n`;
415
+
416
+ if (story.asA && story.iWant && story.soThat) {
417
+ body += `> **As a** ${story.asA}, **I want** ${story.iWant}, **So that** ${story.soThat}\n\n`;
418
+ }
419
+
420
+ body += `**Acceptance Criteria:**\n`;
421
+ for (const ac of story.acceptanceCriteria) {
422
+ const checkbox = ac.completed ? '[x]' : '[ ]';
423
+ body += `- ${checkbox} **${ac.id}**: ${ac.description}\n`;
424
+ }
425
+ body += '\n';
426
+ }
427
+
428
+ body += `---\n\n`;
429
+
430
+ // Tasks section
431
+ if (incrementData.tasks.length > 0) {
432
+ body += `## Tasks\n\n`;
433
+ const completedTasks = incrementData.tasks.filter(t => t.completed).length;
434
+ const totalTasks = incrementData.tasks.length;
435
+ const taskPercentage = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0;
436
+ body += `Progress: ${completedTasks}/${totalTasks} tasks (${taskPercentage}%)\n\n`;
437
+
438
+ for (const task of incrementData.tasks) {
439
+ const checkbox = task.completed ? '[x]' : '[ ]';
440
+ body += `- ${checkbox} **${task.id}**: ${task.title}\n`;
441
+ if (task.priority || task.userStories.length > 0) {
442
+ const parts: string[] = [];
443
+ if (task.priority) parts.push(`Priority: ${task.priority}`);
444
+ if (task.userStories.length > 0) parts.push(`User ${task.userStories.length === 1 ? 'Story' : 'Stories'}: ${task.userStories.join(', ')}`);
445
+ body += ` - ${parts.join(' | ')}\n`;
446
+ }
447
+ }
448
+ body += '\n---\n\n';
449
+ }
450
+
451
+ // Links to increment files
452
+ body += `## SpecWeave Increment\n\n`;
453
+ if (githubRepo) {
454
+ body += `- **Spec**: [\`spec.md\`](https://github.com/${githubRepo}/blob/develop/.specweave/increments/${incrementId}/spec.md)\n`;
455
+ body += `- **Plan**: [\`plan.md\`](https://github.com/${githubRepo}/blob/develop/.specweave/increments/${incrementId}/plan.md)\n`;
456
+ body += `- **Tasks**: [\`tasks.md\`](https://github.com/${githubRepo}/blob/develop/.specweave/increments/${incrementId}/tasks.md)\n`;
457
+ } else {
458
+ body += `- **Spec**: \`spec.md\`\n`;
459
+ body += `- **Plan**: \`plan.md\`\n`;
460
+ body += `- **Tasks**: \`tasks.md\`\n`;
461
+ }
462
+
463
+ body += `\n---\n\n`;
464
+ body += `🤖 Auto-synced by SpecWeave Increment Sync`;
465
+
466
+ // Labels
467
+ const labels = ['specweave', 'increment'];
468
+
469
+ // Add type label (default to 'enhancement' if not specified)
470
+ const typeLabel = incrementData.frontmatter.type?.toLowerCase() || 'enhancement';
471
+ labels.push(typeLabel);
472
+
473
+ // Add priority label (default to 'p2' if not specified)
474
+ const priority = incrementData.frontmatter.priority?.toLowerCase() || 'p2';
475
+ labels.push(priority);
476
+
477
+ return { title, body, labels };
478
+ }
479
+
480
+ /**
481
+ * Generate a feature ID if not present in frontmatter
482
+ * Uses date-based format: FS-YY-MM-DD
483
+ */
484
+ private generateFeatureId(incrementData: IncrementData): string {
485
+ // Try to extract increment number
486
+ const incrementNum = incrementData.frontmatter.increment.match(/^(\d+)/)?.[1];
487
+ if (incrementNum) {
488
+ return `FS-${incrementNum.padStart(3, '0')}`;
489
+ }
490
+
491
+ // Fallback to date-based
492
+ const created = incrementData.frontmatter.created;
493
+ if (created) {
494
+ const match = created.match(/^(\d{4})-(\d{2})-(\d{2})/);
495
+ if (match) {
496
+ return `FS-${match[1].slice(2)}-${match[2]}-${match[3]}`;
497
+ }
498
+ }
499
+
500
+ return 'FS-UNKNOWN';
501
+ }
502
+ }
@@ -167,35 +167,30 @@ Progress: 3/6 tasks complete (50%)
167
167
  🤖 Auto-synced by SpecWeave
168
168
  ```
169
169
 
170
- ## Migration Guide
171
-
172
- ### Update Existing Builders
170
+ ## Implementation
173
171
 
174
- All content builders must be updated to follow this standard:
172
+ ### Content Builders
175
173
 
176
- 1. **EpicContentBuilder** (`plugins/specweave-github/lib/epic-content-builder.ts`)
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
- 2. **SpecContentSync** (`src/core/spec-content-sync.ts`)
183
- - TODO: Update `buildExternalDescription` to use GitHub URLs
184
- - TODO: Pass repo parameter to builders
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
- 3. **Increment Sync** (bash scripts and TypeScript)
187
- - TODO: Use UserStoryContentBuilder as reference
188
- - TODO: Ensure all issue creation uses this format
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
- ### Update Commands
187
+ ### Commands
191
188
 
192
- All commands that create GitHub issues must use this standard:
189
+ All GitHub sync commands use the Universal Hierarchy:
193
190
 
194
- - `/specweave-github:create-issue` - Use standard format
195
- - `/specweave-github:sync` - Use standard format
196
- - `/specweave-github:sync-epic` - Use standard format
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
- - **Reference Implementation**: `plugins/specweave-github/lib/user-story-content-builder.ts`
231
- - **Test Script**: `.specweave/increments/0032-*/scripts/update-us-004-fixed.mjs`
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: Build production-ready monitoring, logging, and tracing systems. Implements comprehensive observability strategies, SLI/SLO management, and incident response workflows. Use PROACTIVELY for monitoring infrastructure, performance optimization, or production reliability.
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 for Large Monitoring Setups
11
+ ## ⚠️ Chunking Rule
12
12
 
13
- When generating comprehensive monitoring and observability setups that exceed 1000 lines (e.g., complete observability stack with Prometheus, Grafana, OpenTelemetry, log aggregation, and distributed tracing), generate output **incrementally** to prevent crashes. Break large monitoring implementations into logical components (e.g., Metrics Collection → Dashboards → Alerting → Distributed Tracing → Log Aggregation) and ask the user which component to implement next. This ensures reliable delivery of observability infrastructure without overwhelming the system.
13
+ Large monitoring stacks (Prometheus + Grafana + OpenTelemetry + logs) = 1000+ lines. Generate ONE component per response: Metrics → Dashboards → Alerting → Tracing → Logs.
14
14
 
15
- You are an observability engineer specializing in production-grade monitoring, logging, tracing, and reliability systems for enterprise-scale applications.
15
+ ## How to Invoke This Agent
16
16
 
17
- ## 🚀 How to Invoke This Agent
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 comprehensive monitoring stack with Prometheus, Grafana, and OpenTelemetry for microservices with SLI/SLO tracking",
27
- model: "haiku" // optional: haiku, sonnet, opus
22
+ prompt: "Design monitoring for microservices with SLI/SLO tracking"
28
23
  });
29
24
  ```
30
25
 
31
- **Naming Convention**: `{plugin}:{directory}:{yaml-name-or-directory-name}`
32
- - **Plugin**: specweave-infrastructure
33
- - **Directory**: observability-engineer
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
- **When to Use**:
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
- ## Purpose
44
- Expert observability engineer specializing in comprehensive monitoring strategies, distributed tracing, and production reliability systems. Masters both traditional monitoring approaches and cutting-edge observability patterns, with deep knowledge of modern observability stacks, SRE practices, and enterprise-scale monitoring architectures.
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