specweave 0.13.6 → 0.14.0
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 +189 -0
- package/dist/cli/commands/init.js +1 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/status-line.d.ts +14 -0
- package/dist/cli/commands/status-line.d.ts.map +1 -0
- package/dist/cli/commands/status-line.js +75 -0
- package/dist/cli/commands/status-line.js.map +1 -0
- package/dist/core/status-line/status-line-manager.d.ts +62 -0
- package/dist/core/status-line/status-line-manager.d.ts.map +1 -0
- package/dist/core/status-line/status-line-manager.js +169 -0
- package/dist/core/status-line/status-line-manager.js.map +1 -0
- package/dist/core/status-line/types.d.ts +50 -0
- package/dist/core/status-line/types.d.ts.map +1 -0
- package/dist/core/status-line/types.js +17 -0
- package/dist/core/status-line/types.js.map +1 -0
- package/dist/utils/project-mapper.d.ts +74 -0
- package/dist/utils/project-mapper.d.ts.map +1 -0
- package/dist/utils/project-mapper.js +273 -0
- package/dist/utils/project-mapper.js.map +1 -0
- package/dist/utils/spec-splitter.d.ts +68 -0
- package/dist/utils/spec-splitter.d.ts.map +1 -0
- package/dist/utils/spec-splitter.js +314 -0
- package/dist/utils/spec-splitter.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/hooks/lib/update-status-line.sh +138 -0
- package/plugins/specweave/hooks/post-task-completion.sh +10 -0
- package/plugins/specweave/skills/multi-project-spec-mapper/SKILL.md +399 -0
- package/plugins/specweave-ado/lib/ado-multi-project-sync.js +453 -0
- package/plugins/specweave-ado/lib/ado-multi-project-sync.ts +633 -0
- package/plugins/specweave-docs/skills/docusaurus/SKILL.md +17 -3
- package/plugins/specweave-docs-preview/commands/preview.md +29 -4
- package/plugins/specweave-github/lib/github-multi-project-sync.js +340 -0
- package/plugins/specweave-github/lib/github-multi-project-sync.ts +461 -0
- package/plugins/specweave-jira/lib/jira-multi-project-sync.js +244 -0
- package/plugins/specweave-jira/lib/jira-multi-project-sync.ts +358 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Splitter - Split Monolithic Specs into Project-Specific Files
|
|
3
|
+
*
|
|
4
|
+
* Takes a comprehensive spec file covering multiple projects and intelligently
|
|
5
|
+
* splits it into project-specific spec files (FE/, BE/, MOBILE/, etc.)
|
|
6
|
+
*
|
|
7
|
+
* @module spec-splitter
|
|
8
|
+
*/
|
|
9
|
+
import { type UserStory, type ProjectRule } from './project-mapper.js';
|
|
10
|
+
export interface SpecMetadata {
|
|
11
|
+
specId?: string;
|
|
12
|
+
spec_id?: string;
|
|
13
|
+
title: string;
|
|
14
|
+
version: string;
|
|
15
|
+
status: string;
|
|
16
|
+
created: string;
|
|
17
|
+
lastUpdated?: string;
|
|
18
|
+
last_updated?: string;
|
|
19
|
+
authors: string[];
|
|
20
|
+
reviewers?: string[];
|
|
21
|
+
priority: string;
|
|
22
|
+
estimatedEffort?: string;
|
|
23
|
+
estimated_effort?: string;
|
|
24
|
+
targetRelease?: string;
|
|
25
|
+
target_release?: string;
|
|
26
|
+
jiraSync?: boolean;
|
|
27
|
+
jira_sync?: boolean;
|
|
28
|
+
jiraProjects?: string[];
|
|
29
|
+
jira_projects?: string[];
|
|
30
|
+
}
|
|
31
|
+
export interface ParsedSpec {
|
|
32
|
+
metadata: SpecMetadata;
|
|
33
|
+
executiveSummary: string;
|
|
34
|
+
problemStatement: string;
|
|
35
|
+
userStories: UserStory[];
|
|
36
|
+
functionalRequirements: string;
|
|
37
|
+
nonFunctionalRequirements: string;
|
|
38
|
+
successMetrics: string;
|
|
39
|
+
technicalArchitecture: string;
|
|
40
|
+
testStrategy: string;
|
|
41
|
+
riskAnalysis: string;
|
|
42
|
+
futureRoadmap: string;
|
|
43
|
+
appendices: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Parse spec file into structured format
|
|
47
|
+
*
|
|
48
|
+
* @param specPath Path to spec.md file
|
|
49
|
+
* @returns Parsed spec structure
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseSpecFile(specPath: string): Promise<ParsedSpec>;
|
|
52
|
+
/**
|
|
53
|
+
* Split spec into project-specific files
|
|
54
|
+
*
|
|
55
|
+
* @param specPath Path to monolithic spec.md
|
|
56
|
+
* @param outputDir Directory to write project-specific specs (e.g., .specweave/docs/internal/specs/)
|
|
57
|
+
* @param projectRules Custom project mapping rules (optional)
|
|
58
|
+
* @returns Map of projectId → output file path
|
|
59
|
+
*/
|
|
60
|
+
export declare function splitSpecIntoProjects(specPath: string, outputDir: string, projectRules?: ProjectRule[]): Promise<Map<string, string>>;
|
|
61
|
+
/**
|
|
62
|
+
* Create multi-project folder structure
|
|
63
|
+
*
|
|
64
|
+
* @param specsDir Base specs directory (.specweave/docs/internal/specs/)
|
|
65
|
+
* @param projectIds Array of project IDs (e.g., ['FE', 'BE', 'MOBILE'])
|
|
66
|
+
*/
|
|
67
|
+
export declare function createMultiProjectFolderStructure(specsDir: string, projectIds: string[]): Promise<void>;
|
|
68
|
+
//# sourceMappingURL=spec-splitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-splitter.d.ts","sourceRoot":"","sources":["../../src/utils/spec-splitter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,YAAY,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,SAAS,EAAE,CAAC;IACzB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,yBAAyB,EAAE,MAAM,CAAC;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA+BzE;AA8ID;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,YAAY,GAAE,WAAW,EAA0B,GAClD,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA0B9B;AA6GD;;;;;GAKG;AACH,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,IAAI,CAAC,CAgCf"}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Splitter - Split Monolithic Specs into Project-Specific Files
|
|
3
|
+
*
|
|
4
|
+
* Takes a comprehensive spec file covering multiple projects and intelligently
|
|
5
|
+
* splits it into project-specific spec files (FE/, BE/, MOBILE/, etc.)
|
|
6
|
+
*
|
|
7
|
+
* @module spec-splitter
|
|
8
|
+
*/
|
|
9
|
+
import fs from 'fs-extra';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { splitSpecByProject, DEFAULT_PROJECT_RULES } from './project-mapper.js';
|
|
12
|
+
/**
|
|
13
|
+
* Parse spec file into structured format
|
|
14
|
+
*
|
|
15
|
+
* @param specPath Path to spec.md file
|
|
16
|
+
* @returns Parsed spec structure
|
|
17
|
+
*/
|
|
18
|
+
export async function parseSpecFile(specPath) {
|
|
19
|
+
const content = await fs.readFile(specPath, 'utf-8');
|
|
20
|
+
// Extract frontmatter
|
|
21
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
22
|
+
if (!frontmatterMatch) {
|
|
23
|
+
throw new Error('No frontmatter found in spec file');
|
|
24
|
+
}
|
|
25
|
+
const metadata = parseFrontmatter(frontmatterMatch[1]);
|
|
26
|
+
// Extract sections
|
|
27
|
+
const sections = extractSections(content);
|
|
28
|
+
// Parse user stories
|
|
29
|
+
const userStories = parseUserStories(sections.userStories || '');
|
|
30
|
+
return {
|
|
31
|
+
metadata,
|
|
32
|
+
executiveSummary: sections.executiveSummary || '',
|
|
33
|
+
problemStatement: sections.problemStatement || '',
|
|
34
|
+
userStories,
|
|
35
|
+
functionalRequirements: sections.functionalRequirements || '',
|
|
36
|
+
nonFunctionalRequirements: sections.nonFunctionalRequirements || '',
|
|
37
|
+
successMetrics: sections.successMetrics || '',
|
|
38
|
+
technicalArchitecture: sections.technicalArchitecture || '',
|
|
39
|
+
testStrategy: sections.testStrategy || '',
|
|
40
|
+
riskAnalysis: sections.riskAnalysis || '',
|
|
41
|
+
futureRoadmap: sections.futureRoadmap || '',
|
|
42
|
+
appendices: sections.appendices || ''
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Parse YAML frontmatter into metadata object
|
|
47
|
+
*/
|
|
48
|
+
function parseFrontmatter(frontmatter) {
|
|
49
|
+
const lines = frontmatter.split('\n');
|
|
50
|
+
const metadata = {};
|
|
51
|
+
let currentKey = null;
|
|
52
|
+
let currentArray = [];
|
|
53
|
+
for (const line of lines) {
|
|
54
|
+
// Check for array items (starts with -)
|
|
55
|
+
if (currentKey && line.trim().startsWith('-')) {
|
|
56
|
+
const value = line.trim().slice(1).trim();
|
|
57
|
+
currentArray.push(value);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
// If we were collecting an array, save it
|
|
61
|
+
if (currentKey && currentArray.length > 0) {
|
|
62
|
+
metadata[currentKey] = currentArray;
|
|
63
|
+
currentArray = [];
|
|
64
|
+
currentKey = null;
|
|
65
|
+
}
|
|
66
|
+
// Parse key: value line
|
|
67
|
+
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
68
|
+
if (match) {
|
|
69
|
+
const key = match[1];
|
|
70
|
+
const value = match[2].trim();
|
|
71
|
+
// Handle arrays (JSON format like ["a", "b"])
|
|
72
|
+
if (value.startsWith('[')) {
|
|
73
|
+
metadata[key] = JSON.parse(value);
|
|
74
|
+
}
|
|
75
|
+
// Handle empty array (key: with nothing after)
|
|
76
|
+
else if (!value || value === '') {
|
|
77
|
+
currentKey = key;
|
|
78
|
+
currentArray = [];
|
|
79
|
+
}
|
|
80
|
+
// Handle boolean
|
|
81
|
+
else if (value === 'true' || value === 'false') {
|
|
82
|
+
metadata[key] = value === 'true';
|
|
83
|
+
}
|
|
84
|
+
// Handle string
|
|
85
|
+
else {
|
|
86
|
+
metadata[key] = value.replace(/^["']|["']$/g, '');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Save any remaining array
|
|
91
|
+
if (currentKey && currentArray.length > 0) {
|
|
92
|
+
metadata[currentKey] = currentArray;
|
|
93
|
+
}
|
|
94
|
+
return metadata;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Extract sections from spec markdown
|
|
98
|
+
*/
|
|
99
|
+
function extractSections(content) {
|
|
100
|
+
const sections = {};
|
|
101
|
+
// Remove frontmatter
|
|
102
|
+
const withoutFrontmatter = content.replace(/^---\n[\s\S]*?\n---\n/, '');
|
|
103
|
+
// Split by h2 headers (##)
|
|
104
|
+
const parts = withoutFrontmatter.split(/^## /m);
|
|
105
|
+
for (const part of parts) {
|
|
106
|
+
if (!part.trim())
|
|
107
|
+
continue;
|
|
108
|
+
const lines = part.split('\n');
|
|
109
|
+
const sectionName = lines[0].trim();
|
|
110
|
+
const sectionContent = lines.slice(1).join('\n').trim();
|
|
111
|
+
// Normalize section names
|
|
112
|
+
const normalizedName = sectionName
|
|
113
|
+
.replace(/[^a-zA-Z0-9\s]/g, '')
|
|
114
|
+
.replace(/\s+/g, '')
|
|
115
|
+
.replace(/^(.)/, (m) => m.toLowerCase());
|
|
116
|
+
sections[normalizedName] = sectionContent;
|
|
117
|
+
}
|
|
118
|
+
return sections;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Parse user stories from spec section
|
|
122
|
+
*/
|
|
123
|
+
function parseUserStories(userStoriesSection) {
|
|
124
|
+
const stories = [];
|
|
125
|
+
// Split by h3 headers (###)
|
|
126
|
+
const storyParts = userStoriesSection.split(/^### /m);
|
|
127
|
+
for (const part of storyParts) {
|
|
128
|
+
if (!part.trim())
|
|
129
|
+
continue;
|
|
130
|
+
const lines = part.split('\n');
|
|
131
|
+
const titleLine = lines[0].trim();
|
|
132
|
+
// Extract US-XXX: Title
|
|
133
|
+
const match = titleLine.match(/^(US-\d+):\s*(.+?)(?:\s*\(Priority|$)/);
|
|
134
|
+
if (!match)
|
|
135
|
+
continue;
|
|
136
|
+
const id = match[1];
|
|
137
|
+
const title = match[2].trim();
|
|
138
|
+
// Extract description (As a... I want... So that...)
|
|
139
|
+
const descriptionMatch = part.match(/\*\*As a\*\*\s+(.+?)\n\*\*I want\*\*\s+(.+?)\n\*\*So that\*\*\s+(.+?)(?:\n|$)/s);
|
|
140
|
+
const description = descriptionMatch
|
|
141
|
+
? `As a ${descriptionMatch[1].trim()} I want ${descriptionMatch[2].trim()} So that ${descriptionMatch[3].trim()}`
|
|
142
|
+
: '';
|
|
143
|
+
// Extract acceptance criteria
|
|
144
|
+
const acceptanceCriteria = [];
|
|
145
|
+
const acMatches = part.matchAll(/\*\*AC-[A-Z0-9-]+\*\*:\s*(.+?)(?:\n-|\n\*\*|$)/gs);
|
|
146
|
+
for (const acMatch of acMatches) {
|
|
147
|
+
acceptanceCriteria.push(acMatch[1].trim());
|
|
148
|
+
}
|
|
149
|
+
// Extract technical context (if present)
|
|
150
|
+
const technicalContextMatch = part.match(/#### Technical Context\n\n([\s\S]+?)(?:\n####|\n---|\n###|$)/);
|
|
151
|
+
const technicalContext = technicalContextMatch ? technicalContextMatch[1].trim() : undefined;
|
|
152
|
+
stories.push({
|
|
153
|
+
id,
|
|
154
|
+
title,
|
|
155
|
+
description,
|
|
156
|
+
acceptanceCriteria,
|
|
157
|
+
technicalContext
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return stories;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Split spec into project-specific files
|
|
164
|
+
*
|
|
165
|
+
* @param specPath Path to monolithic spec.md
|
|
166
|
+
* @param outputDir Directory to write project-specific specs (e.g., .specweave/docs/internal/specs/)
|
|
167
|
+
* @param projectRules Custom project mapping rules (optional)
|
|
168
|
+
* @returns Map of projectId → output file path
|
|
169
|
+
*/
|
|
170
|
+
export async function splitSpecIntoProjects(specPath, outputDir, projectRules = DEFAULT_PROJECT_RULES) {
|
|
171
|
+
// Parse spec
|
|
172
|
+
const parsedSpec = await parseSpecFile(specPath);
|
|
173
|
+
// Split user stories by project
|
|
174
|
+
const projectStories = splitSpecByProject(parsedSpec.userStories, projectRules);
|
|
175
|
+
// Create output map
|
|
176
|
+
const outputMap = new Map();
|
|
177
|
+
// Generate project-specific specs
|
|
178
|
+
for (const [projectId, stories] of projectStories.entries()) {
|
|
179
|
+
const projectDir = path.join(outputDir, projectId);
|
|
180
|
+
await fs.ensureDir(projectDir);
|
|
181
|
+
const specId = (parsedSpec.metadata.specId || parsedSpec.metadata.spec_id || '0001').replace(/^SPEC-/, '');
|
|
182
|
+
const outputPath = path.join(projectDir, `spec-${specId.toLowerCase()}-${projectId.toLowerCase()}.md`);
|
|
183
|
+
const projectSpec = generateProjectSpec(parsedSpec, projectId, stories);
|
|
184
|
+
await fs.writeFile(outputPath, projectSpec, 'utf-8');
|
|
185
|
+
outputMap.set(projectId, outputPath);
|
|
186
|
+
}
|
|
187
|
+
return outputMap;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Generate project-specific spec file
|
|
191
|
+
*/
|
|
192
|
+
function generateProjectSpec(parsedSpec, projectId, userStories) {
|
|
193
|
+
const lines = [];
|
|
194
|
+
// Frontmatter
|
|
195
|
+
lines.push('---');
|
|
196
|
+
lines.push(`spec_id: ${parsedSpec.metadata.specId}`);
|
|
197
|
+
lines.push(`project: ${projectId}`);
|
|
198
|
+
lines.push(`title: "${parsedSpec.metadata.title} - ${projectId}"`);
|
|
199
|
+
lines.push(`version: ${parsedSpec.metadata.version}`);
|
|
200
|
+
lines.push(`status: ${parsedSpec.metadata.status}`);
|
|
201
|
+
lines.push(`created: ${parsedSpec.metadata.created}`);
|
|
202
|
+
lines.push(`last_updated: ${parsedSpec.metadata.lastUpdated}`);
|
|
203
|
+
lines.push(`authors:`);
|
|
204
|
+
for (const author of parsedSpec.metadata.authors) {
|
|
205
|
+
lines.push(` - ${author}`);
|
|
206
|
+
}
|
|
207
|
+
lines.push(`priority: ${parsedSpec.metadata.priority}`);
|
|
208
|
+
lines.push(`estimated_effort: ${parsedSpec.metadata.estimatedEffort}`);
|
|
209
|
+
lines.push(`target_release: ${parsedSpec.metadata.targetRelease}`);
|
|
210
|
+
if (parsedSpec.metadata.jiraSync) {
|
|
211
|
+
lines.push(`jira_sync: true`);
|
|
212
|
+
lines.push(`jira_project: ${projectId}`);
|
|
213
|
+
}
|
|
214
|
+
lines.push('---');
|
|
215
|
+
lines.push('');
|
|
216
|
+
// Title
|
|
217
|
+
lines.push(`# SPEC-${parsedSpec.metadata.specId}: ${parsedSpec.metadata.title} - ${projectId}`);
|
|
218
|
+
lines.push('');
|
|
219
|
+
// Executive Summary (project-specific)
|
|
220
|
+
lines.push('## Executive Summary');
|
|
221
|
+
lines.push('');
|
|
222
|
+
lines.push(`**Project**: ${projectId}`);
|
|
223
|
+
lines.push(`**Scope**: ${userStories.length} user stories for ${projectId} component`);
|
|
224
|
+
lines.push('');
|
|
225
|
+
lines.push(parsedSpec.executiveSummary);
|
|
226
|
+
lines.push('');
|
|
227
|
+
// User Stories (filtered for this project)
|
|
228
|
+
lines.push('## User Stories');
|
|
229
|
+
lines.push('');
|
|
230
|
+
for (const story of userStories) {
|
|
231
|
+
lines.push(`### ${story.id}: ${story.title}`);
|
|
232
|
+
lines.push('');
|
|
233
|
+
lines.push(story.description);
|
|
234
|
+
lines.push('');
|
|
235
|
+
if (story.acceptanceCriteria.length > 0) {
|
|
236
|
+
lines.push('#### Acceptance Criteria');
|
|
237
|
+
lines.push('');
|
|
238
|
+
for (const ac of story.acceptanceCriteria) {
|
|
239
|
+
lines.push(`**${ac}**`);
|
|
240
|
+
lines.push('');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (story.technicalContext) {
|
|
244
|
+
lines.push('#### Technical Context');
|
|
245
|
+
lines.push('');
|
|
246
|
+
lines.push(story.technicalContext);
|
|
247
|
+
lines.push('');
|
|
248
|
+
}
|
|
249
|
+
lines.push('---');
|
|
250
|
+
lines.push('');
|
|
251
|
+
}
|
|
252
|
+
// Shared sections (include full content from original spec)
|
|
253
|
+
if (parsedSpec.functionalRequirements) {
|
|
254
|
+
lines.push('## Functional Requirements (Shared)');
|
|
255
|
+
lines.push('');
|
|
256
|
+
lines.push('*See main spec for complete functional requirements*');
|
|
257
|
+
lines.push('');
|
|
258
|
+
}
|
|
259
|
+
if (parsedSpec.nonFunctionalRequirements) {
|
|
260
|
+
lines.push('## Non-Functional Requirements (Shared)');
|
|
261
|
+
lines.push('');
|
|
262
|
+
lines.push('*See main spec for complete non-functional requirements*');
|
|
263
|
+
lines.push('');
|
|
264
|
+
}
|
|
265
|
+
if (parsedSpec.technicalArchitecture) {
|
|
266
|
+
lines.push('## Technical Architecture');
|
|
267
|
+
lines.push('');
|
|
268
|
+
lines.push(parsedSpec.technicalArchitecture);
|
|
269
|
+
lines.push('');
|
|
270
|
+
}
|
|
271
|
+
// Footer
|
|
272
|
+
lines.push('---');
|
|
273
|
+
lines.push('');
|
|
274
|
+
lines.push('**Document End**');
|
|
275
|
+
lines.push('');
|
|
276
|
+
return lines.join('\n');
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Create multi-project folder structure
|
|
280
|
+
*
|
|
281
|
+
* @param specsDir Base specs directory (.specweave/docs/internal/specs/)
|
|
282
|
+
* @param projectIds Array of project IDs (e.g., ['FE', 'BE', 'MOBILE'])
|
|
283
|
+
*/
|
|
284
|
+
export async function createMultiProjectFolderStructure(specsDir, projectIds) {
|
|
285
|
+
await fs.ensureDir(specsDir);
|
|
286
|
+
for (const projectId of projectIds) {
|
|
287
|
+
const projectDir = path.join(specsDir, projectId);
|
|
288
|
+
await fs.ensureDir(projectDir);
|
|
289
|
+
// Create README.md for project
|
|
290
|
+
const readme = `# ${projectId} Specifications
|
|
291
|
+
|
|
292
|
+
This folder contains specifications for the **${projectId}** project.
|
|
293
|
+
|
|
294
|
+
## Organization
|
|
295
|
+
|
|
296
|
+
- \`spec-NNNN-*.md\` - Project-specific specifications (living docs)
|
|
297
|
+
- Each spec is permanently archived here after increment completion
|
|
298
|
+
- Specs are auto-generated from increment specs and split by project
|
|
299
|
+
|
|
300
|
+
## External Sync
|
|
301
|
+
|
|
302
|
+
${projectId} specs sync to:
|
|
303
|
+
- **JIRA**: Project ${projectId} (Epics)
|
|
304
|
+
- **GitHub**: Issues tagged with \`${projectId}\` label
|
|
305
|
+
|
|
306
|
+
## Maintenance
|
|
307
|
+
|
|
308
|
+
- Specs are auto-updated via living docs sync hooks
|
|
309
|
+
- Manual edits should be made in increment specs, not here
|
|
310
|
+
`;
|
|
311
|
+
await fs.writeFile(path.join(projectDir, 'README.md'), readme, 'utf-8');
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=spec-splitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-splitter.js","sourceRoot":"","sources":["../../src/utils/spec-splitter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specweave",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
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",
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# update-status-line.sh
|
|
4
|
+
#
|
|
5
|
+
# Updates the status line cache with current increment progress.
|
|
6
|
+
# Called by post-task-completion hook.
|
|
7
|
+
#
|
|
8
|
+
# Performance: 10-50ms (runs async in hook, user doesn't wait)
|
|
9
|
+
#
|
|
10
|
+
# Cache format (.specweave/state/status-line.json):
|
|
11
|
+
# {
|
|
12
|
+
# "incrementId": "0017-sync-architecture-fix",
|
|
13
|
+
# "incrementName": "sync-architecture-fix",
|
|
14
|
+
# "totalTasks": 30,
|
|
15
|
+
# "completedTasks": 15,
|
|
16
|
+
# "percentage": 50,
|
|
17
|
+
# "currentTask": {
|
|
18
|
+
# "id": "T-016",
|
|
19
|
+
# "title": "Update documentation"
|
|
20
|
+
# },
|
|
21
|
+
# "lastUpdate": "2025-11-10T15:30:00Z",
|
|
22
|
+
# "lastModified": 1699632600
|
|
23
|
+
# }
|
|
24
|
+
|
|
25
|
+
set -euo pipefail
|
|
26
|
+
|
|
27
|
+
# Find project root
|
|
28
|
+
find_project_root() {
|
|
29
|
+
local dir="$PWD"
|
|
30
|
+
while [[ "$dir" != "/" ]]; do
|
|
31
|
+
if [[ -d "$dir/.specweave" ]]; then
|
|
32
|
+
echo "$dir"
|
|
33
|
+
return 0
|
|
34
|
+
fi
|
|
35
|
+
dir=$(dirname "$dir")
|
|
36
|
+
done
|
|
37
|
+
echo "$PWD"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
PROJECT_ROOT=$(find_project_root)
|
|
41
|
+
CACHE_FILE="$PROJECT_ROOT/.specweave/state/status-line.json"
|
|
42
|
+
STATE_FILE="$PROJECT_ROOT/.specweave/state/active-increment.json"
|
|
43
|
+
|
|
44
|
+
# Ensure state directory exists
|
|
45
|
+
mkdir -p "$PROJECT_ROOT/.specweave/state"
|
|
46
|
+
|
|
47
|
+
# Check if there's an active increment
|
|
48
|
+
if [[ ! -f "$STATE_FILE" ]]; then
|
|
49
|
+
# No active increment = clear cache
|
|
50
|
+
echo '{}' > "$CACHE_FILE"
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# Get active increment ID
|
|
55
|
+
INCREMENT_ID=$(jq -r '.id // empty' "$STATE_FILE" 2>/dev/null || echo "")
|
|
56
|
+
if [[ -z "$INCREMENT_ID" ]]; then
|
|
57
|
+
echo '{}' > "$CACHE_FILE"
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
TASKS_FILE="$PROJECT_ROOT/.specweave/increments/$INCREMENT_ID/tasks.md"
|
|
62
|
+
|
|
63
|
+
# No tasks file yet? (Planning phase)
|
|
64
|
+
if [[ ! -f "$TASKS_FILE" ]]; then
|
|
65
|
+
echo '{}' > "$CACHE_FILE"
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Get tasks.md mtime for invalidation detection
|
|
70
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
71
|
+
# macOS
|
|
72
|
+
MTIME=$(stat -f %m "$TASKS_FILE" 2>/dev/null || echo 0)
|
|
73
|
+
else
|
|
74
|
+
# Linux
|
|
75
|
+
MTIME=$(stat -c %Y "$TASKS_FILE" 2>/dev/null || echo 0)
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Parse tasks.md (THIS is the slow part: 10-50ms)
|
|
79
|
+
TOTAL_TASKS=$(grep -c '^## T-' "$TASKS_FILE" 2>/dev/null || echo 0)
|
|
80
|
+
COMPLETED_TASKS=$(grep -c '^\[x\]' "$TASKS_FILE" 2>/dev/null || echo 0)
|
|
81
|
+
|
|
82
|
+
# Calculate percentage
|
|
83
|
+
if [[ "$TOTAL_TASKS" -gt 0 ]]; then
|
|
84
|
+
PERCENTAGE=$(( COMPLETED_TASKS * 100 / TOTAL_TASKS ))
|
|
85
|
+
else
|
|
86
|
+
PERCENTAGE=0
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Find current task (first incomplete task)
|
|
90
|
+
# Strategy: Find first [ ] checkbox, then get the task heading above it
|
|
91
|
+
CURRENT_TASK_LINE=$(grep -B1 '^\[ \]' "$TASKS_FILE" 2>/dev/null | grep '^## T-' | head -1 || echo "")
|
|
92
|
+
CURRENT_TASK_ID=""
|
|
93
|
+
CURRENT_TASK_TITLE=""
|
|
94
|
+
|
|
95
|
+
if [[ -n "$CURRENT_TASK_LINE" ]]; then
|
|
96
|
+
# Extract task ID (T-NNN)
|
|
97
|
+
CURRENT_TASK_ID=$(echo "$CURRENT_TASK_LINE" | grep -o 'T-[0-9][0-9]*' || echo "")
|
|
98
|
+
|
|
99
|
+
# Extract task title (after "## T-NNN: ")
|
|
100
|
+
# Use parameter expansion to remove prefix
|
|
101
|
+
TEMP="${CURRENT_TASK_LINE#*: }"
|
|
102
|
+
CURRENT_TASK_TITLE=$(echo "$TEMP" | head -c 50)
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# Extract increment name (remove leading 4-digit number and dash)
|
|
106
|
+
INCREMENT_NAME=$(echo "$INCREMENT_ID" | sed 's/^[0-9]\{4\}-//')
|
|
107
|
+
|
|
108
|
+
# Build current task JSON
|
|
109
|
+
if [[ -n "$CURRENT_TASK_ID" ]]; then
|
|
110
|
+
CURRENT_TASK_JSON=$(jq -n \
|
|
111
|
+
--arg id "$CURRENT_TASK_ID" \
|
|
112
|
+
--arg title "$CURRENT_TASK_TITLE" \
|
|
113
|
+
'{id: $id, title: $title}')
|
|
114
|
+
else
|
|
115
|
+
CURRENT_TASK_JSON="null"
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# Write cache atomically using jq
|
|
119
|
+
jq -n \
|
|
120
|
+
--arg id "$INCREMENT_ID" \
|
|
121
|
+
--arg name "$INCREMENT_NAME" \
|
|
122
|
+
--argjson total "$TOTAL_TASKS" \
|
|
123
|
+
--argjson completed "$COMPLETED_TASKS" \
|
|
124
|
+
--argjson percentage "$PERCENTAGE" \
|
|
125
|
+
--argjson task "$CURRENT_TASK_JSON" \
|
|
126
|
+
--argjson mtime "$MTIME" \
|
|
127
|
+
'{
|
|
128
|
+
incrementId: $id,
|
|
129
|
+
incrementName: $name,
|
|
130
|
+
totalTasks: $total,
|
|
131
|
+
completedTasks: $completed,
|
|
132
|
+
percentage: $percentage,
|
|
133
|
+
currentTask: $task,
|
|
134
|
+
lastUpdate: (now | strftime("%Y-%m-%dT%H:%M:%SZ")),
|
|
135
|
+
lastModified: $mtime
|
|
136
|
+
}' > "$CACHE_FILE"
|
|
137
|
+
|
|
138
|
+
exit 0
|
|
@@ -289,6 +289,16 @@ play_sound() {
|
|
|
289
289
|
esac
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
+
# ============================================================================
|
|
293
|
+
# STATUS LINE UPDATE
|
|
294
|
+
# ============================================================================
|
|
295
|
+
# Update status line cache BEFORE playing sound (async, non-blocking)
|
|
296
|
+
# Cache will be read by status line renderer for fast display (<1ms)
|
|
297
|
+
|
|
298
|
+
echo "[$(date)] 📊 Updating status line cache" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
299
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
300
|
+
bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
|
|
301
|
+
|
|
292
302
|
if [ "$SESSION_ENDING" = "true" ]; then
|
|
293
303
|
echo "[$(date)] 🔔 Playing completion sound" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
294
304
|
play_sound
|