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.
Files changed (35) hide show
  1. package/CLAUDE.md +189 -0
  2. package/dist/cli/commands/init.js +1 -1
  3. package/dist/cli/commands/init.js.map +1 -1
  4. package/dist/cli/commands/status-line.d.ts +14 -0
  5. package/dist/cli/commands/status-line.d.ts.map +1 -0
  6. package/dist/cli/commands/status-line.js +75 -0
  7. package/dist/cli/commands/status-line.js.map +1 -0
  8. package/dist/core/status-line/status-line-manager.d.ts +62 -0
  9. package/dist/core/status-line/status-line-manager.d.ts.map +1 -0
  10. package/dist/core/status-line/status-line-manager.js +169 -0
  11. package/dist/core/status-line/status-line-manager.js.map +1 -0
  12. package/dist/core/status-line/types.d.ts +50 -0
  13. package/dist/core/status-line/types.d.ts.map +1 -0
  14. package/dist/core/status-line/types.js +17 -0
  15. package/dist/core/status-line/types.js.map +1 -0
  16. package/dist/utils/project-mapper.d.ts +74 -0
  17. package/dist/utils/project-mapper.d.ts.map +1 -0
  18. package/dist/utils/project-mapper.js +273 -0
  19. package/dist/utils/project-mapper.js.map +1 -0
  20. package/dist/utils/spec-splitter.d.ts +68 -0
  21. package/dist/utils/spec-splitter.d.ts.map +1 -0
  22. package/dist/utils/spec-splitter.js +314 -0
  23. package/dist/utils/spec-splitter.js.map +1 -0
  24. package/package.json +1 -1
  25. package/plugins/specweave/hooks/lib/update-status-line.sh +138 -0
  26. package/plugins/specweave/hooks/post-task-completion.sh +10 -0
  27. package/plugins/specweave/skills/multi-project-spec-mapper/SKILL.md +399 -0
  28. package/plugins/specweave-ado/lib/ado-multi-project-sync.js +453 -0
  29. package/plugins/specweave-ado/lib/ado-multi-project-sync.ts +633 -0
  30. package/plugins/specweave-docs/skills/docusaurus/SKILL.md +17 -3
  31. package/plugins/specweave-docs-preview/commands/preview.md +29 -4
  32. package/plugins/specweave-github/lib/github-multi-project-sync.js +340 -0
  33. package/plugins/specweave-github/lib/github-multi-project-sync.ts +461 -0
  34. package/plugins/specweave-jira/lib/jira-multi-project-sync.js +244 -0
  35. 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.13.6",
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