specweave 0.28.22 → 0.28.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli/helpers/init/initial-increment-generator.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/initial-increment-generator.js +123 -35
- package/dist/src/cli/helpers/init/initial-increment-generator.js.map +1 -1
- package/dist/src/cli/helpers/init/next-steps.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/next-steps.js +16 -47
- package/dist/src/cli/helpers/init/next-steps.js.map +1 -1
- package/dist/src/core/sync/bidirectional-engine.d.ts.map +1 -1
- package/dist/src/core/sync/bidirectional-engine.js +3 -1
- package/dist/src/core/sync/bidirectional-engine.js.map +1 -1
- package/dist/src/importers/import-coordinator.d.ts.map +1 -1
- package/dist/src/importers/import-coordinator.js +17 -0
- package/dist/src/importers/import-coordinator.js.map +1 -1
- package/dist/src/init/repo/types.d.ts +1 -1
- package/dist/src/utils/multi-project-detector.d.ts +92 -0
- package/dist/src/utils/multi-project-detector.d.ts.map +1 -0
- package/dist/src/utils/multi-project-detector.js +369 -0
- package/dist/src/utils/multi-project-detector.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/agents/pm/AGENT.md +33 -12
- package/plugins/specweave/commands/specweave-import-external.md +1 -1
- package/plugins/specweave/commands/specweave.md +1 -1
- package/plugins/specweave/skills/increment-planner/SKILL.md +6 -13
- package/plugins/specweave/skills/spec-generator/SKILL.md +6 -11
- package/plugins/specweave-ado/commands/specweave-ado-sync.md +12 -12
- package/plugins/specweave-ado/lib/ado-multi-project-sync.js +0 -1
- package/plugins/specweave-github/agents/github-manager/AGENT.md +5 -5
- package/plugins/specweave-github/commands/specweave-github-sync.md +12 -12
- package/plugins/specweave-github/commands/specweave-github-update-user-story.md +1 -1
- package/plugins/specweave-github/skills/github-sync/SKILL.md +4 -4
- package/plugins/specweave-jira/commands/specweave-jira-sync.md +11 -11
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
- package/plugins/specweave-jira/skills/specweave-jira-mapper/SKILL.md +2 -2
- package/plugins/specweave-release/commands/specweave-release-npm.md +94 -4
- package/plugins/specweave/hooks/docs-changed.sh.backup +0 -79
- package/plugins/specweave/hooks/human-input-required.sh.backup +0 -75
- package/plugins/specweave/hooks/post-first-increment.sh.backup +0 -61
- package/plugins/specweave/hooks/post-increment-change.sh.backup +0 -98
- package/plugins/specweave/hooks/post-increment-completion.sh.backup +0 -231
- package/plugins/specweave/hooks/post-increment-planning.sh.backup +0 -1048
- package/plugins/specweave/hooks/post-increment-status-change.sh.backup +0 -147
- package/plugins/specweave/hooks/post-spec-update.sh.backup +0 -158
- package/plugins/specweave/hooks/post-user-story-complete.sh.backup +0 -179
- package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +0 -83
- package/plugins/specweave/hooks/pre-implementation.sh.backup +0 -67
- package/plugins/specweave/hooks/pre-task-completion.sh.backup +0 -194
- package/plugins/specweave/hooks/pre-tool-use.sh.backup +0 -133
- package/plugins/specweave/hooks/user-prompt-submit.sh.backup +0 -386
- package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +0 -353
- package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +0 -172
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +0 -170
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +0 -1238
- package/plugins/specweave-github/hooks/post-task-completion.sh.backup +0 -258
- package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +0 -172
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +0 -1218
- package/plugins/specweave-release/hooks/post-task-completion.sh.backup +0 -110
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Project Detector
|
|
3
|
+
*
|
|
4
|
+
* Detects multi-project/umbrella configuration from:
|
|
5
|
+
* 1. config.json (umbrella.enabled, multiProject.enabled)
|
|
6
|
+
* 2. childRepos configuration
|
|
7
|
+
* 3. Project folders in specs/
|
|
8
|
+
* 4. Sync profile projects
|
|
9
|
+
*
|
|
10
|
+
* Used by PM Agent, spec generators, and initial-increment-generator
|
|
11
|
+
* to determine whether to generate project-scoped user stories.
|
|
12
|
+
*
|
|
13
|
+
* @module utils/multi-project-detector
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Project configuration detected from config or folder structure
|
|
17
|
+
*/
|
|
18
|
+
export interface DetectedProject {
|
|
19
|
+
id: string;
|
|
20
|
+
prefix: string;
|
|
21
|
+
name: string;
|
|
22
|
+
path?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Multi-project detection result
|
|
26
|
+
*/
|
|
27
|
+
export interface MultiProjectDetectionResult {
|
|
28
|
+
/** Whether multi-project mode is active */
|
|
29
|
+
isMultiProject: boolean;
|
|
30
|
+
/** Reason for detection (for debugging/logging) */
|
|
31
|
+
detectionReason: string;
|
|
32
|
+
/** Detected projects (empty if single-project) */
|
|
33
|
+
projects: DetectedProject[];
|
|
34
|
+
/** Whether umbrella mode is enabled */
|
|
35
|
+
umbrellaEnabled: boolean;
|
|
36
|
+
/** Whether board/area path mapping is configured */
|
|
37
|
+
hasBoardMapping: boolean;
|
|
38
|
+
/** Primary project prefix for cross-cutting stories (e.g., 'AUTH' for auth) */
|
|
39
|
+
crossCuttingPrefix?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Infer project prefix from project ID/name
|
|
43
|
+
*/
|
|
44
|
+
export declare function inferProjectPrefix(projectId: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Detect multi-project configuration
|
|
47
|
+
*
|
|
48
|
+
* @param projectRoot - Path to project root (default: process.cwd())
|
|
49
|
+
* @returns Detection result with project information
|
|
50
|
+
*/
|
|
51
|
+
export declare function detectMultiProjectMode(projectRoot?: string): MultiProjectDetectionResult;
|
|
52
|
+
/**
|
|
53
|
+
* Get project prefixes for user story generation
|
|
54
|
+
*
|
|
55
|
+
* @param projectRoot - Path to project root
|
|
56
|
+
* @returns Array of project prefixes (e.g., ['FE', 'BE', 'SHARED'])
|
|
57
|
+
*/
|
|
58
|
+
export declare function getProjectPrefixes(projectRoot?: string): string[];
|
|
59
|
+
/**
|
|
60
|
+
* Get project by prefix
|
|
61
|
+
*
|
|
62
|
+
* @param prefix - Project prefix (e.g., 'FE')
|
|
63
|
+
* @param projectRoot - Path to project root
|
|
64
|
+
* @returns Project or undefined
|
|
65
|
+
*/
|
|
66
|
+
export declare function getProjectByPrefix(prefix: string, projectRoot?: string): DetectedProject | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Format user story ID with project prefix (if multi-project)
|
|
69
|
+
*
|
|
70
|
+
* @param storyNumber - Story number (1, 2, 3...)
|
|
71
|
+
* @param projectPrefix - Optional project prefix (e.g., 'FE')
|
|
72
|
+
* @param projectRoot - Path to project root
|
|
73
|
+
* @returns Formatted ID (e.g., 'US-FE-001' or 'US-001')
|
|
74
|
+
*/
|
|
75
|
+
export declare function formatUserStoryId(storyNumber: number, projectPrefix?: string, projectRoot?: string): string;
|
|
76
|
+
/**
|
|
77
|
+
* Format acceptance criteria ID with project prefix (if multi-project)
|
|
78
|
+
*
|
|
79
|
+
* @param userStoryNumber - User story number (1, 2, 3...)
|
|
80
|
+
* @param acNumber - Acceptance criteria number within story (1, 2, 3...)
|
|
81
|
+
* @param projectPrefix - Optional project prefix (e.g., 'FE')
|
|
82
|
+
* @returns Formatted ID (e.g., 'AC-FE-US1-01' or 'AC-US1-01')
|
|
83
|
+
*/
|
|
84
|
+
export declare function formatAcceptanceCriteriaId(userStoryNumber: number, acNumber: number, projectPrefix?: string): string;
|
|
85
|
+
/**
|
|
86
|
+
* Generate template variables for multi-project spec
|
|
87
|
+
*
|
|
88
|
+
* @param detection - Multi-project detection result
|
|
89
|
+
* @returns Template variable map
|
|
90
|
+
*/
|
|
91
|
+
export declare function generateMultiProjectTemplateVars(detection: MultiProjectDetectionResult): Record<string, string>;
|
|
92
|
+
//# sourceMappingURL=multi-project-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-project-detector.d.ts","sourceRoot":"","sources":["../../../src/utils/multi-project-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,2CAA2C;IAC3C,cAAc,EAAE,OAAO,CAAC;IAExB,mDAAmD;IACnD,eAAe,EAAE,MAAM,CAAC;IAExB,kDAAkD;IAClD,QAAQ,EAAE,eAAe,EAAE,CAAC;IAE5B,uCAAuC;IACvC,eAAe,EAAE,OAAO,CAAC;IAEzB,oDAAoD;IACpD,eAAe,EAAE,OAAO,CAAC;IAEzB,+EAA+E;IAC/E,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AA8BD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAyB5D;AAyID;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,GAAE,MAAsB,GAAG,2BAA2B,CAyFvG;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,GAAE,MAAsB,GAAG,MAAM,EAAE,CAQhF;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,WAAW,GAAE,MAAsB,GAClC,eAAe,GAAG,SAAS,CAK7B;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,MAAM,EACtB,WAAW,GAAE,MAAsB,GAClC,MAAM,CAgBR;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,MAAM,GACrB,MAAM,CAQR;AAED;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAC9C,SAAS,EAAE,2BAA2B,GACrC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA6BxB"}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Project Detector
|
|
3
|
+
*
|
|
4
|
+
* Detects multi-project/umbrella configuration from:
|
|
5
|
+
* 1. config.json (umbrella.enabled, multiProject.enabled)
|
|
6
|
+
* 2. childRepos configuration
|
|
7
|
+
* 3. Project folders in specs/
|
|
8
|
+
* 4. Sync profile projects
|
|
9
|
+
*
|
|
10
|
+
* Used by PM Agent, spec generators, and initial-increment-generator
|
|
11
|
+
* to determine whether to generate project-scoped user stories.
|
|
12
|
+
*
|
|
13
|
+
* @module utils/multi-project-detector
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from './fs-native.js';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
/**
|
|
18
|
+
* Default project prefixes for common architectures
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_PROJECT_PREFIXES = {
|
|
21
|
+
'fe': 'FE',
|
|
22
|
+
'frontend': 'FE',
|
|
23
|
+
'web': 'FE',
|
|
24
|
+
'ui': 'FE',
|
|
25
|
+
'client': 'FE',
|
|
26
|
+
'be': 'BE',
|
|
27
|
+
'backend': 'BE',
|
|
28
|
+
'api': 'BE',
|
|
29
|
+
'server': 'BE',
|
|
30
|
+
'service': 'BE',
|
|
31
|
+
'shared': 'SHARED',
|
|
32
|
+
'common': 'SHARED',
|
|
33
|
+
'lib': 'SHARED',
|
|
34
|
+
'types': 'SHARED',
|
|
35
|
+
'mobile': 'MOBILE',
|
|
36
|
+
'ios': 'MOBILE',
|
|
37
|
+
'android': 'MOBILE',
|
|
38
|
+
'app': 'MOBILE',
|
|
39
|
+
'infra': 'INFRA',
|
|
40
|
+
'infrastructure': 'INFRA',
|
|
41
|
+
'devops': 'INFRA',
|
|
42
|
+
'deploy': 'INFRA',
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Infer project prefix from project ID/name
|
|
46
|
+
*/
|
|
47
|
+
export function inferProjectPrefix(projectId) {
|
|
48
|
+
const normalizedId = projectId.toLowerCase();
|
|
49
|
+
// Check for exact matches first
|
|
50
|
+
if (DEFAULT_PROJECT_PREFIXES[normalizedId]) {
|
|
51
|
+
return DEFAULT_PROJECT_PREFIXES[normalizedId];
|
|
52
|
+
}
|
|
53
|
+
// Check for suffix matches (e.g., 'my-app-fe' → 'FE')
|
|
54
|
+
for (const [key, prefix] of Object.entries(DEFAULT_PROJECT_PREFIXES)) {
|
|
55
|
+
if (normalizedId.endsWith(`-${key}`) || normalizedId.endsWith(`_${key}`)) {
|
|
56
|
+
return prefix;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Check for contains (e.g., 'frontend-service' → 'FE')
|
|
60
|
+
for (const [key, prefix] of Object.entries(DEFAULT_PROJECT_PREFIXES)) {
|
|
61
|
+
if (normalizedId.includes(key)) {
|
|
62
|
+
return prefix;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Default: uppercase first part of ID
|
|
66
|
+
const parts = projectId.split(/[-_]/);
|
|
67
|
+
return parts[0].toUpperCase().slice(0, 6);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Parse project configuration from childRepos
|
|
71
|
+
*/
|
|
72
|
+
function parseChildRepos(childRepos) {
|
|
73
|
+
const projects = [];
|
|
74
|
+
for (const repo of childRepos) {
|
|
75
|
+
if (!repo.id)
|
|
76
|
+
continue;
|
|
77
|
+
projects.push({
|
|
78
|
+
id: repo.id,
|
|
79
|
+
prefix: repo.prefix || inferProjectPrefix(repo.id),
|
|
80
|
+
name: repo.displayName || repo.id,
|
|
81
|
+
path: repo.path
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return projects;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Parse project configuration from multiProject.projects
|
|
88
|
+
*/
|
|
89
|
+
function parseMultiProjectConfig(projectsConfig) {
|
|
90
|
+
const projects = [];
|
|
91
|
+
for (const [id, config] of Object.entries(projectsConfig)) {
|
|
92
|
+
if (typeof config !== 'object')
|
|
93
|
+
continue;
|
|
94
|
+
projects.push({
|
|
95
|
+
id: id,
|
|
96
|
+
prefix: config.prefix || inferProjectPrefix(id),
|
|
97
|
+
name: config.name || config.displayName || id,
|
|
98
|
+
path: config.path
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return projects;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Detect projects from specs folder structure
|
|
105
|
+
*/
|
|
106
|
+
function detectProjectsFromFolders(specsPath) {
|
|
107
|
+
const projects = [];
|
|
108
|
+
if (!fs.existsSync(specsPath)) {
|
|
109
|
+
return projects;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const entries = fs.readdirSync(specsPath, { withFileTypes: true });
|
|
113
|
+
for (const entry of entries) {
|
|
114
|
+
// Skip non-directories and special folders
|
|
115
|
+
if (!entry.isDirectory())
|
|
116
|
+
continue;
|
|
117
|
+
if (entry.name.startsWith('.') || entry.name.startsWith('_'))
|
|
118
|
+
continue;
|
|
119
|
+
if (entry.name === 'default')
|
|
120
|
+
continue;
|
|
121
|
+
projects.push({
|
|
122
|
+
id: entry.name,
|
|
123
|
+
prefix: inferProjectPrefix(entry.name),
|
|
124
|
+
name: entry.name,
|
|
125
|
+
path: path.join(specsPath, entry.name)
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Ignore read errors
|
|
131
|
+
}
|
|
132
|
+
return projects;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Parse projects from sync profile configuration
|
|
136
|
+
*/
|
|
137
|
+
function parseProjectsFromSyncProfiles(syncConfig) {
|
|
138
|
+
const projects = [];
|
|
139
|
+
const seenIds = new Set();
|
|
140
|
+
if (!syncConfig?.profiles)
|
|
141
|
+
return projects;
|
|
142
|
+
for (const [, profile] of Object.entries(syncConfig.profiles)) {
|
|
143
|
+
const profileConfig = profile?.config;
|
|
144
|
+
if (!profileConfig)
|
|
145
|
+
continue;
|
|
146
|
+
// Check for boardMapping (JIRA)
|
|
147
|
+
if (profileConfig.boardMapping) {
|
|
148
|
+
for (const [boardName, projectId] of Object.entries(profileConfig.boardMapping)) {
|
|
149
|
+
const id = String(projectId);
|
|
150
|
+
if (seenIds.has(id))
|
|
151
|
+
continue;
|
|
152
|
+
seenIds.add(id);
|
|
153
|
+
projects.push({
|
|
154
|
+
id: id,
|
|
155
|
+
prefix: inferProjectPrefix(id),
|
|
156
|
+
name: boardName || id
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Check for areaPathMapping (ADO)
|
|
161
|
+
if (profileConfig.areaPathMapping) {
|
|
162
|
+
for (const [areaPath, projectId] of Object.entries(profileConfig.areaPathMapping)) {
|
|
163
|
+
const id = String(projectId);
|
|
164
|
+
if (seenIds.has(id))
|
|
165
|
+
continue;
|
|
166
|
+
seenIds.add(id);
|
|
167
|
+
projects.push({
|
|
168
|
+
id: id,
|
|
169
|
+
prefix: inferProjectPrefix(id),
|
|
170
|
+
name: areaPath.split('\\').pop() || id
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Check for projects array
|
|
175
|
+
if (profileConfig.projects && Array.isArray(profileConfig.projects)) {
|
|
176
|
+
for (const projectId of profileConfig.projects) {
|
|
177
|
+
const id = String(projectId);
|
|
178
|
+
if (seenIds.has(id))
|
|
179
|
+
continue;
|
|
180
|
+
seenIds.add(id);
|
|
181
|
+
projects.push({
|
|
182
|
+
id: id,
|
|
183
|
+
prefix: inferProjectPrefix(id),
|
|
184
|
+
name: id
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return projects;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Detect multi-project configuration
|
|
193
|
+
*
|
|
194
|
+
* @param projectRoot - Path to project root (default: process.cwd())
|
|
195
|
+
* @returns Detection result with project information
|
|
196
|
+
*/
|
|
197
|
+
export function detectMultiProjectMode(projectRoot = process.cwd()) {
|
|
198
|
+
const configPath = path.join(projectRoot, '.specweave', 'config.json');
|
|
199
|
+
const specsPath = path.join(projectRoot, '.specweave', 'docs', 'internal', 'specs');
|
|
200
|
+
let config = {};
|
|
201
|
+
// Read config.json if exists
|
|
202
|
+
if (fs.existsSync(configPath)) {
|
|
203
|
+
try {
|
|
204
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// Ignore parse errors
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Check 1: umbrella.enabled with 2+ child repos
|
|
211
|
+
if (config.umbrella?.enabled === true) {
|
|
212
|
+
const projects = config.umbrella?.childRepos
|
|
213
|
+
? parseChildRepos(config.umbrella.childRepos)
|
|
214
|
+
: [];
|
|
215
|
+
// Multi-project requires 2+ projects (1 project = single-project mode)
|
|
216
|
+
if (projects.length > 1) {
|
|
217
|
+
return {
|
|
218
|
+
isMultiProject: true,
|
|
219
|
+
detectionReason: 'umbrella.enabled with childRepos',
|
|
220
|
+
projects,
|
|
221
|
+
umbrellaEnabled: true,
|
|
222
|
+
hasBoardMapping: false
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Check 2: multiProject.enabled with 2+ projects
|
|
227
|
+
if (config.multiProject?.enabled === true) {
|
|
228
|
+
const projects = config.multiProject?.projects
|
|
229
|
+
? parseMultiProjectConfig(config.multiProject.projects)
|
|
230
|
+
: [];
|
|
231
|
+
// Multi-project requires 2+ projects (1 project = single-project mode)
|
|
232
|
+
if (projects.length > 1) {
|
|
233
|
+
return {
|
|
234
|
+
isMultiProject: true,
|
|
235
|
+
detectionReason: 'multiProject.enabled with projects config',
|
|
236
|
+
projects,
|
|
237
|
+
umbrellaEnabled: false,
|
|
238
|
+
hasBoardMapping: false
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// Check 3: Sync profile with board/area path mapping
|
|
243
|
+
if (config.sync?.profiles) {
|
|
244
|
+
const syncProjects = parseProjectsFromSyncProfiles(config.sync);
|
|
245
|
+
const hasBoardMapping = Object.values(config.sync.profiles).some((p) => p?.config?.boardMapping || p?.config?.areaPathMapping);
|
|
246
|
+
if (syncProjects.length > 1) {
|
|
247
|
+
return {
|
|
248
|
+
isMultiProject: true,
|
|
249
|
+
detectionReason: 'sync profiles with multiple projects',
|
|
250
|
+
projects: syncProjects,
|
|
251
|
+
umbrellaEnabled: false,
|
|
252
|
+
hasBoardMapping
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Check 4: Multiple project folders in specs/
|
|
257
|
+
const folderProjects = detectProjectsFromFolders(specsPath);
|
|
258
|
+
if (folderProjects.length > 1) {
|
|
259
|
+
return {
|
|
260
|
+
isMultiProject: true,
|
|
261
|
+
detectionReason: 'multiple project folders in specs/',
|
|
262
|
+
projects: folderProjects,
|
|
263
|
+
umbrellaEnabled: false,
|
|
264
|
+
hasBoardMapping: false
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
// Single project mode
|
|
268
|
+
return {
|
|
269
|
+
isMultiProject: false,
|
|
270
|
+
detectionReason: 'no multi-project configuration detected',
|
|
271
|
+
projects: [],
|
|
272
|
+
umbrellaEnabled: false,
|
|
273
|
+
hasBoardMapping: false
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Get project prefixes for user story generation
|
|
278
|
+
*
|
|
279
|
+
* @param projectRoot - Path to project root
|
|
280
|
+
* @returns Array of project prefixes (e.g., ['FE', 'BE', 'SHARED'])
|
|
281
|
+
*/
|
|
282
|
+
export function getProjectPrefixes(projectRoot = process.cwd()) {
|
|
283
|
+
const detection = detectMultiProjectMode(projectRoot);
|
|
284
|
+
if (!detection.isMultiProject || detection.projects.length === 0) {
|
|
285
|
+
return [];
|
|
286
|
+
}
|
|
287
|
+
return detection.projects.map(p => p.prefix);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Get project by prefix
|
|
291
|
+
*
|
|
292
|
+
* @param prefix - Project prefix (e.g., 'FE')
|
|
293
|
+
* @param projectRoot - Path to project root
|
|
294
|
+
* @returns Project or undefined
|
|
295
|
+
*/
|
|
296
|
+
export function getProjectByPrefix(prefix, projectRoot = process.cwd()) {
|
|
297
|
+
const detection = detectMultiProjectMode(projectRoot);
|
|
298
|
+
return detection.projects.find(p => p.prefix.toUpperCase() === prefix.toUpperCase());
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Format user story ID with project prefix (if multi-project)
|
|
302
|
+
*
|
|
303
|
+
* @param storyNumber - Story number (1, 2, 3...)
|
|
304
|
+
* @param projectPrefix - Optional project prefix (e.g., 'FE')
|
|
305
|
+
* @param projectRoot - Path to project root
|
|
306
|
+
* @returns Formatted ID (e.g., 'US-FE-001' or 'US-001')
|
|
307
|
+
*/
|
|
308
|
+
export function formatUserStoryId(storyNumber, projectPrefix, projectRoot = process.cwd()) {
|
|
309
|
+
const paddedNumber = String(storyNumber).padStart(3, '0');
|
|
310
|
+
if (projectPrefix) {
|
|
311
|
+
return `US-${projectPrefix.toUpperCase()}-${paddedNumber}`;
|
|
312
|
+
}
|
|
313
|
+
// Auto-detect if multi-project
|
|
314
|
+
const detection = detectMultiProjectMode(projectRoot);
|
|
315
|
+
if (detection.isMultiProject) {
|
|
316
|
+
// If multi-project but no prefix provided, use generic format
|
|
317
|
+
// (caller should provide prefix for proper scoping)
|
|
318
|
+
return `US-${paddedNumber}`;
|
|
319
|
+
}
|
|
320
|
+
return `US-${paddedNumber}`;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Format acceptance criteria ID with project prefix (if multi-project)
|
|
324
|
+
*
|
|
325
|
+
* @param userStoryNumber - User story number (1, 2, 3...)
|
|
326
|
+
* @param acNumber - Acceptance criteria number within story (1, 2, 3...)
|
|
327
|
+
* @param projectPrefix - Optional project prefix (e.g., 'FE')
|
|
328
|
+
* @returns Formatted ID (e.g., 'AC-FE-US1-01' or 'AC-US1-01')
|
|
329
|
+
*/
|
|
330
|
+
export function formatAcceptanceCriteriaId(userStoryNumber, acNumber, projectPrefix) {
|
|
331
|
+
const paddedAcNumber = String(acNumber).padStart(2, '0');
|
|
332
|
+
if (projectPrefix) {
|
|
333
|
+
return `AC-${projectPrefix.toUpperCase()}-US${userStoryNumber}-${paddedAcNumber}`;
|
|
334
|
+
}
|
|
335
|
+
return `AC-US${userStoryNumber}-${paddedAcNumber}`;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Generate template variables for multi-project spec
|
|
339
|
+
*
|
|
340
|
+
* @param detection - Multi-project detection result
|
|
341
|
+
* @returns Template variable map
|
|
342
|
+
*/
|
|
343
|
+
export function generateMultiProjectTemplateVars(detection) {
|
|
344
|
+
const vars = {
|
|
345
|
+
MULTI_PROJECT: detection.isMultiProject ? 'true' : 'false'
|
|
346
|
+
};
|
|
347
|
+
if (!detection.isMultiProject) {
|
|
348
|
+
return vars;
|
|
349
|
+
}
|
|
350
|
+
// Add project-specific variables
|
|
351
|
+
for (const project of detection.projects) {
|
|
352
|
+
const prefixUpper = project.prefix.toUpperCase();
|
|
353
|
+
vars[`PROJECT_${prefixUpper}_ID`] = project.id;
|
|
354
|
+
vars[`PROJECT_${prefixUpper}_PREFIX`] = project.prefix;
|
|
355
|
+
vars[`PROJECT_${prefixUpper}_NAME`] = project.name;
|
|
356
|
+
}
|
|
357
|
+
// Add common project IDs for templates
|
|
358
|
+
const feProject = detection.projects.find(p => p.prefix.toUpperCase() === 'FE');
|
|
359
|
+
const beProject = detection.projects.find(p => p.prefix.toUpperCase() === 'BE');
|
|
360
|
+
const sharedProject = detection.projects.find(p => p.prefix.toUpperCase() === 'SHARED' || p.prefix.toUpperCase() === 'COMMON');
|
|
361
|
+
if (feProject)
|
|
362
|
+
vars.PROJECT_FE_ID = feProject.id;
|
|
363
|
+
if (beProject)
|
|
364
|
+
vars.PROJECT_BE_ID = beProject.id;
|
|
365
|
+
if (sharedProject)
|
|
366
|
+
vars.PROJECT_SHARED_ID = sharedProject.id;
|
|
367
|
+
return vars;
|
|
368
|
+
}
|
|
369
|
+
//# sourceMappingURL=multi-project-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-project-detector.js","sourceRoot":"","sources":["../../../src/utils/multi-project-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.28.
|
|
3
|
+
"version": "0.28.25",
|
|
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",
|
|
@@ -445,24 +445,44 @@ graph TD
|
|
|
445
445
|
|
|
446
446
|
**YOU MUST CHECK THIS BEFORE WRITING ANY USER STORIES:**
|
|
447
447
|
|
|
448
|
-
|
|
449
|
-
# 1. Check config.json for umbrella mode
|
|
450
|
-
cat .specweave/config.json | jq '.umbrella.enabled'
|
|
448
|
+
**Detection Utility** (`src/utils/multi-project-detector.ts`): SpecWeave has automated multi-project detection. When generating specs programmatically, use `detectMultiProjectMode(projectRoot)`. For manual checks, run these commands:
|
|
451
449
|
|
|
452
|
-
|
|
453
|
-
|
|
450
|
+
```bash
|
|
451
|
+
# Quick check: Is multi-project mode enabled?
|
|
452
|
+
Read .specweave/config.json and check for:
|
|
453
|
+
# - umbrella.enabled: true
|
|
454
|
+
# - multiProject.enabled: true
|
|
455
|
+
# - umbrella.childRepos[] (array of repos)
|
|
456
|
+
# - sync.profiles[].config.boardMapping or areaPathMapping
|
|
454
457
|
|
|
455
|
-
#
|
|
456
|
-
|
|
458
|
+
# Alternative: Check for multiple project folders
|
|
459
|
+
Glob ".specweave/docs/internal/specs/*/" and count directories
|
|
460
|
+
# If > 1 directory (excluding 'default') → multi-project mode
|
|
457
461
|
```
|
|
458
462
|
|
|
459
463
|
**Decision Flow:**
|
|
460
464
|
```
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
465
|
+
Step 1: Read .specweave/config.json
|
|
466
|
+
Step 2: Check THESE conditions (ANY = multi-project):
|
|
467
|
+
├─ umbrella.enabled === true AND childRepos.length > 0?
|
|
468
|
+
│ → YES → Use project prefixes from childRepos[].prefix
|
|
469
|
+
├─ multiProject.enabled === true AND projects object has keys?
|
|
470
|
+
│ → YES → Use project prefixes from projects object
|
|
471
|
+
├─ sync.profiles[].config.boardMapping exists?
|
|
472
|
+
│ → YES → Use project IDs from boardMapping values
|
|
473
|
+
├─ Multiple folders in .specweave/docs/internal/specs/?
|
|
474
|
+
│ → YES → Use folder names as project IDs
|
|
475
|
+
└─ User prompt mentions "frontend", "backend", "3 repos"?
|
|
476
|
+
→ YES → Ask user to confirm project prefixes
|
|
477
|
+
|
|
478
|
+
Step 3: If multi-project detected:
|
|
479
|
+
→ MUST use project-scoped user stories (US-FE-001, US-BE-001)
|
|
480
|
+
→ MUST use project-scoped ACs (AC-FE-US1-01, AC-BE-US1-01)
|
|
481
|
+
→ Include 'projects:' array in spec.md frontmatter
|
|
482
|
+
|
|
483
|
+
Step 4: If NO multi-project config:
|
|
484
|
+
→ Use standard user stories (US-001, US-002)
|
|
485
|
+
→ Use standard ACs (AC-US1-01, AC-US2-01)
|
|
466
486
|
```
|
|
467
487
|
|
|
468
488
|
**If multi-project detected, NEVER generate:**
|
|
@@ -472,6 +492,7 @@ Is umbrella.enabled: true?
|
|
|
472
492
|
**ALWAYS generate:**
|
|
473
493
|
- ✅ `US-FE-001`, `US-BE-001`, `US-SHARED-001` (project-scoped)
|
|
474
494
|
- ✅ `AC-FE-US1-01`, `AC-BE-US1-01` (project-scoped ACs)
|
|
495
|
+
- ✅ Frontmatter with `multi_project: true` and `projects:` array
|
|
475
496
|
|
|
476
497
|
---
|
|
477
498
|
|
|
@@ -347,7 +347,7 @@ No tasks defined.
|
|
|
347
347
|
- **NO automatic increment creation**: Imported items live in living docs ONLY
|
|
348
348
|
- User must manually create increment when ready to work on external item
|
|
349
349
|
- **Read-only snapshot**: External items are imported as static snapshots
|
|
350
|
-
- No
|
|
350
|
+
- No two-way sync (external tool → SpecWeave only)
|
|
351
351
|
- **Pagination**: Large imports (500+ items) may take several minutes
|
|
352
352
|
- **API quota**: Uses GitHub/JIRA/ADO API quota
|
|
353
353
|
- GitHub: 5000 requests/hour (authenticated)
|
|
@@ -43,7 +43,7 @@ Claude Code does not support command routing. Each command must be invoked direc
|
|
|
43
43
|
| Command | Description | Example |
|
|
44
44
|
|---------|-------------|---------|
|
|
45
45
|
| `/specweave-github:create-issue` | Create GitHub issue | `/specweave-github:create-issue 0031` |
|
|
46
|
-
| `/specweave-github:sync` |
|
|
46
|
+
| `/specweave-github:sync` | Two-way sync | `/specweave-github:sync 0031` |
|
|
47
47
|
| `/specweave-github:sync-tasks` | Sync tasks as sub-issues | `/specweave-github:sync-tasks 0031` |
|
|
48
48
|
| `/specweave-github:close-issue` | Close GitHub issue | `/specweave-github:close-issue 0031` |
|
|
49
49
|
| `/specweave-github:status` | Show sync status | `/specweave-github:status` |
|
|
@@ -134,20 +134,13 @@ Every increment MUST have `metadata.json` or:
|
|
|
134
134
|
|
|
135
135
|
**⚠️ CRITICAL: Before creating ANY user stories, detect if this is a multi-project (umbrella) setup!**
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
# 1. Check config.json for umbrella mode
|
|
139
|
-
UMBRELLA_ENABLED=$(cat .specweave/config.json 2>/dev/null | jq -r '.umbrella.enabled // false')
|
|
140
|
-
|
|
141
|
-
# 2. Check for childRepos
|
|
142
|
-
CHILD_REPOS=$(cat .specweave/config.json 2>/dev/null | jq -r '.umbrella.childRepos[]?.id // empty' | tr '\n' ',')
|
|
137
|
+
**Automated Detection**: `src/utils/multi-project-detector.ts` provides `detectMultiProjectMode(projectRoot)` which checks ALL config formats and returns `{ isMultiProject, projects, detectionReason }`.
|
|
143
138
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
echo "Project folders: $PROJECT_FOLDERS"
|
|
150
|
-
```
|
|
139
|
+
**Manual check (for agents)**: Read `.specweave/config.json` and check:
|
|
140
|
+
- `umbrella.enabled: true` with `childRepos[]`
|
|
141
|
+
- `multiProject.enabled: true` with `projects{}`
|
|
142
|
+
- `sync.profiles[].config.boardMapping` exists
|
|
143
|
+
- Multiple folders in `.specweave/docs/internal/specs/`
|
|
151
144
|
|
|
152
145
|
**If multi-project detected (`umbrella.enabled: true` OR multiple project folders exist):**
|
|
153
146
|
- ✅ **MUST** generate project-scoped user stories: `US-FE-001`, `US-BE-001`, `US-SHARED-001`
|
|
@@ -360,18 +360,13 @@ spec_generator:
|
|
|
360
360
|
|
|
361
361
|
### Detection (MANDATORY FIRST STEP)
|
|
362
362
|
|
|
363
|
-
**
|
|
363
|
+
**Automated Detection**: Use `detectMultiProjectMode(projectRoot)` from `src/utils/multi-project-detector.ts`. This utility checks ALL config formats automatically.
|
|
364
364
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
cat .specweave/config.json | jq '.umbrella.childRepos[]'
|
|
371
|
-
|
|
372
|
-
# 3. Check for project folders in specs/
|
|
373
|
-
ls -la .specweave/docs/internal/specs/
|
|
374
|
-
```
|
|
365
|
+
**Manual check (for agents)**: Read `.specweave/config.json` and check:
|
|
366
|
+
- `umbrella.enabled` + `childRepos[]`
|
|
367
|
+
- `multiProject.enabled` + `projects{}`
|
|
368
|
+
- `sync.profiles[].config.boardMapping`
|
|
369
|
+
- Multiple folders in `.specweave/docs/internal/specs/`
|
|
375
370
|
|
|
376
371
|
**If ANY of these conditions are TRUE → Multi-project mode ACTIVE:**
|
|
377
372
|
- `umbrella.enabled: true` in config.json
|