speccrew 0.6.68 → 0.7.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/.speccrew/agents/speccrew-task-worker.md +1 -1
- package/.speccrew/agents/speccrew-team-leader.md +336 -189
- package/.speccrew/skills/speccrew-agentflow-manager/SKILL.md +161 -0
- package/.speccrew/skills/speccrew-agentflow-manager/workflow.agentflow.xml +347 -0
- package/.speccrew/skills/speccrew-deploy-build/SKILL.md +3 -56
- package/.speccrew/skills/speccrew-deploy-build/workflow.agentflow.xml +125 -0
- package/.speccrew/skills/speccrew-deploy-migrate/SKILL.md +3 -64
- package/.speccrew/skills/speccrew-deploy-migrate/workflow.agentflow.xml +135 -0
- package/.speccrew/skills/speccrew-deploy-smoke-test/SKILL.md +4 -156
- package/.speccrew/skills/speccrew-deploy-smoke-test/workflow.agentflow.xml +178 -0
- package/.speccrew/skills/speccrew-deploy-startup/SKILL.md +3 -135
- package/.speccrew/skills/speccrew-deploy-startup/workflow.agentflow.xml +223 -0
- package/.speccrew/skills/speccrew-dev-backend/SKILL.md +10 -2
- package/.speccrew/skills/speccrew-dev-backend/workflow.agentflow.xml +254 -0
- package/.speccrew/skills/speccrew-dev-desktop-electron/SKILL.md +10 -2
- package/.speccrew/skills/speccrew-dev-desktop-electron/workflow.agentflow.xml +259 -0
- package/.speccrew/skills/speccrew-dev-desktop-tauri/SKILL.md +10 -2
- package/.speccrew/skills/speccrew-dev-desktop-tauri/workflow.agentflow.xml +245 -0
- package/.speccrew/skills/speccrew-dev-frontend/SKILL.md +10 -2
- package/.speccrew/skills/speccrew-dev-frontend/workflow.agentflow.xml +262 -0
- package/.speccrew/skills/speccrew-dev-mobile/SKILL.md +10 -2
- package/.speccrew/skills/speccrew-dev-mobile/workflow.agentflow.xml +244 -0
- package/.speccrew/skills/speccrew-dev-review-backend/SKILL.md +10 -2
- package/.speccrew/skills/speccrew-dev-review-backend/workflow.agentflow.xml +251 -0
- package/.speccrew/skills/speccrew-dev-review-desktop/SKILL.md +10 -2
- package/.speccrew/skills/speccrew-dev-review-desktop/workflow.agentflow.xml +214 -0
- package/.speccrew/skills/speccrew-dev-review-frontend/SKILL.md +10 -2
- package/.speccrew/skills/speccrew-dev-review-frontend/workflow.agentflow.xml +213 -0
- package/.speccrew/skills/speccrew-dev-review-mobile/SKILL.md +10 -2
- package/.speccrew/skills/speccrew-dev-review-mobile/workflow.agentflow.xml +214 -0
- package/.speccrew/skills/speccrew-fd-api-contract/SKILL.md +7 -1
- package/.speccrew/skills/speccrew-fd-api-contract/workflow.agentflow.xml +222 -0
- package/.speccrew/skills/speccrew-fd-feature-analyze/SKILL.md +7 -1
- package/.speccrew/skills/speccrew-fd-feature-analyze/workflow.agentflow.xml +223 -0
- package/.speccrew/skills/speccrew-fd-feature-design/SKILL.md +7 -1
- package/.speccrew/skills/speccrew-fd-feature-design/workflow.agentflow.xml +322 -0
- package/.speccrew/skills/speccrew-get-timestamp/SKILL.md +3 -39
- package/.speccrew/skills/speccrew-get-timestamp/workflow.agentflow.xml +43 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-api-analyze/SKILL.md +57 -508
- package/.speccrew/skills/{speccrew-knowledge-bizs-api-analyze-xml/SKILL.md → speccrew-knowledge-bizs-api-analyze/workflow.agentflow.xml} +1 -154
- package/.speccrew/skills/speccrew-knowledge-bizs-api-graph/SKILL.md +73 -283
- package/.speccrew/skills/{speccrew-knowledge-bizs-api-graph-xml/SKILL.md → speccrew-knowledge-bizs-api-graph/workflow.agentflow.xml} +0 -298
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/SKILL.md +931 -801
- package/.speccrew/skills/{speccrew-knowledge-bizs-dispatch-xml/SKILL.md → speccrew-knowledge-bizs-dispatch/workflow.agentflow.xml} +42 -272
- package/.speccrew/skills/speccrew-knowledge-bizs-identify-entries/SKILL.md +263 -71
- package/.speccrew/skills/{speccrew-knowledge-bizs-identify-entries-xml/SKILL.md → speccrew-knowledge-bizs-identify-entries/workflow.agentflow.xml} +8 -184
- package/.speccrew/skills/speccrew-knowledge-bizs-init-features/SKILL.md +200 -181
- package/.speccrew/skills/{speccrew-knowledge-bizs-init-features-xml/SKILL.md → speccrew-knowledge-bizs-init-features/workflow.agentflow.xml} +7 -134
- package/.speccrew/skills/speccrew-knowledge-bizs-module-classify/SKILL.md +5 -89
- package/.speccrew/skills/speccrew-knowledge-bizs-module-classify/workflow.agentflow.xml +129 -0
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-analyze/SKILL.md +454 -326
- package/.speccrew/skills/{speccrew-knowledge-bizs-ui-analyze-xml/SKILL.md → speccrew-knowledge-bizs-ui-analyze/workflow.agentflow.xml} +8 -128
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-graph/SKILL.md +302 -247
- package/.speccrew/skills/{speccrew-knowledge-bizs-ui-graph-xml/SKILL.md → speccrew-knowledge-bizs-ui-graph/workflow.agentflow.xml} +7 -199
- package/.speccrew/skills/speccrew-knowledge-bizs-ui-style-extract/SKILL.md +267 -156
- package/.speccrew/skills/{speccrew-knowledge-bizs-ui-style-extract-xml/SKILL.md → speccrew-knowledge-bizs-ui-style-extract/workflow.agentflow.xml} +7 -151
- package/.speccrew/skills/speccrew-knowledge-graph-query/SKILL.md +3 -122
- package/.speccrew/skills/speccrew-knowledge-graph-query/workflow.agentflow.xml +106 -0
- package/.speccrew/skills/speccrew-knowledge-graph-write/SKILL.md +3 -80
- package/.speccrew/skills/speccrew-knowledge-graph-write/workflow.agentflow.xml +152 -0
- package/.speccrew/skills/speccrew-knowledge-module-summarize/SKILL.md +371 -265
- package/.speccrew/skills/{speccrew-knowledge-module-summarize-xml/SKILL.md → speccrew-knowledge-module-summarize/workflow.agentflow.xml} +7 -197
- package/.speccrew/skills/speccrew-knowledge-system-summarize/SKILL.md +45 -333
- package/.speccrew/skills/{speccrew-knowledge-system-summarize-xml/SKILL.md → speccrew-knowledge-system-summarize/workflow.agentflow.xml} +0 -177
- package/.speccrew/skills/speccrew-knowledge-techs-dispatch/SKILL.md +174 -727
- package/.speccrew/skills/{speccrew-knowledge-techs-dispatch-xml/SKILL.md → speccrew-knowledge-techs-dispatch/workflow.agentflow.xml} +10 -351
- package/.speccrew/skills/speccrew-knowledge-techs-generate/SKILL.md +20 -150
- package/.speccrew/skills/{speccrew-knowledge-techs-generate-xml/SKILL.md → speccrew-knowledge-techs-generate/workflow.agentflow.xml} +0 -169
- package/.speccrew/skills/speccrew-knowledge-techs-generate-conventions/SKILL.md +75 -587
- package/.speccrew/skills/{speccrew-knowledge-techs-generate-conventions-xml/SKILL.md → speccrew-knowledge-techs-generate-conventions/workflow.agentflow.xml} +0 -153
- package/.speccrew/skills/speccrew-knowledge-techs-generate-quality/SKILL.md +463 -297
- package/.speccrew/skills/{speccrew-knowledge-techs-generate-quality-xml/SKILL.md → speccrew-knowledge-techs-generate-quality/workflow.agentflow.xml} +0 -164
- package/.speccrew/skills/speccrew-knowledge-techs-generate-ui-style/SKILL.md +57 -292
- package/.speccrew/skills/{speccrew-knowledge-techs-generate-ui-style-xml/SKILL.md → speccrew-knowledge-techs-generate-ui-style/workflow.agentflow.xml} +2 -193
- package/.speccrew/skills/speccrew-knowledge-techs-index/SKILL.md +49 -335
- package/.speccrew/skills/{speccrew-knowledge-techs-index-xml/SKILL.md → speccrew-knowledge-techs-index/workflow.agentflow.xml} +0 -167
- package/.speccrew/skills/speccrew-knowledge-techs-init/SKILL.md +28 -109
- package/.speccrew/skills/{speccrew-knowledge-techs-init-xml/SKILL.md → speccrew-knowledge-techs-init/workflow.agentflow.xml} +0 -189
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/SKILL.md +3 -487
- package/.speccrew/skills/speccrew-knowledge-techs-ui-analyze/workflow.agentflow.xml +278 -0
- package/.speccrew/skills/speccrew-pm-knowledge-detector/SKILL.md +3 -71
- package/.speccrew/skills/speccrew-pm-knowledge-detector/workflow.agentflow.xml +108 -0
- package/.speccrew/skills/speccrew-pm-module-initializer/SKILL.md +3 -107
- package/.speccrew/skills/speccrew-pm-module-initializer/workflow.agentflow.xml +139 -0
- package/.speccrew/skills/speccrew-pm-module-matcher/SKILL.md +3 -115
- package/.speccrew/skills/speccrew-pm-module-matcher/workflow.agentflow.xml +146 -0
- package/.speccrew/skills/speccrew-pm-requirement-analysis/SKILL.md +3 -343
- package/.speccrew/skills/speccrew-pm-requirement-analysis/workflow.agentflow.xml +174 -0
- package/.speccrew/skills/speccrew-pm-requirement-assess/SKILL.md +3 -91
- package/.speccrew/skills/speccrew-pm-requirement-assess/workflow.agentflow.xml +173 -0
- package/.speccrew/skills/speccrew-pm-requirement-clarify/SKILL.md +3 -224
- package/.speccrew/skills/speccrew-pm-requirement-clarify/workflow.agentflow.xml +159 -0
- package/.speccrew/skills/speccrew-pm-requirement-model/SKILL.md +3 -275
- package/.speccrew/skills/speccrew-pm-requirement-model/workflow.agentflow.xml +210 -0
- package/.speccrew/skills/speccrew-pm-requirement-simple/SKILL.md +3 -76
- package/.speccrew/skills/speccrew-pm-requirement-simple/workflow.agentflow.xml +120 -0
- package/.speccrew/skills/speccrew-pm-sub-prd-generate/SKILL.md +7 -1
- package/.speccrew/skills/speccrew-pm-sub-prd-generate/workflow.agentflow.xml +218 -0
- package/.speccrew/skills/speccrew-sd-backend/SKILL.md +7 -1
- package/.speccrew/skills/speccrew-sd-backend/workflow.agentflow.xml +264 -0
- package/.speccrew/skills/speccrew-sd-desktop/SKILL.md +7 -1
- package/.speccrew/skills/speccrew-sd-desktop/workflow.agentflow.xml +288 -0
- package/.speccrew/skills/speccrew-sd-framework-evaluate/SKILL.md +7 -1
- package/.speccrew/skills/speccrew-sd-framework-evaluate/workflow.agentflow.xml +235 -0
- package/.speccrew/skills/speccrew-sd-frontend/SKILL.md +7 -1
- package/.speccrew/skills/speccrew-sd-frontend/workflow.agentflow.xml +299 -0
- package/.speccrew/skills/speccrew-sd-mobile/SKILL.md +7 -1
- package/.speccrew/skills/speccrew-sd-mobile/workflow.agentflow.xml +301 -0
- package/.speccrew/skills/speccrew-test-case-design/SKILL.md +165 -284
- package/.speccrew/skills/speccrew-test-case-design/workflow.agentflow.xml +210 -0
- package/.speccrew/skills/speccrew-test-code-gen/SKILL.md +204 -324
- package/.speccrew/skills/speccrew-test-code-gen/workflow.agentflow.xml +265 -0
- package/.speccrew/skills/speccrew-test-reporter/SKILL.md +205 -184
- package/.speccrew/skills/speccrew-test-reporter/workflow.agentflow.xml +284 -0
- package/.speccrew/skills/speccrew-test-runner/SKILL.md +242 -241
- package/.speccrew/skills/speccrew-test-runner/workflow.agentflow.xml +314 -0
- package/bin/cli.js +8 -1
- package/lib/commands/validate.js +565 -0
- package/package.json +1 -1
- package/workspace-template/docs/rules/{xml-workflow-spec.md → agentflow-spec.md} +5 -5
- package/workspace-template/scripts/validate-agentflow.js +637 -0
- package/.speccrew/agents/speccrew-team-leader-xml.md +0 -480
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/STATUS-FORMATS.md +0 -99
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/batch-orchestrator.js +0 -176
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/get-next-batch.js +0 -150
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/get-pending-features.js +0 -106
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/mark-stale.js +0 -249
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/merge-features.js +0 -300
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/process-batch-results.js +0 -915
- package/.speccrew/skills/speccrew-knowledge-bizs-dispatch/scripts/update-feature-status.js +0 -226
- package/.speccrew/skills/speccrew-knowledge-bizs-init-features/examples/features.json +0 -34
- package/.speccrew/skills/speccrew-knowledge-bizs-init-features/scripts/generate-inventory.js +0 -1071
- package/.speccrew/skills/speccrew-knowledge-bizs-init-features/scripts/test-inventory.js +0 -26
- package/.speccrew/skills/speccrew-knowledge-techs-dispatch/STATUS-FORMATS.md +0 -550
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* merge-features.js
|
|
4
|
-
*
|
|
5
|
-
* Merge incremental feature inventory (*.new.json) with existing features (*.json).
|
|
6
|
-
* Identifies added/removed/changed/unchanged features and cleans up artifacts for removed features.
|
|
7
|
-
*
|
|
8
|
-
* Usage: node merge-features.js --syncStatePath <path> --completedDir <path> --projectRoot <path>
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const path = require('path');
|
|
13
|
-
|
|
14
|
-
function parseArgs() {
|
|
15
|
-
const args = process.argv.slice(2);
|
|
16
|
-
const params = {};
|
|
17
|
-
for (let i = 0; i < args.length; i++) {
|
|
18
|
-
if (args[i].startsWith('--')) {
|
|
19
|
-
const key = args[i].slice(2);
|
|
20
|
-
const value = args[i + 1];
|
|
21
|
-
if (value !== undefined && !value.startsWith('--')) {
|
|
22
|
-
params[key] = value;
|
|
23
|
-
i++;
|
|
24
|
-
} else {
|
|
25
|
-
params[key] = true;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return params;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function normalizePath(p) {
|
|
33
|
-
return p ? p.replace(/\\/g, '/') : '';
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Safely delete a file, logging the action
|
|
37
|
-
function safeDelete(filePath, cleanedFiles) {
|
|
38
|
-
if (fs.existsSync(filePath)) {
|
|
39
|
-
try {
|
|
40
|
-
fs.unlinkSync(filePath);
|
|
41
|
-
cleanedFiles.push(normalizePath(filePath));
|
|
42
|
-
return true;
|
|
43
|
-
} catch (e) {
|
|
44
|
-
console.error(`Warning: Failed to delete ${filePath}: ${e.message}`);
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Compare timestamps: returns true if sourceModified is newer than analysisCompleted
|
|
52
|
-
function isNewer(lastModified, completedAt) {
|
|
53
|
-
if (!completedAt) return true; // never analyzed → needs analysis
|
|
54
|
-
if (!lastModified) return false; // no modification info → assume unchanged
|
|
55
|
-
|
|
56
|
-
// lastModified is ISO format: "2026-04-07T12:30:00.000Z"
|
|
57
|
-
// completedAt is custom format: "2026-04-07-123000" (from dispatch)
|
|
58
|
-
// Normalize completedAt to comparable format
|
|
59
|
-
const normalizedCompleted = normalizeCompletedAt(completedAt);
|
|
60
|
-
|
|
61
|
-
const modDate = new Date(lastModified);
|
|
62
|
-
const compDate = new Date(normalizedCompleted);
|
|
63
|
-
|
|
64
|
-
// If either date is invalid, treat as changed (safer)
|
|
65
|
-
if (isNaN(modDate.getTime()) || isNaN(compDate.getTime())) {
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return modDate > compDate;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Normalize completedAt format "2026-04-07-123000" → "2026-04-07T12:30:00"
|
|
73
|
-
function normalizeCompletedAt(completedAt) {
|
|
74
|
-
if (!completedAt) return null;
|
|
75
|
-
|
|
76
|
-
// Try ISO format first
|
|
77
|
-
if (completedAt.includes('T')) return completedAt;
|
|
78
|
-
|
|
79
|
-
// Handle "YYYY-MM-DD-HHmmss" format
|
|
80
|
-
const match = completedAt.match(/^(\d{4}-\d{2}-\d{2})-(\d{2})(\d{2})(\d{2})$/);
|
|
81
|
-
if (match) {
|
|
82
|
-
return `${match[1]}T${match[2]}:${match[3]}:${match[4]}`;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return completedAt;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function main() {
|
|
89
|
-
const params = parseArgs();
|
|
90
|
-
|
|
91
|
-
const syncStatePath = params.syncStatePath;
|
|
92
|
-
const completedDir = params.completedDir;
|
|
93
|
-
const projectRoot = params.projectRoot;
|
|
94
|
-
|
|
95
|
-
if (!syncStatePath || !completedDir || !projectRoot) {
|
|
96
|
-
console.error('Usage: node merge-features.js --syncStatePath <path> --completedDir <path> --projectRoot <path>');
|
|
97
|
-
process.exit(1);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const resolvedSyncState = path.resolve(syncStatePath);
|
|
101
|
-
const resolvedCompletedDir = path.resolve(completedDir);
|
|
102
|
-
const resolvedProjectRoot = path.resolve(projectRoot);
|
|
103
|
-
|
|
104
|
-
// Scan for *.new.json files
|
|
105
|
-
if (!fs.existsSync(resolvedSyncState)) {
|
|
106
|
-
// Output empty result
|
|
107
|
-
console.log(JSON.stringify({ platforms: [], totalAdded: 0, totalRemoved: 0, totalChanged: 0, totalUnchanged: 0 }));
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const newFiles = fs.readdirSync(resolvedSyncState)
|
|
112
|
-
.filter(f => f.startsWith('features-') && f.endsWith('.new.json'));
|
|
113
|
-
|
|
114
|
-
if (newFiles.length === 0) {
|
|
115
|
-
// No incremental files found
|
|
116
|
-
console.log(JSON.stringify({ platforms: [], totalAdded: 0, totalRemoved: 0, totalChanged: 0, totalUnchanged: 0 }));
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const result = {
|
|
121
|
-
platforms: [],
|
|
122
|
-
totalAdded: 0,
|
|
123
|
-
totalRemoved: 0,
|
|
124
|
-
totalChanged: 0,
|
|
125
|
-
totalUnchanged: 0
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
for (const newFile of newFiles) {
|
|
129
|
-
const newFilePath = path.join(resolvedSyncState, newFile);
|
|
130
|
-
// features-backend-bpm.new.json → features-backend-bpm.json
|
|
131
|
-
const oldFileName = newFile.replace('.new.json', '.json');
|
|
132
|
-
const oldFilePath = path.join(resolvedSyncState, oldFileName);
|
|
133
|
-
|
|
134
|
-
// Read new features
|
|
135
|
-
let newData;
|
|
136
|
-
try {
|
|
137
|
-
newData = JSON.parse(fs.readFileSync(newFilePath, 'utf8'));
|
|
138
|
-
} catch (e) {
|
|
139
|
-
console.error(`Error reading ${newFile}: ${e.message}`);
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Read old features (if exists)
|
|
144
|
-
let oldData = null;
|
|
145
|
-
if (fs.existsSync(oldFilePath)) {
|
|
146
|
-
try {
|
|
147
|
-
oldData = JSON.parse(fs.readFileSync(oldFilePath, 'utf8'));
|
|
148
|
-
} catch (e) {
|
|
149
|
-
console.error(`Warning: Failed to read ${oldFileName}, treating as fresh: ${e.message}`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// If no old data, just rename new → old (first-time generation)
|
|
154
|
-
if (!oldData) {
|
|
155
|
-
fs.renameSync(newFilePath, oldFilePath);
|
|
156
|
-
const platformResult = {
|
|
157
|
-
platformId: newData.platformId || oldFileName.replace('features-', '').replace('.json', ''),
|
|
158
|
-
added: newData.features.map(f => f.fileName),
|
|
159
|
-
removed: [],
|
|
160
|
-
changed: [],
|
|
161
|
-
unchanged: [],
|
|
162
|
-
cleanedFiles: []
|
|
163
|
-
};
|
|
164
|
-
result.platforms.push(platformResult);
|
|
165
|
-
result.totalAdded += platformResult.added.length;
|
|
166
|
-
console.error(`Platform ${platformResult.platformId}: ${platformResult.added.length} features (first run)`);
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Build old features map by sourcePath
|
|
171
|
-
const oldMap = new Map();
|
|
172
|
-
for (const feature of (oldData.features || [])) {
|
|
173
|
-
oldMap.set(normalizePath(feature.sourcePath), feature);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Build new features map by sourcePath
|
|
177
|
-
const newMap = new Map();
|
|
178
|
-
for (const feature of (newData.features || [])) {
|
|
179
|
-
newMap.set(normalizePath(feature.sourcePath), feature);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const platformResult = {
|
|
183
|
-
platformId: newData.platformId || oldData.platformId || oldFileName.replace('features-', '').replace('.json', ''),
|
|
184
|
-
added: [],
|
|
185
|
-
removed: [],
|
|
186
|
-
changed: [],
|
|
187
|
-
unchanged: [],
|
|
188
|
-
cleanedFiles: []
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
// Merged features list
|
|
192
|
-
const mergedFeatures = [];
|
|
193
|
-
|
|
194
|
-
// Process new features
|
|
195
|
-
for (const [sourcePath, newFeature] of newMap) {
|
|
196
|
-
const oldFeature = oldMap.get(sourcePath);
|
|
197
|
-
|
|
198
|
-
if (!oldFeature) {
|
|
199
|
-
// Added: new feature not in old
|
|
200
|
-
mergedFeatures.push({
|
|
201
|
-
...newFeature,
|
|
202
|
-
analyzed: false,
|
|
203
|
-
startedAt: null,
|
|
204
|
-
completedAt: null,
|
|
205
|
-
analysisNotes: null
|
|
206
|
-
});
|
|
207
|
-
platformResult.added.push(newFeature.fileName);
|
|
208
|
-
} else if (!oldFeature.analyzed || !oldFeature.completedAt) {
|
|
209
|
-
// Previously not analyzed → keep as not analyzed, use new metadata
|
|
210
|
-
mergedFeatures.push({
|
|
211
|
-
...newFeature,
|
|
212
|
-
analyzed: false,
|
|
213
|
-
startedAt: oldFeature.startedAt,
|
|
214
|
-
completedAt: oldFeature.completedAt,
|
|
215
|
-
analysisNotes: oldFeature.analysisNotes
|
|
216
|
-
});
|
|
217
|
-
platformResult.changed.push(newFeature.fileName);
|
|
218
|
-
} else if (isNewer(newFeature.lastModified, oldFeature.completedAt)) {
|
|
219
|
-
// Changed: source modified after last analysis
|
|
220
|
-
mergedFeatures.push({
|
|
221
|
-
...newFeature,
|
|
222
|
-
analyzed: false,
|
|
223
|
-
startedAt: null,
|
|
224
|
-
completedAt: oldFeature.completedAt, // preserve for reference
|
|
225
|
-
analysisNotes: `Source modified since last analysis (was: ${oldFeature.analysisNotes || 'N/A'})`
|
|
226
|
-
});
|
|
227
|
-
platformResult.changed.push(newFeature.fileName);
|
|
228
|
-
} else {
|
|
229
|
-
// Unchanged: keep old feature state entirely
|
|
230
|
-
mergedFeatures.push({
|
|
231
|
-
...oldFeature,
|
|
232
|
-
// Update metadata from new scan (in case id/documentPath changed)
|
|
233
|
-
id: newFeature.id,
|
|
234
|
-
documentPath: newFeature.documentPath,
|
|
235
|
-
lastModified: newFeature.lastModified
|
|
236
|
-
});
|
|
237
|
-
platformResult.unchanged.push(newFeature.fileName);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Process removed features (in old but not in new)
|
|
242
|
-
for (const [sourcePath, oldFeature] of oldMap) {
|
|
243
|
-
if (!newMap.has(sourcePath)) {
|
|
244
|
-
platformResult.removed.push(oldFeature.fileName);
|
|
245
|
-
|
|
246
|
-
// Clean up artifacts
|
|
247
|
-
// 1. Delete document .md file
|
|
248
|
-
if (oldFeature.documentPath) {
|
|
249
|
-
const docAbsPath = path.join(resolvedProjectRoot, oldFeature.documentPath);
|
|
250
|
-
safeDelete(docAbsPath, platformResult.cleanedFiles);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// 2. Delete .done.json marker
|
|
254
|
-
const donePath = path.join(resolvedCompletedDir, `${oldFeature.fileName}.done.json`);
|
|
255
|
-
safeDelete(donePath, platformResult.cleanedFiles);
|
|
256
|
-
|
|
257
|
-
// 3. Delete .graph.json marker
|
|
258
|
-
const graphPath = path.join(resolvedCompletedDir, `${oldFeature.fileName}.graph.json`);
|
|
259
|
-
safeDelete(graphPath, platformResult.cleanedFiles);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Update inventory metadata
|
|
264
|
-
const analyzedCount = mergedFeatures.filter(f => f.analyzed).length;
|
|
265
|
-
const mergedInventory = {
|
|
266
|
-
...newData,
|
|
267
|
-
// Preserve some old metadata
|
|
268
|
-
analysisMethod: oldData.analysisMethod || newData.analysisMethod,
|
|
269
|
-
// Update counts
|
|
270
|
-
totalFiles: mergedFeatures.length,
|
|
271
|
-
analyzedCount: analyzedCount,
|
|
272
|
-
pendingCount: mergedFeatures.length - analyzedCount,
|
|
273
|
-
generatedAt: new Date().toISOString().replace(/[-:]/g, '').slice(0, 15).replace('T', '-'),
|
|
274
|
-
features: mergedFeatures
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
// Also recalculate modules list
|
|
278
|
-
const moduleSet = new Set(mergedFeatures.map(f => f.module));
|
|
279
|
-
mergedInventory.modules = [...moduleSet].sort();
|
|
280
|
-
|
|
281
|
-
// Write back merged features (overwrite old file)
|
|
282
|
-
fs.writeFileSync(oldFilePath, JSON.stringify(mergedInventory, null, 2), 'utf8');
|
|
283
|
-
|
|
284
|
-
// Delete .new.json file
|
|
285
|
-
fs.unlinkSync(newFilePath);
|
|
286
|
-
|
|
287
|
-
result.platforms.push(platformResult);
|
|
288
|
-
result.totalAdded += platformResult.added.length;
|
|
289
|
-
result.totalRemoved += platformResult.removed.length;
|
|
290
|
-
result.totalChanged += platformResult.changed.length;
|
|
291
|
-
result.totalUnchanged += platformResult.unchanged.length;
|
|
292
|
-
|
|
293
|
-
console.error(`Platform ${platformResult.platformId}: +${platformResult.added.length} added, -${platformResult.removed.length} removed, ~${platformResult.changed.length} changed, =${platformResult.unchanged.length} unchanged`);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Output result as JSON to stdout
|
|
297
|
-
console.log(JSON.stringify(result, null, 2));
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
main();
|