specweave 0.22.14 → 0.23.1
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 +178 -1
- package/dist/src/cli/commands/import-external.d.ts +22 -0
- package/dist/src/cli/commands/import-external.d.ts.map +1 -0
- package/dist/src/cli/commands/import-external.js +282 -0
- package/dist/src/cli/commands/import-external.js.map +1 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +46 -0
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/helpers/github-repo-selector.d.ts +59 -0
- package/dist/src/cli/helpers/github-repo-selector.d.ts.map +1 -0
- package/dist/src/cli/helpers/github-repo-selector.js +265 -0
- package/dist/src/cli/helpers/github-repo-selector.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.js +5 -17
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/config/types.d.ts +16 -16
- package/dist/src/core/increment/ac-status-manager.d.ts.map +1 -1
- package/dist/src/core/increment/ac-status-manager.js +4 -2
- package/dist/src/core/increment/ac-status-manager.js.map +1 -1
- package/dist/src/core/increment/completion-validator.d.ts +30 -1
- package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
- package/dist/src/core/increment/completion-validator.js +151 -3
- package/dist/src/core/increment/completion-validator.js.map +1 -1
- package/dist/src/core/increment/increment-archiver.d.ts +25 -0
- package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
- package/dist/src/core/increment/increment-archiver.js +130 -3
- package/dist/src/core/increment/increment-archiver.js.map +1 -1
- package/dist/src/core/living-docs/feature-archiver.d.ts +37 -0
- package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -1
- package/dist/src/core/living-docs/feature-archiver.js +262 -18
- package/dist/src/core/living-docs/feature-archiver.js.map +1 -1
- package/dist/src/core/living-docs/feature-id-manager.d.ts +17 -0
- package/dist/src/core/living-docs/feature-id-manager.d.ts.map +1 -1
- package/dist/src/core/living-docs/feature-id-manager.js +25 -0
- package/dist/src/core/living-docs/feature-id-manager.js.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.d.ts +14 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +46 -0
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/repo-structure/repo-id-generator.d.ts +20 -0
- package/dist/src/core/repo-structure/repo-id-generator.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-id-generator.js +44 -0
- package/dist/src/core/repo-structure/repo-id-generator.js.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.js +5 -2
- package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
- package/dist/src/core/sync/sync-event-logger.d.ts +15 -1
- package/dist/src/core/sync/sync-event-logger.d.ts.map +1 -1
- package/dist/src/core/sync/sync-event-logger.js +39 -1
- package/dist/src/core/sync/sync-event-logger.js.map +1 -1
- package/dist/src/core/types/sync-config-validator.d.ts +57 -0
- package/dist/src/core/types/sync-config-validator.d.ts.map +1 -0
- package/dist/src/core/types/sync-config-validator.js +116 -0
- package/dist/src/core/types/sync-config-validator.js.map +1 -0
- package/dist/src/importers/duplicate-detector.d.ts +107 -0
- package/dist/src/importers/duplicate-detector.d.ts.map +1 -0
- package/dist/src/importers/duplicate-detector.js +189 -0
- package/dist/src/importers/duplicate-detector.js.map +1 -0
- package/dist/src/importers/import-coordinator.d.ts +15 -0
- package/dist/src/importers/import-coordinator.d.ts.map +1 -1
- package/dist/src/importers/import-coordinator.js +43 -1
- package/dist/src/importers/import-coordinator.js.map +1 -1
- package/dist/src/importers/item-converter.d.ts +5 -0
- package/dist/src/importers/item-converter.d.ts.map +1 -1
- package/dist/src/importers/item-converter.js +27 -2
- package/dist/src/importers/item-converter.js.map +1 -1
- package/dist/src/importers/rate-limiter.d.ts +128 -0
- package/dist/src/importers/rate-limiter.d.ts.map +1 -0
- package/dist/src/importers/rate-limiter.js +200 -0
- package/dist/src/importers/rate-limiter.js.map +1 -0
- package/dist/src/init/compliance/types.d.ts +2 -2
- package/dist/src/integrations/ado/ado-client.d.ts +6 -0
- package/dist/src/integrations/ado/ado-client.d.ts.map +1 -1
- package/dist/src/integrations/ado/ado-client.js +23 -0
- package/dist/src/integrations/ado/ado-client.js.map +1 -1
- package/dist/src/integrations/jira/jira-client.d.ts +6 -0
- package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-client.js +38 -0
- package/dist/src/integrations/jira/jira-client.js.map +1 -1
- package/dist/src/sync/external-item-sync-service.d.ts +150 -0
- package/dist/src/sync/external-item-sync-service.d.ts.map +1 -0
- package/dist/src/sync/external-item-sync-service.js +241 -0
- package/dist/src/sync/external-item-sync-service.js.map +1 -0
- package/dist/src/sync/format-preservation-sync.d.ts +90 -0
- package/dist/src/sync/format-preservation-sync.d.ts.map +1 -0
- package/dist/src/sync/format-preservation-sync.js +173 -0
- package/dist/src/sync/format-preservation-sync.js.map +1 -0
- package/dist/src/sync/index.d.ts +8 -0
- package/dist/src/sync/index.d.ts.map +1 -0
- package/dist/src/sync/index.js +6 -0
- package/dist/src/sync/index.js.map +1 -0
- package/dist/src/sync/sync-coordinator.d.ts +49 -0
- package/dist/src/sync/sync-coordinator.d.ts.map +1 -0
- package/dist/src/sync/sync-coordinator.js +248 -0
- package/dist/src/sync/sync-coordinator.js.map +1 -0
- package/dist/src/sync/sync-metadata.d.ts +75 -0
- package/dist/src/sync/sync-metadata.d.ts.map +1 -0
- package/dist/src/sync/sync-metadata.js +100 -0
- package/dist/src/sync/sync-metadata.js.map +1 -0
- package/dist/src/types/living-docs-us-file.d.ts +63 -0
- package/dist/src/types/living-docs-us-file.d.ts.map +1 -0
- package/dist/src/types/living-docs-us-file.js +27 -0
- package/dist/src/types/living-docs-us-file.js.map +1 -0
- package/dist/src/validators/format-preservation-validator.d.ts +127 -0
- package/dist/src/validators/format-preservation-validator.d.ts.map +1 -0
- package/dist/src/validators/format-preservation-validator.js +187 -0
- package/dist/src/validators/format-preservation-validator.js.map +1 -0
- package/package.json +3 -2
- package/plugins/specweave/.claude-plugin/plugin.json +20 -0
- package/plugins/specweave/commands/specweave-archive-features.md +11 -1
- package/plugins/specweave/commands/specweave-archive.md +51 -15
- package/plugins/specweave/commands/specweave-import-docs.md +88 -278
- package/plugins/specweave/commands/specweave-import-external.md +407 -0
- package/plugins/specweave/hooks/post-edit-spec.sh +94 -0
- package/plugins/specweave/hooks/post-increment-completion.sh +0 -0
- package/plugins/specweave/hooks/post-spec-update.sh +0 -0
- package/plugins/specweave/hooks/post-task-completion.sh +13 -3
- package/plugins/specweave/hooks/post-write-spec.sh +91 -0
- package/plugins/specweave/lib/hooks/auto-transition.js +1 -1
- package/plugins/specweave/lib/hooks/auto-transition.js.bak +50 -0
- package/plugins/specweave/lib/hooks/auto-transition.ts +1 -1
- package/plugins/specweave/lib/hooks/auto-transition.ts.bak +84 -0
- package/plugins/specweave/lib/hooks/git-diff-analyzer.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/git-diff-analyzer.d.ts.bak +89 -0
- package/plugins/specweave/lib/hooks/git-diff-analyzer.js.bak +142 -0
- package/plugins/specweave/lib/hooks/git-diff-analyzer.ts.bak +269 -0
- package/plugins/specweave/lib/hooks/invoke-translator-skill.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/invoke-translator-skill.d.ts.bak +60 -0
- package/plugins/specweave/lib/hooks/invoke-translator-skill.js +1 -1
- package/plugins/specweave/lib/hooks/invoke-translator-skill.js.bak +155 -0
- package/plugins/specweave/lib/hooks/invoke-translator-skill.ts +1 -1
- package/plugins/specweave/lib/hooks/invoke-translator-skill.ts.bak +264 -0
- package/plugins/specweave/lib/hooks/prepare-reflection-context.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/prepare-reflection-context.d.ts.bak +42 -0
- package/plugins/specweave/lib/hooks/prepare-reflection-context.js.bak +110 -0
- package/plugins/specweave/lib/hooks/prepare-reflection-context.ts.bak +178 -0
- package/plugins/specweave/lib/hooks/reflection-config-loader.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/reflection-config-loader.d.ts.bak +45 -0
- package/plugins/specweave/lib/hooks/reflection-config-loader.js.bak +92 -0
- package/plugins/specweave/lib/hooks/reflection-config-loader.ts.bak +156 -0
- package/plugins/specweave/lib/hooks/reflection-parser.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/reflection-parser.d.ts.bak +33 -0
- package/plugins/specweave/lib/hooks/reflection-parser.js.bak +301 -0
- package/plugins/specweave/lib/hooks/reflection-parser.ts.bak +484 -0
- package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.ts.bak +56 -0
- package/plugins/specweave/lib/hooks/reflection-prompt-builder.js.bak +182 -0
- package/plugins/specweave/lib/hooks/reflection-prompt-builder.ts.bak +306 -0
- package/plugins/specweave/lib/hooks/reflection-storage.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/reflection-storage.d.ts.bak +64 -0
- package/plugins/specweave/lib/hooks/reflection-storage.js.bak +231 -0
- package/plugins/specweave/lib/hooks/reflection-storage.ts.bak +369 -0
- package/plugins/specweave/lib/hooks/run-self-reflection.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/run-self-reflection.d.ts.bak +43 -0
- package/plugins/specweave/lib/hooks/run-self-reflection.js.bak +132 -0
- package/plugins/specweave/lib/hooks/run-self-reflection.ts.bak +258 -0
- package/plugins/specweave/lib/hooks/sync-cache.js.bak +294 -0
- package/plugins/specweave/lib/hooks/sync-living-docs.d.js.bak +1 -0
- package/plugins/specweave/lib/hooks/sync-living-docs.d.ts.bak +27 -0
- package/plugins/specweave/lib/hooks/sync-living-docs.js +35 -1
- package/plugins/specweave/lib/hooks/sync-living-docs.js.bak +339 -0
- package/plugins/specweave/lib/hooks/sync-us-tasks.js +179 -3
- package/plugins/specweave/lib/hooks/sync-us-tasks.js.bak +476 -0
- package/plugins/specweave/lib/hooks/translate-file.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/translate-file.d.ts.bak +59 -0
- package/plugins/specweave/lib/hooks/translate-file.js +1 -1
- package/plugins/specweave/lib/hooks/translate-file.js.bak +289 -0
- package/plugins/specweave/lib/hooks/translate-file.ts +1 -1
- package/plugins/specweave/lib/hooks/translate-file.ts.bak +428 -0
- package/plugins/specweave/lib/hooks/translate-living-docs.d.js.bak +0 -0
- package/plugins/specweave/lib/hooks/translate-living-docs.d.ts.bak +13 -0
- package/plugins/specweave/lib/hooks/translate-living-docs.js.bak +119 -0
- package/plugins/specweave/lib/hooks/translate-living-docs.ts.bak +224 -0
- package/plugins/specweave/lib/hooks/update-ac-status.js +1 -1
- package/plugins/specweave/lib/hooks/update-ac-status.js.bak +51 -0
- package/plugins/specweave/lib/hooks/update-ac-status.ts +1 -1
- package/plugins/specweave/lib/hooks/update-ac-status.ts.bak +103 -0
- package/plugins/specweave/lib/hooks/update-tasks-md.d.js.bak +1 -0
- package/plugins/specweave/lib/hooks/update-tasks-md.d.ts.bak +29 -0
- package/plugins/specweave/lib/hooks/update-tasks-md.js.bak +296 -0
- package/plugins/specweave/lib/hooks/update-tasks-md.ts.bak +489 -0
- package/plugins/specweave/lib/vendor/core/increment/ac-status-manager.d.ts +115 -0
- package/plugins/specweave/lib/vendor/core/increment/ac-status-manager.js +345 -0
- package/plugins/specweave/lib/vendor/core/increment/ac-status-manager.js.map +1 -0
- package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.d.ts +106 -0
- package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js +220 -0
- package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js.map +1 -0
- package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.d.ts +60 -0
- package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js +192 -0
- package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js.map +1 -0
- package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.d.ts +52 -0
- package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js +276 -0
- package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js.map +1 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +163 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +541 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -0
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +157 -0
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.js +191 -0
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.js.map +1 -0
- package/plugins/specweave/lib/vendor/generators/spec/task-parser.d.ts +95 -0
- package/plugins/specweave/lib/vendor/generators/spec/task-parser.js +301 -0
- package/plugins/specweave/lib/vendor/generators/spec/task-parser.js.map +1 -0
- package/plugins/specweave/lib/vendor/utils/logger.d.ts +48 -0
- package/plugins/specweave/lib/vendor/utils/logger.js +53 -0
- package/plugins/specweave/lib/vendor/utils/logger.js.map +1 -0
- package/plugins/specweave/lib/vendor/utils/translation.d.ts +187 -0
- package/plugins/specweave/lib/vendor/utils/translation.js +414 -0
- package/plugins/specweave/lib/vendor/utils/translation.js.map +1 -0
- package/plugins/specweave-github/commands/specweave-github-update-user-story.md +1 -1
- package/plugins/specweave-github/skills/github-issue-standard/SKILL.md +1 -1
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +531 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* ACStatusManager: Automatically sync spec.md AC checkboxes with tasks.md completion status
|
|
5
|
+
*/
|
|
6
|
+
export class ACStatusManager {
|
|
7
|
+
constructor(rootPath) {
|
|
8
|
+
this.rootPath = rootPath;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parse tasks.md and extract AC completion status
|
|
12
|
+
*
|
|
13
|
+
* @param tasksContent - Content of tasks.md file
|
|
14
|
+
* @returns Map of AC-ID to ACCompletionStatus
|
|
15
|
+
*/
|
|
16
|
+
parseTasksForACStatus(tasksContent) {
|
|
17
|
+
const acMap = new Map();
|
|
18
|
+
// Regex to match task headers: ### T-###: Task name
|
|
19
|
+
const taskHeaderRegex = /###\s+(T-\d+):/g;
|
|
20
|
+
// Split content by task headers
|
|
21
|
+
const lines = tasksContent.split('\n');
|
|
22
|
+
let currentTaskId = null;
|
|
23
|
+
let currentTaskACs = [];
|
|
24
|
+
let hasCheckedBoxes = false;
|
|
25
|
+
let hasUncheckedBoxes = false;
|
|
26
|
+
for (let i = 0; i < lines.length; i++) {
|
|
27
|
+
const line = lines[i];
|
|
28
|
+
// Check if this is a task header
|
|
29
|
+
const taskMatch = line.match(/###\s+(T-\d+):/);
|
|
30
|
+
if (taskMatch) {
|
|
31
|
+
// Process previous task if exists
|
|
32
|
+
if (currentTaskId && currentTaskACs.length > 0) {
|
|
33
|
+
// Task is complete if it has at least one [x] and no [ ]
|
|
34
|
+
const currentTaskComplete = hasCheckedBoxes && !hasUncheckedBoxes;
|
|
35
|
+
this.addTaskToACMap(acMap, currentTaskId, currentTaskACs, currentTaskComplete);
|
|
36
|
+
}
|
|
37
|
+
// Start new task
|
|
38
|
+
currentTaskId = taskMatch[1];
|
|
39
|
+
currentTaskACs = [];
|
|
40
|
+
hasCheckedBoxes = false;
|
|
41
|
+
hasUncheckedBoxes = false;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
// Check if this line contains AC tags (supports both old and new formats)
|
|
45
|
+
// Old format: **AC**: AC-US1-01
|
|
46
|
+
// New format: **Satisfies ACs**: AC-US1-01, AC-US1-02
|
|
47
|
+
const acMatch = line.match(/\*\*(?:AC|Satisfies ACs)\*\*:\s*(.+)/);
|
|
48
|
+
if (acMatch && currentTaskId) {
|
|
49
|
+
// Extract AC-IDs (can be comma or space separated)
|
|
50
|
+
const acText = acMatch[1].trim();
|
|
51
|
+
const acIds = acText
|
|
52
|
+
.split(/[,\s]+/)
|
|
53
|
+
.map(id => id.trim())
|
|
54
|
+
.filter(id => id.startsWith('AC-'));
|
|
55
|
+
currentTaskACs.push(...acIds);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// Check task completion status
|
|
59
|
+
if (currentTaskId && line.includes('- [')) {
|
|
60
|
+
if (line.includes('- [ ]')) {
|
|
61
|
+
hasUncheckedBoxes = true;
|
|
62
|
+
}
|
|
63
|
+
else if (line.includes('- [x]')) {
|
|
64
|
+
hasCheckedBoxes = true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Process last task
|
|
69
|
+
if (currentTaskId && currentTaskACs.length > 0) {
|
|
70
|
+
const currentTaskComplete = hasCheckedBoxes && !hasUncheckedBoxes;
|
|
71
|
+
this.addTaskToACMap(acMap, currentTaskId, currentTaskACs, currentTaskComplete);
|
|
72
|
+
}
|
|
73
|
+
return acMap;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Helper method to add a task to the AC map
|
|
77
|
+
*/
|
|
78
|
+
addTaskToACMap(acMap, taskId, acIds, isComplete) {
|
|
79
|
+
for (const acId of acIds) {
|
|
80
|
+
if (!acMap.has(acId)) {
|
|
81
|
+
acMap.set(acId, {
|
|
82
|
+
acId,
|
|
83
|
+
totalTasks: 0,
|
|
84
|
+
completedTasks: 0,
|
|
85
|
+
percentage: 0,
|
|
86
|
+
isComplete: false,
|
|
87
|
+
tasks: []
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const status = acMap.get(acId);
|
|
91
|
+
status.totalTasks++;
|
|
92
|
+
if (isComplete) {
|
|
93
|
+
status.completedTasks++;
|
|
94
|
+
}
|
|
95
|
+
status.tasks.push(taskId);
|
|
96
|
+
// Calculate percentage
|
|
97
|
+
status.percentage = status.totalTasks > 0
|
|
98
|
+
? Math.round((status.completedTasks / status.totalTasks) * 100 * 100) / 100
|
|
99
|
+
: 0;
|
|
100
|
+
// Update isComplete flag
|
|
101
|
+
status.isComplete = status.percentage === 100;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Parse spec.md and extract AC checkboxes
|
|
106
|
+
*
|
|
107
|
+
* @param specContent - Content of spec.md file
|
|
108
|
+
* @returns Map of AC-ID to ACDefinition
|
|
109
|
+
*/
|
|
110
|
+
parseSpecForACs(specContent) {
|
|
111
|
+
const acMap = new Map();
|
|
112
|
+
const lines = specContent.split('\n');
|
|
113
|
+
for (let i = 0; i < lines.length; i++) {
|
|
114
|
+
const line = lines[i];
|
|
115
|
+
// Match AC checkbox pattern: - [ ] AC-ID: Description
|
|
116
|
+
// or: - [x] AC-ID: Description
|
|
117
|
+
// Also handles bold format: - [ ] **AC-ID**: Description
|
|
118
|
+
const acMatch = line.match(/^-\s+\[([ x])\]\s+\*{0,2}(AC-[A-Z0-9-]+)\*{0,2}:\s*(.+)/);
|
|
119
|
+
if (acMatch) {
|
|
120
|
+
const checked = acMatch[1] === 'x';
|
|
121
|
+
const acId = acMatch[2];
|
|
122
|
+
const description = acMatch[3].trim();
|
|
123
|
+
acMap.set(acId, {
|
|
124
|
+
acId,
|
|
125
|
+
description,
|
|
126
|
+
checked,
|
|
127
|
+
lineNumber: i + 1, // 1-indexed
|
|
128
|
+
fullLine: line
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return acMap;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Sync AC status from tasks.md to spec.md
|
|
136
|
+
*
|
|
137
|
+
* @param incrementId - Increment ID (e.g., "0039")
|
|
138
|
+
* @returns Result of sync operation
|
|
139
|
+
*/
|
|
140
|
+
async syncACStatus(incrementId) {
|
|
141
|
+
const result = {
|
|
142
|
+
synced: false,
|
|
143
|
+
updated: [],
|
|
144
|
+
conflicts: [],
|
|
145
|
+
warnings: [],
|
|
146
|
+
changes: []
|
|
147
|
+
};
|
|
148
|
+
// Paths
|
|
149
|
+
const incrementPath = path.join(this.rootPath, '.specweave', 'increments', incrementId);
|
|
150
|
+
const specPath = path.join(incrementPath, 'spec.md');
|
|
151
|
+
const tasksPath = path.join(incrementPath, 'tasks.md');
|
|
152
|
+
// Check files exist
|
|
153
|
+
if (!fs.existsSync(specPath)) {
|
|
154
|
+
result.warnings.push('spec.md does not exist');
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
if (!fs.existsSync(tasksPath)) {
|
|
158
|
+
result.warnings.push('tasks.md does not exist');
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
// Read files
|
|
162
|
+
const specContent = fs.readFileSync(specPath, 'utf-8');
|
|
163
|
+
const tasksContent = fs.readFileSync(tasksPath, 'utf-8');
|
|
164
|
+
// Parse both files
|
|
165
|
+
const taskACStatuses = this.parseTasksForACStatus(tasksContent);
|
|
166
|
+
const specACs = this.parseSpecForACs(specContent);
|
|
167
|
+
// Sync logic
|
|
168
|
+
const specLines = specContent.split('\n');
|
|
169
|
+
let updated = false;
|
|
170
|
+
for (const [acId, taskStatus] of taskACStatuses.entries()) {
|
|
171
|
+
const specAC = specACs.get(acId);
|
|
172
|
+
if (!specAC) {
|
|
173
|
+
result.warnings.push(`${acId} referenced in tasks.md but not found in spec.md`);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
// Check if status should change
|
|
177
|
+
const shouldBeChecked = taskStatus.isComplete;
|
|
178
|
+
const currentlyChecked = specAC.checked;
|
|
179
|
+
if (shouldBeChecked && !currentlyChecked) {
|
|
180
|
+
// Update from [ ] to [x]
|
|
181
|
+
const oldLine = specLines[specAC.lineNumber - 1];
|
|
182
|
+
const newLine = oldLine.replace('- [ ]', '- [x]');
|
|
183
|
+
specLines[specAC.lineNumber - 1] = newLine;
|
|
184
|
+
result.updated.push(acId);
|
|
185
|
+
result.changes.push(`${acId}: [ ] → [x] (${taskStatus.completedTasks}/${taskStatus.totalTasks} tasks complete)`);
|
|
186
|
+
updated = true;
|
|
187
|
+
}
|
|
188
|
+
else if (!shouldBeChecked && currentlyChecked) {
|
|
189
|
+
// Conflict: AC is [x] but tasks incomplete
|
|
190
|
+
result.conflicts.push(`${acId}: [x] but only ${taskStatus.completedTasks}/${taskStatus.totalTasks} tasks complete (${taskStatus.percentage}%)`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Check for ACs with no tasks
|
|
194
|
+
for (const [acId, specAC] of specACs.entries()) {
|
|
195
|
+
if (!taskACStatuses.has(acId)) {
|
|
196
|
+
if (specAC.checked) {
|
|
197
|
+
result.warnings.push(`${acId}: [x] but no tasks found (manual verification?)`);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
result.warnings.push(`${acId}: has no tasks mapped`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Write updated spec.md
|
|
205
|
+
if (updated) {
|
|
206
|
+
const newSpecContent = specLines.join('\n');
|
|
207
|
+
fs.writeFileSync(specPath, newSpecContent, 'utf-8');
|
|
208
|
+
result.synced = true;
|
|
209
|
+
}
|
|
210
|
+
// Log sync event to metadata.json
|
|
211
|
+
if (result.synced || result.conflicts.length > 0 || result.warnings.length > 0) {
|
|
212
|
+
this.logSyncEvent(incrementId, result);
|
|
213
|
+
}
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Log AC sync event to metadata.json
|
|
218
|
+
* Keeps last 20 events for audit trail
|
|
219
|
+
*
|
|
220
|
+
* @param incrementId - Increment ID
|
|
221
|
+
* @param result - Sync result to log
|
|
222
|
+
*/
|
|
223
|
+
logSyncEvent(incrementId, result) {
|
|
224
|
+
const metadataPath = path.join(this.rootPath, '.specweave', 'increments', incrementId, 'metadata.json');
|
|
225
|
+
try {
|
|
226
|
+
// Read existing metadata
|
|
227
|
+
let metadata = {};
|
|
228
|
+
if (fs.existsSync(metadataPath)) {
|
|
229
|
+
const metadataContent = fs.readFileSync(metadataPath, 'utf-8');
|
|
230
|
+
metadata = JSON.parse(metadataContent);
|
|
231
|
+
}
|
|
232
|
+
// Initialize acSyncEvents array if doesn't exist
|
|
233
|
+
if (!metadata.acSyncEvents) {
|
|
234
|
+
metadata.acSyncEvents = [];
|
|
235
|
+
}
|
|
236
|
+
// Create new event
|
|
237
|
+
const event = {
|
|
238
|
+
timestamp: new Date().toISOString(),
|
|
239
|
+
updated: result.updated,
|
|
240
|
+
conflicts: result.conflicts,
|
|
241
|
+
warnings: result.warnings,
|
|
242
|
+
changesCount: result.updated.length
|
|
243
|
+
};
|
|
244
|
+
// Add event to beginning (newest first)
|
|
245
|
+
metadata.acSyncEvents.unshift(event);
|
|
246
|
+
// Keep only last 20 events (rolling history)
|
|
247
|
+
if (metadata.acSyncEvents.length > 20) {
|
|
248
|
+
metadata.acSyncEvents = metadata.acSyncEvents.slice(0, 20);
|
|
249
|
+
}
|
|
250
|
+
// Update lastActivity timestamp
|
|
251
|
+
metadata.lastActivity = new Date().toISOString();
|
|
252
|
+
// Write updated metadata
|
|
253
|
+
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), 'utf-8');
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
// Non-blocking: Log error but don't fail sync
|
|
257
|
+
console.error('Warning: Failed to log AC sync event to metadata.json', error);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Validate AC-task mapping
|
|
262
|
+
*
|
|
263
|
+
* @param incrementId - Increment ID
|
|
264
|
+
* @returns Validation result
|
|
265
|
+
*/
|
|
266
|
+
validateACMapping(incrementId) {
|
|
267
|
+
const result = {
|
|
268
|
+
valid: true,
|
|
269
|
+
orphanedACs: [],
|
|
270
|
+
invalidReferences: [],
|
|
271
|
+
errors: []
|
|
272
|
+
};
|
|
273
|
+
const incrementPath = path.join(this.rootPath, '.specweave', 'increments', incrementId);
|
|
274
|
+
const specPath = path.join(incrementPath, 'spec.md');
|
|
275
|
+
const tasksPath = path.join(incrementPath, 'tasks.md');
|
|
276
|
+
// Check files exist
|
|
277
|
+
if (!fs.existsSync(specPath)) {
|
|
278
|
+
result.errors.push('spec.md does not exist');
|
|
279
|
+
result.valid = false;
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
if (!fs.existsSync(tasksPath)) {
|
|
283
|
+
result.errors.push('tasks.md does not exist');
|
|
284
|
+
result.valid = false;
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
// Read and parse
|
|
288
|
+
const specContent = fs.readFileSync(specPath, 'utf-8');
|
|
289
|
+
const tasksContent = fs.readFileSync(tasksPath, 'utf-8');
|
|
290
|
+
const taskACStatuses = this.parseTasksForACStatus(tasksContent);
|
|
291
|
+
const specACs = this.parseSpecForACs(specContent);
|
|
292
|
+
// Find orphaned ACs (in spec but no tasks)
|
|
293
|
+
for (const acId of specACs.keys()) {
|
|
294
|
+
if (!taskACStatuses.has(acId)) {
|
|
295
|
+
result.orphanedACs.push(acId);
|
|
296
|
+
result.valid = false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Find invalid references (in tasks but not in spec)
|
|
300
|
+
for (const acId of taskACStatuses.keys()) {
|
|
301
|
+
if (!specACs.has(acId)) {
|
|
302
|
+
result.invalidReferences.push(acId);
|
|
303
|
+
result.valid = false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return result;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Get AC completion summary
|
|
310
|
+
*
|
|
311
|
+
* @param incrementId - Increment ID
|
|
312
|
+
* @returns Summary of AC completion status
|
|
313
|
+
*/
|
|
314
|
+
getACCompletionSummary(incrementId) {
|
|
315
|
+
const incrementPath = path.join(this.rootPath, '.specweave', 'increments', incrementId);
|
|
316
|
+
const tasksPath = path.join(incrementPath, 'tasks.md');
|
|
317
|
+
const summary = {
|
|
318
|
+
totalACs: 0,
|
|
319
|
+
completeACs: 0,
|
|
320
|
+
incompleteACs: 0,
|
|
321
|
+
percentage: 0,
|
|
322
|
+
acStatuses: new Map()
|
|
323
|
+
};
|
|
324
|
+
if (!fs.existsSync(tasksPath)) {
|
|
325
|
+
return summary;
|
|
326
|
+
}
|
|
327
|
+
const tasksContent = fs.readFileSync(tasksPath, 'utf-8');
|
|
328
|
+
const acStatuses = this.parseTasksForACStatus(tasksContent);
|
|
329
|
+
summary.acStatuses = acStatuses;
|
|
330
|
+
summary.totalACs = acStatuses.size;
|
|
331
|
+
for (const status of acStatuses.values()) {
|
|
332
|
+
if (status.isComplete) {
|
|
333
|
+
summary.completeACs++;
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
summary.incompleteACs++;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
summary.percentage = summary.totalACs > 0
|
|
340
|
+
? Math.round((summary.completeACs / summary.totalACs) * 100)
|
|
341
|
+
: 0;
|
|
342
|
+
return summary;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
//# sourceMappingURL=ac-status-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ac-status-manager.js","sourceRoot":"","sources":["../../../../src/core/increment/ac-status-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Active Increment Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages the `.specweave/state/active-increment.json` file which tracks
|
|
5
|
+
* the currently active increment for status line display.
|
|
6
|
+
*
|
|
7
|
+
* **Critical**: This file MUST be updated whenever increment status changes:
|
|
8
|
+
* - When new increment is created → Set as active
|
|
9
|
+
* - When increment is completed → Clear or set to next active
|
|
10
|
+
* - When increment is paused → Set to next active (or clear if none)
|
|
11
|
+
* - When increment is resumed → Set as active
|
|
12
|
+
*
|
|
13
|
+
* Part of increment 0021: Fix Active Increment Tracking
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Active increment state stored in .specweave/state/active-increment.json
|
|
17
|
+
*
|
|
18
|
+
* **UPGRADED**: Now supports MULTIPLE active increments (up to 2)
|
|
19
|
+
* - One regular feature increment
|
|
20
|
+
* - One hotfix/bug increment (optional)
|
|
21
|
+
*/
|
|
22
|
+
export interface ActiveIncrementState {
|
|
23
|
+
ids: string[];
|
|
24
|
+
id?: string | null;
|
|
25
|
+
lastUpdated: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Active Increment Manager
|
|
29
|
+
*
|
|
30
|
+
* Central authority for managing which increment is currently active.
|
|
31
|
+
*/
|
|
32
|
+
export declare class ActiveIncrementManager {
|
|
33
|
+
private rootDir;
|
|
34
|
+
private stateFile;
|
|
35
|
+
constructor(rootDir?: string);
|
|
36
|
+
/**
|
|
37
|
+
* Get all currently active increment IDs
|
|
38
|
+
* Returns empty array if no increments are active
|
|
39
|
+
*
|
|
40
|
+
* **NEW**: Returns array of ALL active increments (max 2)
|
|
41
|
+
*/
|
|
42
|
+
getActive(): string[];
|
|
43
|
+
/**
|
|
44
|
+
* Get the primary active increment (first in list)
|
|
45
|
+
* Returns null if no increments are active
|
|
46
|
+
*
|
|
47
|
+
* This maintains backwards compatibility with code expecting a single ID
|
|
48
|
+
*/
|
|
49
|
+
getPrimary(): string | null;
|
|
50
|
+
/**
|
|
51
|
+
* Add an increment to the active list
|
|
52
|
+
* Validates that the increment exists and is actually active
|
|
53
|
+
*
|
|
54
|
+
* **NEW**: Adds to list instead of replacing (max 2)
|
|
55
|
+
* @param skipValidation - Skip validation (used during lazy initialization to prevent circular dependency)
|
|
56
|
+
*/
|
|
57
|
+
addActive(incrementId: string, skipValidation?: boolean): void;
|
|
58
|
+
/**
|
|
59
|
+
* Remove an increment from the active list
|
|
60
|
+
*/
|
|
61
|
+
removeActive(incrementId: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* Set the active increment (legacy method for backwards compatibility)
|
|
64
|
+
* Now delegates to addActive()
|
|
65
|
+
* @param skipValidation - Skip validation (used during lazy initialization to prevent circular dependency)
|
|
66
|
+
*/
|
|
67
|
+
setActive(incrementId: string, skipValidation?: boolean): void;
|
|
68
|
+
/**
|
|
69
|
+
* Clear all active increments
|
|
70
|
+
*/
|
|
71
|
+
clearActive(): void;
|
|
72
|
+
/**
|
|
73
|
+
* Smart update: Rebuild active list from metadata
|
|
74
|
+
*
|
|
75
|
+
* This is called when:
|
|
76
|
+
* - An increment is completed
|
|
77
|
+
* - An increment is paused
|
|
78
|
+
* - An increment is abandoned
|
|
79
|
+
*
|
|
80
|
+
* Logic:
|
|
81
|
+
* 1. Scan all increments for status=active
|
|
82
|
+
* 2. Update cache to match reality
|
|
83
|
+
* 3. Max 2 increments (sorted by lastActivity)
|
|
84
|
+
*/
|
|
85
|
+
smartUpdate(): void;
|
|
86
|
+
/**
|
|
87
|
+
* Validate that all active increment pointers are correct
|
|
88
|
+
* Does NOT fix stale pointers - caller should call smartUpdate() if needed
|
|
89
|
+
*
|
|
90
|
+
* Returns true if all valid, false if any invalid
|
|
91
|
+
*/
|
|
92
|
+
validate(): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Write state file atomically (temp file → rename)
|
|
95
|
+
*/
|
|
96
|
+
private writeState;
|
|
97
|
+
/**
|
|
98
|
+
* Get state file path (for testing)
|
|
99
|
+
*/
|
|
100
|
+
getStateFilePath(): string;
|
|
101
|
+
/**
|
|
102
|
+
* Check if state file exists (for testing)
|
|
103
|
+
*/
|
|
104
|
+
exists(): boolean;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=active-increment-manager.d.ts.map
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Active Increment Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages the `.specweave/state/active-increment.json` file which tracks
|
|
5
|
+
* the currently active increment for status line display.
|
|
6
|
+
*
|
|
7
|
+
* **Critical**: This file MUST be updated whenever increment status changes:
|
|
8
|
+
* - When new increment is created → Set as active
|
|
9
|
+
* - When increment is completed → Clear or set to next active
|
|
10
|
+
* - When increment is paused → Set to next active (or clear if none)
|
|
11
|
+
* - When increment is resumed → Set as active
|
|
12
|
+
*
|
|
13
|
+
* Part of increment 0021: Fix Active Increment Tracking
|
|
14
|
+
*/
|
|
15
|
+
import fs from 'fs-extra';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import { IncrementStatus } from '../types/increment-metadata.js';
|
|
18
|
+
import { MetadataManager } from './metadata-manager.js';
|
|
19
|
+
/**
|
|
20
|
+
* Active Increment Manager
|
|
21
|
+
*
|
|
22
|
+
* Central authority for managing which increment is currently active.
|
|
23
|
+
*/
|
|
24
|
+
export class ActiveIncrementManager {
|
|
25
|
+
constructor(rootDir = process.cwd()) {
|
|
26
|
+
this.rootDir = rootDir;
|
|
27
|
+
this.stateFile = path.join(rootDir, '.specweave/state/active-increment.json');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get all currently active increment IDs
|
|
31
|
+
* Returns empty array if no increments are active
|
|
32
|
+
*
|
|
33
|
+
* **NEW**: Returns array of ALL active increments (max 2)
|
|
34
|
+
*/
|
|
35
|
+
getActive() {
|
|
36
|
+
try {
|
|
37
|
+
if (!fs.existsSync(this.stateFile)) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
const content = fs.readFileSync(this.stateFile, 'utf-8');
|
|
41
|
+
const state = JSON.parse(content);
|
|
42
|
+
// Backwards compatibility: Support old format
|
|
43
|
+
if (state.id && !state.ids) {
|
|
44
|
+
return [state.id];
|
|
45
|
+
}
|
|
46
|
+
return state.ids || [];
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
// File read/parse error = no active increments
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get the primary active increment (first in list)
|
|
55
|
+
* Returns null if no increments are active
|
|
56
|
+
*
|
|
57
|
+
* This maintains backwards compatibility with code expecting a single ID
|
|
58
|
+
*/
|
|
59
|
+
getPrimary() {
|
|
60
|
+
const active = this.getActive();
|
|
61
|
+
return active.length > 0 ? active[0] : null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Add an increment to the active list
|
|
65
|
+
* Validates that the increment exists and is actually active
|
|
66
|
+
*
|
|
67
|
+
* **NEW**: Adds to list instead of replacing (max 2)
|
|
68
|
+
* @param skipValidation - Skip validation (used during lazy initialization to prevent circular dependency)
|
|
69
|
+
*/
|
|
70
|
+
addActive(incrementId, skipValidation = false) {
|
|
71
|
+
// Validate increment exists and is active (unless skipValidation is true)
|
|
72
|
+
if (!skipValidation) {
|
|
73
|
+
const metadata = MetadataManager.read(incrementId);
|
|
74
|
+
// Validate increment is actually active
|
|
75
|
+
if (metadata.status !== IncrementStatus.ACTIVE) {
|
|
76
|
+
throw new Error(`Cannot add ${incrementId} as active: status is ${metadata.status}, not active`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Get current active list
|
|
80
|
+
const currentActive = this.getActive();
|
|
81
|
+
// Don't add if already in list
|
|
82
|
+
if (currentActive.includes(incrementId)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Add to list (max 2)
|
|
86
|
+
const newActive = [...currentActive, incrementId].slice(0, 2);
|
|
87
|
+
// Write state
|
|
88
|
+
const state = {
|
|
89
|
+
ids: newActive,
|
|
90
|
+
lastUpdated: new Date().toISOString()
|
|
91
|
+
};
|
|
92
|
+
this.writeState(state);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Remove an increment from the active list
|
|
96
|
+
*/
|
|
97
|
+
removeActive(incrementId) {
|
|
98
|
+
const currentActive = this.getActive();
|
|
99
|
+
const newActive = currentActive.filter(id => id !== incrementId);
|
|
100
|
+
const state = {
|
|
101
|
+
ids: newActive,
|
|
102
|
+
lastUpdated: new Date().toISOString()
|
|
103
|
+
};
|
|
104
|
+
this.writeState(state);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Set the active increment (legacy method for backwards compatibility)
|
|
108
|
+
* Now delegates to addActive()
|
|
109
|
+
* @param skipValidation - Skip validation (used during lazy initialization to prevent circular dependency)
|
|
110
|
+
*/
|
|
111
|
+
setActive(incrementId, skipValidation = false) {
|
|
112
|
+
this.addActive(incrementId, skipValidation);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Clear all active increments
|
|
116
|
+
*/
|
|
117
|
+
clearActive() {
|
|
118
|
+
const state = {
|
|
119
|
+
ids: [],
|
|
120
|
+
lastUpdated: new Date().toISOString()
|
|
121
|
+
};
|
|
122
|
+
this.writeState(state);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Smart update: Rebuild active list from metadata
|
|
126
|
+
*
|
|
127
|
+
* This is called when:
|
|
128
|
+
* - An increment is completed
|
|
129
|
+
* - An increment is paused
|
|
130
|
+
* - An increment is abandoned
|
|
131
|
+
*
|
|
132
|
+
* Logic:
|
|
133
|
+
* 1. Scan all increments for status=active
|
|
134
|
+
* 2. Update cache to match reality
|
|
135
|
+
* 3. Max 2 increments (sorted by lastActivity)
|
|
136
|
+
*/
|
|
137
|
+
smartUpdate() {
|
|
138
|
+
const activeIncrements = MetadataManager.getActive();
|
|
139
|
+
if (activeIncrements.length > 0) {
|
|
140
|
+
// Sort by lastActivity (most recent first)
|
|
141
|
+
const sorted = activeIncrements.sort((a, b) => {
|
|
142
|
+
const aTime = new Date(a.lastActivity).getTime();
|
|
143
|
+
const bTime = new Date(b.lastActivity).getTime();
|
|
144
|
+
return bTime - aTime; // Descending
|
|
145
|
+
});
|
|
146
|
+
// Take max 2
|
|
147
|
+
const activeIds = sorted.slice(0, 2).map(m => m.id);
|
|
148
|
+
const state = {
|
|
149
|
+
ids: activeIds,
|
|
150
|
+
lastUpdated: new Date().toISOString()
|
|
151
|
+
};
|
|
152
|
+
this.writeState(state);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// No active increments
|
|
156
|
+
this.clearActive();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Validate that all active increment pointers are correct
|
|
161
|
+
* Does NOT fix stale pointers - caller should call smartUpdate() if needed
|
|
162
|
+
*
|
|
163
|
+
* Returns true if all valid, false if any invalid
|
|
164
|
+
*/
|
|
165
|
+
validate() {
|
|
166
|
+
const currentActive = this.getActive();
|
|
167
|
+
// No active increments = valid (nothing to validate)
|
|
168
|
+
if (currentActive.length === 0) {
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
let hasStale = false;
|
|
172
|
+
for (const incrementId of currentActive) {
|
|
173
|
+
try {
|
|
174
|
+
// Check if increment still exists
|
|
175
|
+
const metadata = MetadataManager.read(incrementId);
|
|
176
|
+
// Check if increment is actually active
|
|
177
|
+
if (metadata.status !== IncrementStatus.ACTIVE) {
|
|
178
|
+
// Stale pointer! Mark for fix
|
|
179
|
+
console.warn(`⚠️ Active increment pointer is stale: ${incrementId} is ${metadata.status}`);
|
|
180
|
+
hasStale = true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
// Increment doesn't exist = stale pointer
|
|
185
|
+
console.warn(`⚠️ Active increment pointer is invalid: ${incrementId} not found`);
|
|
186
|
+
hasStale = true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Return false if stale found, but DON'T auto-fix (prevents circular dependency)
|
|
190
|
+
// Caller should call smartUpdate() if needed
|
|
191
|
+
return !hasStale;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Write state file atomically (temp file → rename)
|
|
195
|
+
*/
|
|
196
|
+
writeState(state) {
|
|
197
|
+
// Ensure state directory exists
|
|
198
|
+
const stateDir = path.dirname(this.stateFile);
|
|
199
|
+
if (!fs.existsSync(stateDir)) {
|
|
200
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
201
|
+
}
|
|
202
|
+
// Atomic write: temp file → rename
|
|
203
|
+
const tempFile = `${this.stateFile}.tmp`;
|
|
204
|
+
fs.writeFileSync(tempFile, JSON.stringify(state, null, 2), 'utf-8');
|
|
205
|
+
fs.renameSync(tempFile, this.stateFile);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Get state file path (for testing)
|
|
209
|
+
*/
|
|
210
|
+
getStateFilePath() {
|
|
211
|
+
return this.stateFile;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Check if state file exists (for testing)
|
|
215
|
+
*/
|
|
216
|
+
exists() {
|
|
217
|
+
return fs.existsSync(this.stateFile);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=active-increment-manager.js.map
|