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.
- package/CLAUDE.md +43 -0
- package/dist/src/cli/commands/plan/increment-detector.js +2 -2
- package/dist/src/cli/commands/plan/increment-detector.js.map +1 -1
- package/dist/src/cli/commands/plan/plan-orchestrator.js +1 -1
- package/dist/src/cli/commands/plan/plan-orchestrator.js.map +1 -1
- package/dist/src/cli/commands/plan/plan-validator.js +1 -1
- package/dist/src/cli/commands/plan/plan-validator.js.map +1 -1
- package/dist/src/cli/workers/living-docs-worker.js +80 -44
- package/dist/src/cli/workers/living-docs-worker.js.map +1 -1
- package/dist/src/core/increment/active-increment-manager.js +1 -1
- package/dist/src/core/increment/active-increment-manager.js.map +1 -1
- package/dist/src/core/increment/metadata-manager.d.ts +1 -1
- package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.js +7 -7
- package/dist/src/core/increment/metadata-manager.js.map +1 -1
- package/dist/src/core/living-docs/hierarchy-mapper.d.ts.map +1 -1
- package/dist/src/core/living-docs/hierarchy-mapper.js +12 -18
- package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.d.ts +49 -24
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +207 -96
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/llm/index.d.ts +1 -0
- package/dist/src/core/llm/index.d.ts.map +1 -1
- package/dist/src/core/llm/index.js +2 -0
- package/dist/src/core/llm/index.js.map +1 -1
- package/dist/src/core/llm/providers/anthropic-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/anthropic-provider.js +15 -26
- package/dist/src/core/llm/providers/anthropic-provider.js.map +1 -1
- package/dist/src/core/llm/providers/azure-openai-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/azure-openai-provider.js +13 -5
- package/dist/src/core/llm/providers/azure-openai-provider.js.map +1 -1
- package/dist/src/core/llm/providers/bedrock-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/bedrock-provider.js +12 -8
- package/dist/src/core/llm/providers/bedrock-provider.js.map +1 -1
- package/dist/src/core/llm/providers/claude-code-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/claude-code-provider.js +15 -25
- package/dist/src/core/llm/providers/claude-code-provider.js.map +1 -1
- package/dist/src/core/llm/providers/ollama-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/ollama-provider.js +12 -9
- package/dist/src/core/llm/providers/ollama-provider.js.map +1 -1
- package/dist/src/core/llm/providers/openai-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/openai-provider.js +13 -6
- package/dist/src/core/llm/providers/openai-provider.js.map +1 -1
- package/dist/src/core/llm/providers/vertex-ai-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/vertex-ai-provider.js +12 -8
- package/dist/src/core/llm/providers/vertex-ai-provider.js.map +1 -1
- package/dist/src/utils/feature-id-collision.d.ts +28 -0
- package/dist/src/utils/feature-id-collision.d.ts.map +1 -1
- package/dist/src/utils/feature-id-collision.js +83 -0
- package/dist/src/utils/feature-id-collision.js.map +1 -1
- package/dist/src/utils/llm-json-extractor.d.ts +105 -0
- package/dist/src/utils/llm-json-extractor.d.ts.map +1 -0
- package/dist/src/utils/llm-json-extractor.js +336 -0
- package/dist/src/utils/llm-json-extractor.js.map +1 -0
- package/dist/src/utils/structure-level-detector.d.ts +105 -0
- package/dist/src/utils/structure-level-detector.d.ts.map +1 -0
- package/dist/src/utils/structure-level-detector.js +388 -0
- package/dist/src/utils/structure-level-detector.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/commands/specweave-increment.md +57 -9
- package/plugins/specweave/commands/specweave-qa.md +34 -4
- package/plugins/specweave/commands/specweave-sync-specs.md +37 -6
- package/plugins/specweave/commands/specweave-validate.md +41 -1
- package/plugins/specweave/hooks/hooks.json +10 -0
- package/plugins/specweave/hooks/spec-project-validator.sh +111 -0
- package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js +1 -1
- package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js.map +1 -1
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +1 -1
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +7 -7
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
- package/plugins/specweave/skills/increment-planner/SKILL.md +109 -10
- package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +2 -0
- package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +1 -0
- package/plugins/specweave/skills/increment-quality-judge-v2/SKILL.md +41 -2
- package/plugins/specweave/skills/multi-project-spec-mapper/SKILL.md +24 -1
- package/plugins/specweave/skills/spec-generator/SKILL.md +18 -0
- package/plugins/specweave/skills/specweave-framework/SKILL.md +25 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +14 -0
- 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":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,IAAI,MAAM,MAAM,CAAC;AA8GxB;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,cAAsB,OAAO,CAAC,GAAG,EAAE;IACtE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;IACvE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IAEpF,IAAI,MAAM,GAAiB,EAAE,CAAC;IAE9B,6BAA6B;IAC7B,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;QACtC,CAAC;IACH,CAAC;IAED,qDAAqD;IACrD,IAAI,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC1B,KAAK,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/D,IAAI,OAAO,CAAC,QAAQ,KAAK,KAAK;gBAAE,SAAS;YAEzC,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC;YAE3C,+CAA+C;YAC/C,IAAI,OAAO,CAAC,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,IAAI,UAAU,EAAE,CAAC;gBACpE,MAAM,QAAQ,GAAkB,CAAC;wBAC/B,EAAE,EAAE,WAAW,CAAC,UAAU,CAAC;wBAC3B,IAAI,EAAE,UAAU;wBAChB,UAAU,EAAE,UAAU;qBACvB,CAAC,CAAC;gBAEH,MAAM,eAAe,GAAgC,EAAE,CAAC;gBACxD,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;gBAC1C,eAAe,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;gBAEhC,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC;oBAC9D,eAAe,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC;wBAC9B,EAAE,EAAE,WAAW,CAAC,OAAO,CAAC,gBAAgB,CAAC;wBACzC,IAAI,EAAE,OAAO,CAAC,gBAAgB;wBAC9B,SAAS,EAAE,SAAS;wBACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;wBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;qBAC3B,CAAC,CAAC;gBACL,CAAC;gBAED,OAAO;oBACL,KAAK,EAAE,CAAC;oBACR,QAAQ;oBACR,eAAe;oBACf,eAAe,EAAE,kCAAkC;oBACnD,MAAM,EAAE,eAAe;iBACxB,CAAC;YACJ,CAAC;YAED,+DAA+D;YAC/D,IAAI,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,UAAU,EAAE,CAAC;gBACpD,MAAM,QAAQ,GAAkB,CAAC;wBAC/B,EAAE,EAAE,WAAW,CAAC,UAAU,CAAC;wBAC3B,IAAI,EAAE,UAAU;wBAChB,UAAU,EAAE,UAAU;qBACvB,CAAC,CAAC;gBAEH,MAAM,eAAe,GAAgC,EAAE,CAAC;gBACxD,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;gBAC1C,eAAe,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;oBAC/D,EAAE,EAAE,WAAW,CAAC,EAAE,CAAC;oBACnB,IAAI,EAAE,EAAE;oBACR,SAAS,EAAE,SAAS;oBACpB,QAAQ,EAAE,EAAE;iBACb,CAAC,CAAC,CAAC;gBAEJ,OAAO;oBACL,KAAK,EAAE,CAAC;oBACR,QAAQ;oBACR,eAAe;oBACf,eAAe,EAAE,2BAA2B;oBAC5C,MAAM,EAAE,eAAe;iBACxB,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,IAAI,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC1B,KAAK,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/D,IAAI,OAAO,CAAC,QAAQ,KAAK,MAAM;gBAAE,SAAS;YAE1C,IAAI,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;gBACjD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;gBAElD,uDAAuD;gBACvD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;gBAE7E,IAAI,UAAU,CAAC,IAAI,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC7C,iDAAiD;oBACjD,MAAM,QAAQ,GAAkB,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;wBAC/D,EAAE,EAAE,CAAC;wBACL,IAAI,EAAE,CAAC;qBACR,CAAC,CAAC,CAAC;oBAEJ,MAAM,eAAe,GAAgC,EAAE,CAAC;oBACxD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;wBAC3B,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;wBAChD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;4BAC1B,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;wBAC5B,CAAC;wBACD,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;4BACxB,EAAE,EAAE,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC;4BAC3B,IAAI,EAAE,KAAK,CAAC,IAAI;4BAChB,SAAS,EAAE,GAAG;4BACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;4BACxB,WAAW,EAAE,KAAK,CAAC,EAAE;yBACtB,CAAC,CAAC;oBACL,CAAC;oBAED,OAAO;wBACL,KAAK,EAAE,CAAC;wBACR,QAAQ;wBACR,eAAe;wBACf,eAAe,EAAE,yCAAyC;wBAC1D,MAAM,EAAE,YAAY;qBACrB,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,IAAI,MAAM,CAAC,QAAQ,EAAE,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QACpE,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;QACzC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAK,CAAC,CAAC,CAAC;QAEnE,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YACnB,uCAAuC;YACvC,MAAM,QAAQ,GAAkB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC1D,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;gBAClB,IAAI,EAAE,CAAC;aACR,CAAC,CAAC,CAAC;YAEJ,MAAM,eAAe,GAAgC,EAAE,CAAC;YACxD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;gBACjC,eAAe,CAAC,MAAM,CAAC,GAAG,KAAK;qBAC5B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC;qBAC5B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBACT,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC;oBAC/B,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE;oBACpB,SAAS,EAAE,MAAM;iBAClB,CAAC,CAAC,CAAC;YACR,CAAC;YAED,OAAO;gBACL,KAAK,EAAE,CAAC;gBACR,QAAQ;gBACR,eAAe;gBACf,eAAe,EAAE,8BAA8B;gBAC/C,MAAM,EAAE,gBAAgB;aACzB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,qDAAqD;IACrD,IAAI,MAAM,CAAC,YAAY,EAAE,OAAO,IAAI,MAAM,CAAC,YAAY,EAAE,QAAQ,EAAE,CAAC;QAClE,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QACpE,IAAI,cAAc,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAC/B,MAAM,QAAQ,GAAkB,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC/D,EAAE,EAAE,WAAW,CAAC,EAAE,CAAC;gBACnB,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE;aACnB,CAAC,CAAC,CAAC;YAEJ,OAAO;gBACL,KAAK,EAAE,CAAC;gBACR,QAAQ;gBACR,eAAe,EAAE,4BAA4B;gBAC7C,MAAM,EAAE,eAAe;aACxB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YACnE,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CACjC,CAAC,CAAC,WAAW,EAAE;gBACf,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBACvB,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBACvB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAC5B,CAAC;YAEF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,gDAAgD;gBAChD,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;gBAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;gBACvD,MAAM,UAAU,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;gBACpE,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CACvC,CAAC,CAAC,WAAW,EAAE;oBACf,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;oBACvB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAC5B,CAAC;gBAEF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC1B,+CAA+C;oBAC/C,MAAM,QAAQ,GAAkB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;wBAChD,EAAE,EAAE,CAAC,CAAC,IAAI;wBACV,IAAI,EAAE,CAAC,CAAC,IAAI;qBACb,CAAC,CAAC,CAAC;oBAEJ,MAAM,eAAe,GAAgC,EAAE,CAAC;oBACxD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;wBAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;wBAChD,MAAM,QAAQ,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;wBAChE,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CACnC,CAAC,CAAC,WAAW,EAAE;4BACf,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;4BACvB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAC5B,CAAC;wBAEF,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;4BACxB,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gCAChD,EAAE,EAAE,CAAC,CAAC,IAAI;gCACV,IAAI,EAAE,CAAC,CAAC,IAAI;gCACZ,SAAS,EAAE,MAAM,CAAC,IAAI;6BACvB,CAAC,CAAC,CAAC;wBACN,CAAC;oBACH,CAAC;oBAED,OAAO;wBACL,KAAK,EAAE,CAAC;wBACR,QAAQ;wBACR,eAAe;wBACf,eAAe,EAAE,mCAAmC;wBACpD,MAAM,EAAE,kBAAkB;qBAC3B,CAAC;gBACJ,CAAC;gBAED,oBAAoB;gBACpB,MAAM,QAAQ,GAAkB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBAChD,EAAE,EAAE,CAAC,CAAC,IAAI;oBACV,IAAI,EAAE,CAAC,CAAC,IAAI;iBACb,CAAC,CAAC,CAAC;gBAEJ,OAAO;oBACL,KAAK,EAAE,CAAC;oBACR,QAAQ;oBACR,eAAe,EAAE,0BAA0B;oBAC3C,MAAM,EAAE,kBAAkB;iBAC3B,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;QACvB,CAAC;IACH,CAAC;IAED,oCAAoC;IACpC,OAAO;QACL,KAAK,EAAE,CAAC;QACR,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QAC9C,eAAe,EAAE,sDAAsD;QACvE,MAAM,EAAE,gBAAgB;KACzB,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,WAAmB,EACnB,cAAsB,OAAO,CAAC,GAAG,EAAE;IAEnC,MAAM,eAAe,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,yBAAyB;IACzB,IAAI,OAA2B,CAAC;IAChC,IAAI,KAAyB,CAAC;IAE9B,MAAM,gBAAgB,GAAG,WAAW,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACpE,IAAI,gBAAgB,EAAE,CAAC;QACrB,MAAM,WAAW,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;QAExC,wBAAwB;QACxB,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAC7D,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAC/D,CAAC;QAED,sBAAsB;QACtB,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACzD,IAAI,UAAU,EAAE,CAAC;YACf,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,gBAAgB,GAAG,WAAW,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAChF,IAAI,gBAAgB,EAAE,CAAC;YACrB,OAAO,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACrC,QAAQ,CAAC,IAAI,CAAC,0EAA0E,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC;IAED,sCAAsC;IACtC,IAAI,eAAe,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;QAChC,+BAA+B;QAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CACT,oCAAoC;gBACpC,qDAAqD;gBACrD,uBAAuB,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5E,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,0BAA0B;YAC1B,MAAM,YAAY,GAAG,eAAe,CAAC,QAAQ,CAAC,IAAI,CAChD,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,WAAW,CAAC,OAAQ,CAAC,CACjD,CAAC;YACF,IAAI,CAAC,YAAY,IAAI,eAAe,CAAC,MAAM,KAAK,gBAAgB,EAAE,CAAC;gBACjE,QAAQ,CAAC,IAAI,CACX,YAAY,OAAO,gCAAgC;oBACnD,cAAc,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACnE,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;SAAM,CAAC;QACN,+CAA+C;QAC/C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CACT,iEAAiE;gBACjE,qDAAqD;gBACrD,uBAAuB,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5E,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,YAAY,GAAG,OAAO,IAAI,eAAe,CAAC,eAAe,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;gBACrF,CAAC,CAAC,eAAe,CAAC,eAAe,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;gBACjF,CAAC,CAAC,KAAK,CAAC;YAEV,MAAM,CAAC,IAAI,CACT,+DAA+D;gBAC/D,iDAAiD;gBACjD,qBAAqB,YAAY,EAAE,CACpC,CAAC;QACJ,CAAC;aAAM,IAAI,OAAO,IAAI,eAAe,CAAC,eAAe,EAAE,CAAC;YACtD,sCAAsC;YACtC,MAAM,aAAa,GAAG,eAAe,CAAC,eAAe,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5E,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CACnC,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,WAAW,CAAC,KAAM,CAAC,CAC/C,CAAC;gBACF,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,QAAQ,CAAC,IAAI,CACX,UAAU,KAAK,8BAA8B,OAAO,KAAK;wBACzD,cAAc,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACxD,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,0BAA0B;IAC1B,IAAI,YAAgC,CAAC;IACrC,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,eAAe,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;YACzC,YAAY,GAAG,kBAAkB,WAAW,CAAC,OAAO,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC;QACjF,CAAC;aAAM,IAAI,eAAe,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;YACvC,YAAY,GAAG,kBAAkB,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC5B,MAAM;QACN,QAAQ;QACR,cAAc,EAAE,eAAe,CAAC,KAAK;QACrC,OAAO;QACP,KAAK;QACL,YAAY;KACb,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,cAAsB,OAAO,CAAC,GAAG,EAAE;IAOvE,MAAM,MAAM,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;IAEjD,IAAI,MAAM,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,KAAK,EAAE,CAAC;YACR,cAAc,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;YACpC,cAAc,EAAE,EAAE;YAClB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,eAAe,EAAE,MAAM,CAAC,eAAe;SACxC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,KAAK,EAAE,CAAC;QACR,cAAc,EAAE,CAAC,SAAS,CAAC;QAC3B,cAAc,EAAE,CAAC,OAAO,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;KAC1B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI;SACR,WAAW,EAAE;SACb,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;SAC1B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAC3B,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specweave",
|
|
3
|
-
"version": "0.30.
|
|
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
|
|
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
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
14
|
-
- ✅ AI quality assessment
|
|
15
|
-
- ✅
|
|
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
|
|