aios-core 3.1.0 → 3.3.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/.aios-core/core-config.yaml +44 -0
- package/.aios-core/infrastructure/scripts/ide-sync/agent-parser.js +251 -0
- package/.aios-core/infrastructure/scripts/ide-sync/index.js +480 -0
- package/.aios-core/infrastructure/scripts/ide-sync/redirect-generator.js +200 -0
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/antigravity.js +105 -0
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/claude-code.js +84 -0
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/cursor.js +93 -0
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/trae.js +125 -0
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/windsurf.js +106 -0
- package/.aios-core/infrastructure/scripts/ide-sync/validator.js +273 -0
- package/.aios-core/install-manifest.yaml +2269 -349
- package/bin/aios-init.js +126 -0
- package/package.json +13 -1
- package/scripts/generate-install-manifest.js +337 -0
- package/scripts/validate-manifest.js +265 -0
- package/src/installer/brownfield-upgrader.js +438 -0
- package/src/installer/file-hasher.js +137 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validator - Verifies IDE sync status
|
|
3
|
+
* @story 6.19 - IDE Command Auto-Sync System
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs-extra');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const crypto = require('crypto');
|
|
9
|
+
const { getRedirectFilenames } = require('./redirect-generator');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Calculate content hash for comparison
|
|
13
|
+
* @param {string} content - File content
|
|
14
|
+
* @returns {string} - SHA256 hash
|
|
15
|
+
*/
|
|
16
|
+
function hashContent(content) {
|
|
17
|
+
// Normalize line endings for cross-platform consistency
|
|
18
|
+
const normalized = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
19
|
+
return crypto.createHash('sha256').update(normalized, 'utf8').digest('hex');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if a file exists at target path
|
|
24
|
+
* @param {string} filePath - Path to check
|
|
25
|
+
* @returns {boolean} - True if file exists
|
|
26
|
+
*/
|
|
27
|
+
function fileExists(filePath) {
|
|
28
|
+
return fs.existsSync(filePath);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Read file content if it exists
|
|
33
|
+
* @param {string} filePath - Path to read
|
|
34
|
+
* @returns {string|null} - File content or null
|
|
35
|
+
*/
|
|
36
|
+
function readFileIfExists(filePath) {
|
|
37
|
+
try {
|
|
38
|
+
if (fs.existsSync(filePath)) {
|
|
39
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
// Ignore read errors
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Validate sync status for a single IDE
|
|
49
|
+
* @param {object[]} expectedFiles - Array of {filename, content} expected
|
|
50
|
+
* @param {string} targetDir - Target directory to check
|
|
51
|
+
* @param {object} redirectsConfig - Redirects configuration
|
|
52
|
+
* @returns {object} - Validation result
|
|
53
|
+
*/
|
|
54
|
+
function validateIdeSync(expectedFiles, targetDir, redirectsConfig) {
|
|
55
|
+
const result = {
|
|
56
|
+
targetDir,
|
|
57
|
+
missing: [],
|
|
58
|
+
drift: [],
|
|
59
|
+
orphaned: [],
|
|
60
|
+
synced: [],
|
|
61
|
+
total: {
|
|
62
|
+
expected: expectedFiles.length,
|
|
63
|
+
missing: 0,
|
|
64
|
+
drift: 0,
|
|
65
|
+
orphaned: 0,
|
|
66
|
+
synced: 0,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Track expected filenames
|
|
71
|
+
const expectedFilenames = new Set(expectedFiles.map(f => f.filename));
|
|
72
|
+
|
|
73
|
+
// Add redirect filenames to expected
|
|
74
|
+
const redirectFilenames = getRedirectFilenames(redirectsConfig);
|
|
75
|
+
for (const rf of redirectFilenames) {
|
|
76
|
+
expectedFilenames.add(rf);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Check each expected file
|
|
80
|
+
for (const expected of expectedFiles) {
|
|
81
|
+
const targetPath = path.join(targetDir, expected.filename);
|
|
82
|
+
const actualContent = readFileIfExists(targetPath);
|
|
83
|
+
|
|
84
|
+
if (actualContent === null) {
|
|
85
|
+
// File is missing
|
|
86
|
+
result.missing.push({
|
|
87
|
+
filename: expected.filename,
|
|
88
|
+
path: targetPath,
|
|
89
|
+
});
|
|
90
|
+
result.total.missing++;
|
|
91
|
+
} else {
|
|
92
|
+
// Compare content
|
|
93
|
+
const expectedHash = hashContent(expected.content);
|
|
94
|
+
const actualHash = hashContent(actualContent);
|
|
95
|
+
|
|
96
|
+
if (expectedHash !== actualHash) {
|
|
97
|
+
// Content differs (drift)
|
|
98
|
+
result.drift.push({
|
|
99
|
+
filename: expected.filename,
|
|
100
|
+
path: targetPath,
|
|
101
|
+
expectedHash,
|
|
102
|
+
actualHash,
|
|
103
|
+
});
|
|
104
|
+
result.total.drift++;
|
|
105
|
+
} else {
|
|
106
|
+
// File is synced
|
|
107
|
+
result.synced.push({
|
|
108
|
+
filename: expected.filename,
|
|
109
|
+
path: targetPath,
|
|
110
|
+
});
|
|
111
|
+
result.total.synced++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check for orphaned files (files in target not in expected)
|
|
117
|
+
if (fs.existsSync(targetDir)) {
|
|
118
|
+
try {
|
|
119
|
+
const actualFiles = fs.readdirSync(targetDir).filter(f => f.endsWith('.md'));
|
|
120
|
+
|
|
121
|
+
for (const file of actualFiles) {
|
|
122
|
+
if (!expectedFilenames.has(file)) {
|
|
123
|
+
result.orphaned.push({
|
|
124
|
+
filename: file,
|
|
125
|
+
path: path.join(targetDir, file),
|
|
126
|
+
});
|
|
127
|
+
result.total.orphaned++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} catch (error) {
|
|
131
|
+
// Ignore directory read errors
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Validate sync status for all IDEs
|
|
140
|
+
* @param {object} ideConfigs - Map of IDE name to {expectedFiles, targetDir}
|
|
141
|
+
* @param {object} redirectsConfig - Redirects configuration
|
|
142
|
+
* @returns {object} - Full validation result
|
|
143
|
+
*/
|
|
144
|
+
function validateAllIdes(ideConfigs, redirectsConfig) {
|
|
145
|
+
const results = {
|
|
146
|
+
ides: {},
|
|
147
|
+
summary: {
|
|
148
|
+
total: 0,
|
|
149
|
+
synced: 0,
|
|
150
|
+
missing: 0,
|
|
151
|
+
drift: 0,
|
|
152
|
+
orphaned: 0,
|
|
153
|
+
pass: true,
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
for (const [ideName, config] of Object.entries(ideConfigs)) {
|
|
158
|
+
const ideResult = validateIdeSync(
|
|
159
|
+
config.expectedFiles,
|
|
160
|
+
config.targetDir,
|
|
161
|
+
redirectsConfig
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
results.ides[ideName] = ideResult;
|
|
165
|
+
|
|
166
|
+
// Update summary
|
|
167
|
+
results.summary.total += ideResult.total.expected;
|
|
168
|
+
results.summary.synced += ideResult.total.synced;
|
|
169
|
+
results.summary.missing += ideResult.total.missing;
|
|
170
|
+
results.summary.drift += ideResult.total.drift;
|
|
171
|
+
results.summary.orphaned += ideResult.total.orphaned;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Pass if no missing or drift
|
|
175
|
+
results.summary.pass =
|
|
176
|
+
results.summary.missing === 0 && results.summary.drift === 0;
|
|
177
|
+
|
|
178
|
+
return results;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Format validation result as report string
|
|
183
|
+
* @param {object} results - Validation results
|
|
184
|
+
* @param {boolean} verbose - Include detailed file lists
|
|
185
|
+
* @returns {string} - Formatted report
|
|
186
|
+
*/
|
|
187
|
+
function formatValidationReport(results, verbose = false) {
|
|
188
|
+
const lines = [];
|
|
189
|
+
|
|
190
|
+
lines.push('# IDE Sync Validation Report');
|
|
191
|
+
lines.push('');
|
|
192
|
+
|
|
193
|
+
// Summary
|
|
194
|
+
lines.push('## Summary');
|
|
195
|
+
lines.push('');
|
|
196
|
+
lines.push(`| Metric | Count |`);
|
|
197
|
+
lines.push(`|--------|-------|`);
|
|
198
|
+
lines.push(`| Total Expected | ${results.summary.total} |`);
|
|
199
|
+
lines.push(`| Synced | ${results.summary.synced} |`);
|
|
200
|
+
lines.push(`| Missing | ${results.summary.missing} |`);
|
|
201
|
+
lines.push(`| Drift | ${results.summary.drift} |`);
|
|
202
|
+
lines.push(`| Orphaned | ${results.summary.orphaned} |`);
|
|
203
|
+
lines.push('');
|
|
204
|
+
|
|
205
|
+
const status = results.summary.pass ? '✅ PASS' : '❌ FAIL';
|
|
206
|
+
lines.push(`**Status:** ${status}`);
|
|
207
|
+
lines.push('');
|
|
208
|
+
|
|
209
|
+
// Per-IDE details
|
|
210
|
+
if (verbose) {
|
|
211
|
+
lines.push('## IDE Details');
|
|
212
|
+
lines.push('');
|
|
213
|
+
|
|
214
|
+
for (const [ideName, ideResult] of Object.entries(results.ides)) {
|
|
215
|
+
lines.push(`### ${ideName}`);
|
|
216
|
+
lines.push('');
|
|
217
|
+
lines.push(`- Target: \`${ideResult.targetDir}\``);
|
|
218
|
+
lines.push(`- Synced: ${ideResult.total.synced}`);
|
|
219
|
+
lines.push(`- Missing: ${ideResult.total.missing}`);
|
|
220
|
+
lines.push(`- Drift: ${ideResult.total.drift}`);
|
|
221
|
+
lines.push(`- Orphaned: ${ideResult.total.orphaned}`);
|
|
222
|
+
lines.push('');
|
|
223
|
+
|
|
224
|
+
if (ideResult.missing.length > 0) {
|
|
225
|
+
lines.push('**Missing Files:**');
|
|
226
|
+
for (const f of ideResult.missing) {
|
|
227
|
+
lines.push(`- ${f.filename}`);
|
|
228
|
+
}
|
|
229
|
+
lines.push('');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (ideResult.drift.length > 0) {
|
|
233
|
+
lines.push('**Drifted Files:**');
|
|
234
|
+
for (const f of ideResult.drift) {
|
|
235
|
+
lines.push(`- ${f.filename}`);
|
|
236
|
+
}
|
|
237
|
+
lines.push('');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (ideResult.orphaned.length > 0) {
|
|
241
|
+
lines.push('**Orphaned Files:**');
|
|
242
|
+
for (const f of ideResult.orphaned) {
|
|
243
|
+
lines.push(`- ${f.filename}`);
|
|
244
|
+
}
|
|
245
|
+
lines.push('');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Fix instructions
|
|
251
|
+
if (!results.summary.pass) {
|
|
252
|
+
lines.push('## How to Fix');
|
|
253
|
+
lines.push('');
|
|
254
|
+
lines.push('Run the following command to sync IDE files:');
|
|
255
|
+
lines.push('');
|
|
256
|
+
lines.push('```bash');
|
|
257
|
+
lines.push('npm run sync:ide');
|
|
258
|
+
lines.push('```');
|
|
259
|
+
lines.push('');
|
|
260
|
+
lines.push('Then commit the generated files.');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return lines.join('\n');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = {
|
|
267
|
+
hashContent,
|
|
268
|
+
fileExists,
|
|
269
|
+
readFileIfExists,
|
|
270
|
+
validateIdeSync,
|
|
271
|
+
validateAllIdes,
|
|
272
|
+
formatValidationReport,
|
|
273
|
+
};
|