specweave 0.30.16 → 0.30.18

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 (80) hide show
  1. package/CLAUDE.md +43 -0
  2. package/dist/src/cli/commands/plan/increment-detector.js +2 -2
  3. package/dist/src/cli/commands/plan/increment-detector.js.map +1 -1
  4. package/dist/src/cli/commands/plan/plan-orchestrator.js +1 -1
  5. package/dist/src/cli/commands/plan/plan-orchestrator.js.map +1 -1
  6. package/dist/src/cli/commands/plan/plan-validator.js +1 -1
  7. package/dist/src/cli/commands/plan/plan-validator.js.map +1 -1
  8. package/dist/src/cli/workers/living-docs-worker.js +80 -44
  9. package/dist/src/cli/workers/living-docs-worker.js.map +1 -1
  10. package/dist/src/core/increment/active-increment-manager.js +1 -1
  11. package/dist/src/core/increment/active-increment-manager.js.map +1 -1
  12. package/dist/src/core/increment/metadata-manager.d.ts +1 -1
  13. package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
  14. package/dist/src/core/increment/metadata-manager.js +7 -7
  15. package/dist/src/core/increment/metadata-manager.js.map +1 -1
  16. package/dist/src/core/living-docs/hierarchy-mapper.d.ts.map +1 -1
  17. package/dist/src/core/living-docs/hierarchy-mapper.js +12 -18
  18. package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
  19. package/dist/src/core/living-docs/living-docs-sync.d.ts +49 -24
  20. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  21. package/dist/src/core/living-docs/living-docs-sync.js +207 -96
  22. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  23. package/dist/src/core/llm/index.d.ts +1 -0
  24. package/dist/src/core/llm/index.d.ts.map +1 -1
  25. package/dist/src/core/llm/index.js +2 -0
  26. package/dist/src/core/llm/index.js.map +1 -1
  27. package/dist/src/core/llm/providers/anthropic-provider.d.ts.map +1 -1
  28. package/dist/src/core/llm/providers/anthropic-provider.js +15 -26
  29. package/dist/src/core/llm/providers/anthropic-provider.js.map +1 -1
  30. package/dist/src/core/llm/providers/azure-openai-provider.d.ts.map +1 -1
  31. package/dist/src/core/llm/providers/azure-openai-provider.js +13 -5
  32. package/dist/src/core/llm/providers/azure-openai-provider.js.map +1 -1
  33. package/dist/src/core/llm/providers/bedrock-provider.d.ts.map +1 -1
  34. package/dist/src/core/llm/providers/bedrock-provider.js +12 -8
  35. package/dist/src/core/llm/providers/bedrock-provider.js.map +1 -1
  36. package/dist/src/core/llm/providers/claude-code-provider.d.ts.map +1 -1
  37. package/dist/src/core/llm/providers/claude-code-provider.js +15 -25
  38. package/dist/src/core/llm/providers/claude-code-provider.js.map +1 -1
  39. package/dist/src/core/llm/providers/ollama-provider.d.ts.map +1 -1
  40. package/dist/src/core/llm/providers/ollama-provider.js +12 -9
  41. package/dist/src/core/llm/providers/ollama-provider.js.map +1 -1
  42. package/dist/src/core/llm/providers/openai-provider.d.ts.map +1 -1
  43. package/dist/src/core/llm/providers/openai-provider.js +13 -6
  44. package/dist/src/core/llm/providers/openai-provider.js.map +1 -1
  45. package/dist/src/core/llm/providers/vertex-ai-provider.d.ts.map +1 -1
  46. package/dist/src/core/llm/providers/vertex-ai-provider.js +12 -8
  47. package/dist/src/core/llm/providers/vertex-ai-provider.js.map +1 -1
  48. package/dist/src/utils/feature-id-collision.d.ts +28 -0
  49. package/dist/src/utils/feature-id-collision.d.ts.map +1 -1
  50. package/dist/src/utils/feature-id-collision.js +83 -0
  51. package/dist/src/utils/feature-id-collision.js.map +1 -1
  52. package/dist/src/utils/llm-json-extractor.d.ts +105 -0
  53. package/dist/src/utils/llm-json-extractor.d.ts.map +1 -0
  54. package/dist/src/utils/llm-json-extractor.js +336 -0
  55. package/dist/src/utils/llm-json-extractor.js.map +1 -0
  56. package/dist/src/utils/structure-level-detector.d.ts +105 -0
  57. package/dist/src/utils/structure-level-detector.d.ts.map +1 -0
  58. package/dist/src/utils/structure-level-detector.js +388 -0
  59. package/dist/src/utils/structure-level-detector.js.map +1 -0
  60. package/package.json +1 -1
  61. package/plugins/specweave/commands/specweave-increment.md +57 -9
  62. package/plugins/specweave/commands/specweave-qa.md +34 -4
  63. package/plugins/specweave/commands/specweave-sync-specs.md +37 -6
  64. package/plugins/specweave/commands/specweave-validate.md +41 -1
  65. package/plugins/specweave/hooks/hooks.json +10 -0
  66. package/plugins/specweave/hooks/spec-project-validator.sh +111 -0
  67. package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js +1 -1
  68. package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js.map +1 -1
  69. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +1 -1
  70. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +7 -7
  71. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
  72. package/plugins/specweave/skills/increment-planner/SKILL.md +109 -10
  73. package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +2 -0
  74. package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +1 -0
  75. package/plugins/specweave/skills/increment-quality-judge-v2/SKILL.md +41 -2
  76. package/plugins/specweave/skills/multi-project-spec-mapper/SKILL.md +24 -1
  77. package/plugins/specweave/skills/spec-generator/SKILL.md +18 -0
  78. package/plugins/specweave/skills/specweave-framework/SKILL.md +25 -0
  79. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +14 -0
  80. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +21 -0
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Structure Level Detector
3
+ *
4
+ * Detects whether the project uses 1-level or 2-level folder structure
5
+ * for living docs specs:
6
+ *
7
+ * - 1-Level: internal/specs/{project}/FS-XXX/
8
+ * - 2-Level: internal/specs/{project}/{board}/FS-XXX/
9
+ *
10
+ * 2-level is used when:
11
+ * - ADO area path mapping is configured (project → area paths)
12
+ * - JIRA board mapping is configured with multiple boards per project
13
+ * - Umbrella with team-based organization
14
+ *
15
+ * @module utils/structure-level-detector
16
+ */
17
+ /**
18
+ * Structure level configuration
19
+ */
20
+ export interface StructureLevelConfig {
21
+ /** Whether this is 1-level or 2-level structure */
22
+ level: 1 | 2;
23
+ /** Available projects (for 1-level) or top-level containers (for 2-level) */
24
+ projects: ProjectInfo[];
25
+ /** Available boards (only for 2-level) - organized by project */
26
+ boardsByProject?: Record<string, BoardInfo[]>;
27
+ /** Detection reason for debugging */
28
+ detectionReason: string;
29
+ /** Source of configuration */
30
+ source: 'ado-area-path' | 'jira-board' | 'umbrella-teams' | 'multi-project' | 'single-project' | 'existing-folders';
31
+ }
32
+ /**
33
+ * Project information
34
+ */
35
+ export interface ProjectInfo {
36
+ id: string;
37
+ name: string;
38
+ /** For 2-level: this is the ADO project or JIRA project key */
39
+ externalId?: string;
40
+ }
41
+ /**
42
+ * Board information (for 2-level structures)
43
+ */
44
+ export interface BoardInfo {
45
+ id: string;
46
+ name: string;
47
+ /** Parent project ID */
48
+ projectId: string;
49
+ /** Keywords for matching */
50
+ keywords?: string[];
51
+ /** ADO area path */
52
+ areaPath?: string;
53
+ /** JIRA board ID */
54
+ jiraBoardId?: string;
55
+ }
56
+ /**
57
+ * Validation result for spec.md project/board context
58
+ */
59
+ export interface ProjectContextValidation {
60
+ isValid: boolean;
61
+ errors: string[];
62
+ warnings: string[];
63
+ /** Detected structure level */
64
+ structureLevel: 1 | 2;
65
+ /** Parsed project from spec */
66
+ project?: string;
67
+ /** Parsed board from spec (for 2-level) */
68
+ board?: string;
69
+ /** Expected path where this spec will sync to */
70
+ expectedPath?: string;
71
+ }
72
+ /**
73
+ * Detect structure level from configuration
74
+ *
75
+ * @param projectRoot - Path to project root
76
+ * @returns Structure level configuration
77
+ */
78
+ export declare function detectStructureLevel(projectRoot?: string): StructureLevelConfig;
79
+ /**
80
+ * Validate spec.md has required project/board context
81
+ *
82
+ * @param specContent - Content of spec.md file
83
+ * @param projectRoot - Path to project root
84
+ * @returns Validation result
85
+ */
86
+ export declare function validateProjectContext(specContent: string, projectRoot?: string): ProjectContextValidation;
87
+ /**
88
+ * Get required fields for spec.md based on structure level
89
+ *
90
+ * @param projectRoot - Path to project root
91
+ * @returns Object describing required fields
92
+ */
93
+ export declare function getRequiredSpecFields(projectRoot?: string): {
94
+ level: 1 | 2;
95
+ requiredFields: string[];
96
+ optionalFields: string[];
97
+ projects: ProjectInfo[];
98
+ boardsByProject?: Record<string, BoardInfo[]>;
99
+ };
100
+ /**
101
+ * Normalize ID to kebab-case
102
+ */
103
+ declare function normalizeId(name: string): string;
104
+ export { normalizeId };
105
+ //# sourceMappingURL=structure-level-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structure-level-detector.d.ts","sourceRoot":"","sources":["../../../src/utils/structure-level-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;IAEb,6EAA6E;IAC7E,QAAQ,EAAE,WAAW,EAAE,CAAC;IAExB,iEAAiE;IACjE,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAE9C,qCAAqC;IACrC,eAAe,EAAE,MAAM,CAAC;IAExB,8BAA8B;IAC9B,MAAM,EAAE,eAAe,GAAG,YAAY,GAAG,gBAAgB,GAAG,eAAe,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;CACrH;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,+BAA+B;IAC/B,cAAc,EAAE,CAAC,GAAG,CAAC,CAAC;IACtB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA+CD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,GAAE,MAAsB,GAAG,oBAAoB,CAoQ9F;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,WAAW,GAAE,MAAsB,GAClC,wBAAwB,CAgH1B;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,GAAE,MAAsB,GAAG;IAC1E,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;CAC/C,CAmBA;AAED;;GAEG;AACH,iBAAS,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOzC;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,388 @@
1
+ /**
2
+ * Structure Level Detector
3
+ *
4
+ * Detects whether the project uses 1-level or 2-level folder structure
5
+ * for living docs specs:
6
+ *
7
+ * - 1-Level: internal/specs/{project}/FS-XXX/
8
+ * - 2-Level: internal/specs/{project}/{board}/FS-XXX/
9
+ *
10
+ * 2-level is used when:
11
+ * - ADO area path mapping is configured (project → area paths)
12
+ * - JIRA board mapping is configured with multiple boards per project
13
+ * - Umbrella with team-based organization
14
+ *
15
+ * @module utils/structure-level-detector
16
+ */
17
+ import * as fs from './fs-native.js';
18
+ import path from 'path';
19
+ /**
20
+ * Detect structure level from configuration
21
+ *
22
+ * @param projectRoot - Path to project root
23
+ * @returns Structure level configuration
24
+ */
25
+ export function detectStructureLevel(projectRoot = process.cwd()) {
26
+ const configPath = path.join(projectRoot, '.specweave', 'config.json');
27
+ const specsPath = path.join(projectRoot, '.specweave', 'docs', 'internal', 'specs');
28
+ let config = {};
29
+ // Read config.json if exists
30
+ if (fs.existsSync(configPath)) {
31
+ try {
32
+ config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
33
+ }
34
+ catch {
35
+ // Ignore parse errors, use defaults
36
+ }
37
+ }
38
+ // Check 1: ADO area path mapping (2-level structure)
39
+ if (config.sync?.profiles) {
40
+ for (const [, profile] of Object.entries(config.sync.profiles)) {
41
+ if (profile.provider !== 'ado')
42
+ continue;
43
+ const adoProject = profile.config?.project;
44
+ // ADO with areaPathMapping = 2-level structure
45
+ if (profile.config?.areaPathMapping?.mappings?.length && adoProject) {
46
+ const projects = [{
47
+ id: normalizeId(adoProject),
48
+ name: adoProject,
49
+ externalId: adoProject
50
+ }];
51
+ const boardsByProject = {};
52
+ const projectId = normalizeId(adoProject);
53
+ boardsByProject[projectId] = [];
54
+ for (const mapping of profile.config.areaPathMapping.mappings) {
55
+ boardsByProject[projectId].push({
56
+ id: normalizeId(mapping.specweaveProject),
57
+ name: mapping.specweaveProject,
58
+ projectId: projectId,
59
+ keywords: mapping.keywords,
60
+ areaPath: mapping.areaPath
61
+ });
62
+ }
63
+ return {
64
+ level: 2,
65
+ projects,
66
+ boardsByProject,
67
+ detectionReason: 'ADO area path mapping configured',
68
+ source: 'ado-area-path'
69
+ };
70
+ }
71
+ // ADO with simple areaPaths list = also 2-level (project/area)
72
+ if (profile.config?.areaPaths?.length && adoProject) {
73
+ const projects = [{
74
+ id: normalizeId(adoProject),
75
+ name: adoProject,
76
+ externalId: adoProject
77
+ }];
78
+ const boardsByProject = {};
79
+ const projectId = normalizeId(adoProject);
80
+ boardsByProject[projectId] = profile.config.areaPaths.map(ap => ({
81
+ id: normalizeId(ap),
82
+ name: ap,
83
+ projectId: projectId,
84
+ areaPath: ap
85
+ }));
86
+ return {
87
+ level: 2,
88
+ projects,
89
+ boardsByProject,
90
+ detectionReason: 'ADO area paths configured',
91
+ source: 'ado-area-path'
92
+ };
93
+ }
94
+ }
95
+ }
96
+ // Check 2: JIRA board mapping with multiple boards (2-level structure)
97
+ if (config.sync?.profiles) {
98
+ for (const [, profile] of Object.entries(config.sync.profiles)) {
99
+ if (profile.provider !== 'jira')
100
+ continue;
101
+ if (profile.config?.boardMapping?.boards?.length) {
102
+ const boards = profile.config.boardMapping.boards;
103
+ // Group boards by specweaveProject to detect structure
104
+ const projectSet = new Set(boards.map(b => normalizeId(b.specweaveProject)));
105
+ if (projectSet.size > 1 || boards.length > 1) {
106
+ // Multiple boards or multiple projects = 2-level
107
+ const projects = Array.from(projectSet).map(p => ({
108
+ id: p,
109
+ name: p
110
+ }));
111
+ const boardsByProject = {};
112
+ for (const board of boards) {
113
+ const pid = normalizeId(board.specweaveProject);
114
+ if (!boardsByProject[pid]) {
115
+ boardsByProject[pid] = [];
116
+ }
117
+ boardsByProject[pid].push({
118
+ id: normalizeId(board.name),
119
+ name: board.name,
120
+ projectId: pid,
121
+ keywords: board.keywords,
122
+ jiraBoardId: board.id
123
+ });
124
+ }
125
+ return {
126
+ level: 2,
127
+ projects,
128
+ boardsByProject,
129
+ detectionReason: 'JIRA board mapping with multiple boards',
130
+ source: 'jira-board'
131
+ };
132
+ }
133
+ }
134
+ }
135
+ }
136
+ // Check 3: Umbrella with teams (2-level structure)
137
+ if (config.umbrella?.enabled && config.umbrella?.childRepos?.length) {
138
+ const repos = config.umbrella.childRepos;
139
+ const teams = new Set(repos.filter(r => r.team).map(r => r.team));
140
+ if (teams.size > 1) {
141
+ // Multiple teams = 2-level (team/repo)
142
+ const projects = Array.from(teams).map(t => ({
143
+ id: normalizeId(t),
144
+ name: t
145
+ }));
146
+ const boardsByProject = {};
147
+ for (const team of teams) {
148
+ const teamId = normalizeId(team);
149
+ boardsByProject[teamId] = repos
150
+ .filter(r => r.team === team)
151
+ .map(r => ({
152
+ id: normalizeId(r.name || r.id),
153
+ name: r.name || r.id,
154
+ projectId: teamId
155
+ }));
156
+ }
157
+ return {
158
+ level: 2,
159
+ projects,
160
+ boardsByProject,
161
+ detectionReason: 'Umbrella with multiple teams',
162
+ source: 'umbrella-teams'
163
+ };
164
+ }
165
+ }
166
+ // Check 4: multiProject.projects (1-level structure)
167
+ if (config.multiProject?.enabled && config.multiProject?.projects) {
168
+ const projectEntries = Object.entries(config.multiProject.projects);
169
+ if (projectEntries.length >= 1) {
170
+ const projects = projectEntries.map(([id, p]) => ({
171
+ id: normalizeId(id),
172
+ name: p.name || id
173
+ }));
174
+ return {
175
+ level: 1,
176
+ projects,
177
+ detectionReason: 'multiProject configuration',
178
+ source: 'multi-project'
179
+ };
180
+ }
181
+ }
182
+ // Check 5: Existing folder structure
183
+ if (fs.existsSync(specsPath)) {
184
+ try {
185
+ const entries = fs.readdirSync(specsPath, { withFileTypes: true });
186
+ const folders = entries.filter(e => e.isDirectory() &&
187
+ !e.name.startsWith('_') &&
188
+ !e.name.startsWith('.') &&
189
+ !e.name.match(/^FS-\d{3,}/));
190
+ if (folders.length > 0) {
191
+ // Check if any folder has sub-folders (2-level)
192
+ const firstFolder = folders[0];
193
+ const subPath = path.join(specsPath, firstFolder.name);
194
+ const subEntries = fs.readdirSync(subPath, { withFileTypes: true });
195
+ const subFolders = subEntries.filter(e => e.isDirectory() &&
196
+ !e.name.startsWith('_') &&
197
+ !e.name.match(/^FS-\d{3,}/));
198
+ if (subFolders.length > 0) {
199
+ // Has sub-folders that aren't FS-XXX = 2-level
200
+ const projects = folders.map(f => ({
201
+ id: f.name,
202
+ name: f.name
203
+ }));
204
+ const boardsByProject = {};
205
+ for (const folder of folders) {
206
+ const pPath = path.join(specsPath, folder.name);
207
+ const pEntries = fs.readdirSync(pPath, { withFileTypes: true });
208
+ const pFolders = pEntries.filter(e => e.isDirectory() &&
209
+ !e.name.startsWith('_') &&
210
+ !e.name.match(/^FS-\d{3,}/));
211
+ if (pFolders.length > 0) {
212
+ boardsByProject[folder.name] = pFolders.map(b => ({
213
+ id: b.name,
214
+ name: b.name,
215
+ projectId: folder.name
216
+ }));
217
+ }
218
+ }
219
+ return {
220
+ level: 2,
221
+ projects,
222
+ boardsByProject,
223
+ detectionReason: 'Existing 2-level folder structure',
224
+ source: 'existing-folders'
225
+ };
226
+ }
227
+ // 1-level structure
228
+ const projects = folders.map(f => ({
229
+ id: f.name,
230
+ name: f.name
231
+ }));
232
+ return {
233
+ level: 1,
234
+ projects,
235
+ detectionReason: 'Existing project folders',
236
+ source: 'existing-folders'
237
+ };
238
+ }
239
+ }
240
+ catch {
241
+ // Ignore read errors
242
+ }
243
+ }
244
+ // Default: single project (1-level)
245
+ return {
246
+ level: 1,
247
+ projects: [{ id: 'default', name: 'Default' }],
248
+ detectionReason: 'No multi-project configuration - single project mode',
249
+ source: 'single-project'
250
+ };
251
+ }
252
+ /**
253
+ * Validate spec.md has required project/board context
254
+ *
255
+ * @param specContent - Content of spec.md file
256
+ * @param projectRoot - Path to project root
257
+ * @returns Validation result
258
+ */
259
+ export function validateProjectContext(specContent, projectRoot = process.cwd()) {
260
+ const structureConfig = detectStructureLevel(projectRoot);
261
+ const errors = [];
262
+ const warnings = [];
263
+ // Parse YAML frontmatter
264
+ let project;
265
+ let board;
266
+ const frontmatterMatch = specContent.match(/^---\n([\s\S]*?)\n---/);
267
+ if (frontmatterMatch) {
268
+ const frontmatter = frontmatterMatch[1];
269
+ // Extract project field
270
+ const projectMatch = frontmatter.match(/^project:\s*(.+)$/m);
271
+ if (projectMatch) {
272
+ project = projectMatch[1].trim().replace(/^["']|["']$/g, '');
273
+ }
274
+ // Extract board field
275
+ const boardMatch = frontmatter.match(/^board:\s*(.+)$/m);
276
+ if (boardMatch) {
277
+ board = boardMatch[1].trim().replace(/^["']|["']$/g, '');
278
+ }
279
+ }
280
+ // Also check for **Project**: field in body (legacy format)
281
+ if (!project) {
282
+ const bodyProjectMatch = specContent.match(/\*\*Project\*\*:\s*(.+?)(?:\n|$)/i);
283
+ if (bodyProjectMatch) {
284
+ project = bodyProjectMatch[1].trim();
285
+ warnings.push('Using **Project**: field from body - prefer project: in YAML frontmatter');
286
+ }
287
+ }
288
+ // Validation based on structure level
289
+ if (structureConfig.level === 1) {
290
+ // 1-level: project is REQUIRED
291
+ if (!project) {
292
+ errors.push('Missing project field in spec.md. ' +
293
+ 'Add `project: <project_name>` to YAML frontmatter. ' +
294
+ `Available projects: ${structureConfig.projects.map(p => p.id).join(', ')}`);
295
+ }
296
+ else {
297
+ // Validate project exists
298
+ const validProject = structureConfig.projects.find(p => normalizeId(p.id) === normalizeId(project));
299
+ if (!validProject && structureConfig.source !== 'single-project') {
300
+ warnings.push(`Project "${project}" not found in configuration. ` +
301
+ `Available: ${structureConfig.projects.map(p => p.id).join(', ')}`);
302
+ }
303
+ }
304
+ }
305
+ else {
306
+ // 2-level: BOTH project AND board are REQUIRED
307
+ if (!project) {
308
+ errors.push('Missing project field in spec.md (2-level structure detected). ' +
309
+ 'Add `project: <project_name>` to YAML frontmatter. ' +
310
+ `Available projects: ${structureConfig.projects.map(p => p.id).join(', ')}`);
311
+ }
312
+ if (!board) {
313
+ const boardOptions = project && structureConfig.boardsByProject?.[normalizeId(project)]
314
+ ? structureConfig.boardsByProject[normalizeId(project)].map(b => b.id).join(', ')
315
+ : 'N/A';
316
+ errors.push('Missing board field in spec.md (2-level structure detected). ' +
317
+ 'Add `board: <board_name>` to YAML frontmatter. ' +
318
+ `Available boards: ${boardOptions}`);
319
+ }
320
+ else if (project && structureConfig.boardsByProject) {
321
+ // Validate board exists under project
322
+ const projectBoards = structureConfig.boardsByProject[normalizeId(project)];
323
+ if (projectBoards) {
324
+ const validBoard = projectBoards.find(b => normalizeId(b.id) === normalizeId(board));
325
+ if (!validBoard) {
326
+ warnings.push(`Board "${board}" not found under project "${project}". ` +
327
+ `Available: ${projectBoards.map(b => b.id).join(', ')}`);
328
+ }
329
+ }
330
+ }
331
+ }
332
+ // Calculate expected path
333
+ let expectedPath;
334
+ if (project) {
335
+ if (structureConfig.level === 2 && board) {
336
+ expectedPath = `internal/specs/${normalizeId(project)}/${normalizeId(board)}/`;
337
+ }
338
+ else if (structureConfig.level === 1) {
339
+ expectedPath = `internal/specs/${normalizeId(project)}/`;
340
+ }
341
+ }
342
+ return {
343
+ isValid: errors.length === 0,
344
+ errors,
345
+ warnings,
346
+ structureLevel: structureConfig.level,
347
+ project,
348
+ board,
349
+ expectedPath
350
+ };
351
+ }
352
+ /**
353
+ * Get required fields for spec.md based on structure level
354
+ *
355
+ * @param projectRoot - Path to project root
356
+ * @returns Object describing required fields
357
+ */
358
+ export function getRequiredSpecFields(projectRoot = process.cwd()) {
359
+ const config = detectStructureLevel(projectRoot);
360
+ if (config.level === 2) {
361
+ return {
362
+ level: 2,
363
+ requiredFields: ['project', 'board'],
364
+ optionalFields: [],
365
+ projects: config.projects,
366
+ boardsByProject: config.boardsByProject
367
+ };
368
+ }
369
+ return {
370
+ level: 1,
371
+ requiredFields: ['project'],
372
+ optionalFields: ['board'],
373
+ projects: config.projects
374
+ };
375
+ }
376
+ /**
377
+ * Normalize ID to kebab-case
378
+ */
379
+ function normalizeId(name) {
380
+ return name
381
+ .toLowerCase()
382
+ .replace(/\s+/g, '-')
383
+ .replace(/[^a-z0-9-]/g, '')
384
+ .replace(/-+/g, '-')
385
+ .replace(/^-|-$/g, '');
386
+ }
387
+ export { normalizeId };
388
+ //# sourceMappingURL=structure-level-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structure-level-detector.js","sourceRoot":"","sources":["../../../src/utils/structure-level-detector.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specweave",
3
- "version": "0.30.16",
3
+ "version": "0.30.18",
4
4
  "description": "Spec-driven development framework for Claude Code. AI-native workflow with living documentation, intelligent agents, and multilingual support (9 languages). Enterprise-grade traceability with permanent specs and temporary increments.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -371,18 +371,60 @@ Proceeding with hotfix 0006...
371
371
  - Find highest number across both directories (e.g., 032)
372
372
  - Next increment: 033
373
373
 
374
- ### Step 1.5: Detect Multi-Project Mode (CRITICAL!)
374
+ ### Step 1.5: Detect Structure Level & Select Project/Board (v0.31.0+ MANDATORY!)
375
375
 
376
- **⚠️ MANDATORY CHECK before generating spec.md:**
376
+ **⚠️ MANDATORY CHECK before generating spec.md!**
377
377
 
378
- ```bash
379
- # Check for umbrella configuration
380
- UMBRELLA_ENABLED=$(cat .specweave/config.json 2>/dev/null | jq -r '.umbrella.enabled // false')
381
- CHILD_REPOS=$(cat .specweave/config.json 2>/dev/null | jq -r '.umbrella.childRepos[]?.id // empty' | tr '\n' ',')
382
- PROJECT_FOLDERS=$(ls -1 .specweave/docs/internal/specs/ 2>/dev/null | grep -v "^_" | head -5)
378
+ **Structure Level Detection** (use `src/utils/structure-level-detector.ts`):
379
+
380
+ ```typescript
381
+ import { detectStructureLevel } from './utils/structure-level-detector.js';
382
+
383
+ const structureConfig = detectStructureLevel(projectRoot);
384
+ // structureConfig.level: 1 or 2
385
+ // structureConfig.projects: available projects
386
+ // structureConfig.boardsByProject: boards per project (if 2-level)
387
+ ```
388
+
389
+ **Detection Sources** (priority order):
390
+ 1. ADO area path mapping (`sync.profiles.*.config.areaPathMapping`)
391
+ 2. ADO `areaPaths` array
392
+ 3. JIRA board mapping (`sync.profiles.*.config.boardMapping`)
393
+ 4. Umbrella teams (`umbrella.teams`)
394
+ 5. Umbrella repos (`umbrella.childRepos`)
395
+ 6. Multi-project config (`multiProject.enabled`)
396
+ 7. Existing folder structure (fallback)
397
+
398
+ **For 1-Level Structure** (projects only):
399
+ ```
400
+ 📁 Structure Level: 1 (projects only)
401
+ Available projects: web-app, mobile-app, platform-infra
402
+
403
+ Which project should this increment target?
404
+ > web-app
405
+
406
+ ✅ spec.md will include: project: web-app
407
+ ✅ Sync path: internal/specs/web-app/FS-XXX/
408
+ ```
409
+
410
+ **For 2-Level Structure** (projects + boards):
383
411
  ```
412
+ 📁 Structure Level: 2 (projects + boards)
413
+ Available projects: acme-corp
414
+
415
+ 📁 Project: acme-corp
416
+ Boards: clinical-insights, platform-engineering, digital-operations
417
+
418
+ Which board should this increment sync to?
419
+ > clinical-insights
384
420
 
385
- **If multi-project detected:**
421
+ spec.md will include:
422
+ project: acme-corp
423
+ board: clinical-insights
424
+ ✅ Sync path: internal/specs/acme-corp/clinical-insights/FS-XXX/
425
+ ```
426
+
427
+ **Multi-Project User Stories** (if umbrella detected):
386
428
  ```
387
429
  📁 Multi-Project Mode Detected!
388
430
 
@@ -399,7 +441,7 @@ User stories will be generated with project prefixes:
399
441
  Each story will include "Related Repo" field for clarity.
400
442
  ```
401
443
 
402
- **Pass this context to increment-planner skill!**
444
+ **Pass project/board values to increment-planner skill!**
403
445
 
404
446
  ### Step 2: Detect tech stack (CRITICAL - framework-agnostic)
405
447
  - Settings auto-detected
@@ -628,6 +670,8 @@ Next steps:
628
670
 
629
671
  **IMPORTANT**: Tech stack is AUTO-DETECTED from project files (package.json, requirements.txt, etc.), NOT hardcoded!
630
672
 
673
+ **IMPORTANT (v0.31.0+)**: `project:` (and `board:` for 2-level) fields are MANDATORY. See Step 1.5.
674
+
631
675
  ```yaml
632
676
  ---
633
677
  increment: 003-user-authentication
@@ -638,6 +682,10 @@ created: 2025-10-26
638
682
  dependencies: []
639
683
  structure: user-stories
640
684
 
685
+ # PROJECT/BOARD (v0.31.0+ MANDATORY)
686
+ project: web-app # REQUIRED - target project for living docs sync
687
+ board: digital-operations # REQUIRED only for 2-level structures (ADO/JIRA boards)
688
+
641
689
  # Tech stack is DETECTED, not hardcoded
642
690
  tech_stack:
643
691
  detected_from: "package.json" # or "requirements.txt", "go.mod", etc.
@@ -10,10 +10,40 @@ description: Run quality assessment on a SpecWeave increment with risk scoring a
10
10
  ## Purpose
11
11
 
12
12
  Run comprehensive quality assessment on an increment using:
13
- - ✅ Rule-based validation (120 checks)
14
- - ✅ AI quality assessment (7 dimensions including risk)
15
- - ✅ Quantitative risk scoring (Probability × Impact)
16
- - ✅ Quality gate decisions (PASS/CONCERNS/FAIL)
13
+ - ✅ **Gate 1: Rule-based validation** (130+ automated checks)
14
+ - ✅ **Gate 2: LLM-as-Judge** (AI quality assessment with chain-of-thought reasoning)
15
+ - ✅ **Gate 3: Risk scoring** (BMAD Probability × Impact quantitative assessment)
16
+ - ✅ **Quality gate decisions** (PASS/CONCERNS/FAIL)
17
+
18
+ ## LLM-as-Judge Pattern
19
+
20
+ This command implements the **LLM-as-Judge** pattern - an established AI/ML evaluation technique where an LLM evaluates outputs using structured reasoning.
21
+
22
+ **How it works:**
23
+ ```
24
+ ┌─────────────────────────────────────────────────────────────┐
25
+ │ LLM-as-Judge Gate │
26
+ ├─────────────────────────────────────────────────────────────┤
27
+ │ Input: spec.md, plan.md, tasks.md │
28
+ │ │
29
+ │ Process: │
30
+ │ 1. Chain-of-thought analysis (7 dimensions) │
31
+ │ 2. Evidence-based scoring (0-100 per dimension) │
32
+ │ 3. Risk identification (BMAD P×I formula) │
33
+ │ 4. Formal verdict (PASS/CONCERNS/FAIL) │
34
+ │ │
35
+ │ Output: Structured quality report with: │
36
+ │ - Blockers (MUST fix) │
37
+ │ - Concerns (SHOULD fix) │
38
+ │ - Recommendations (NICE to fix) │
39
+ └─────────────────────────────────────────────────────────────┘
40
+ ```
41
+
42
+ **Why LLM-as-Judge?**
43
+ - **Consistency**: Applies uniform evaluation criteria
44
+ - **Depth**: Catches nuanced issues humans might miss
45
+ - **Speed**: ~30 seconds vs hours of manual review
46
+ - **Documented reasoning**: Explains WHY something is an issue
17
47
 
18
48
  ## Usage
19
49