aios-core 3.0.0 → 3.2.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/development/agents/squad-creator.md +261 -0
- package/.aios-core/development/scripts/squad/index.js +36 -2
- package/.aios-core/development/scripts/squad/squad-designer.js +1010 -0
- package/.aios-core/development/scripts/squad/squad-generator.js +1317 -0
- package/.aios-core/development/tasks/squad-creator-create.md +289 -0
- package/.aios-core/development/tasks/squad-creator-design.md +334 -0
- package/.aios-core/development/tasks/squad-creator-download.md +65 -0
- package/.aios-core/development/tasks/squad-creator-list.md +225 -0
- package/.aios-core/development/tasks/squad-creator-publish.md +86 -0
- package/.aios-core/development/tasks/squad-creator-sync-synkra.md +83 -0
- package/.aios-core/install-manifest.yaml +2233 -349
- package/.aios-core/schemas/squad-design-schema.json +299 -0
- package/bin/aios-init.js +126 -0
- package/package.json +4 -1
- package/scripts/generate-install-manifest.js +337 -0
- package/scripts/validate-manifest.js +265 -0
- package/squads/.designs/duplicate-test-design.yaml +23 -0
- package/squads/.designs/force-test-design.yaml +23 -0
- package/squads/.designs/nested-test-design.yaml +23 -0
- package/squads/.designs/test-squad-design.yaml +23 -0
- package/src/installer/brownfield-upgrader.js +438 -0
- package/src/installer/file-hasher.js +137 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate Install Manifest
|
|
4
|
+
* Dynamically generates install-manifest.yaml with file hashes for brownfield upgrades
|
|
5
|
+
*
|
|
6
|
+
* @script scripts/generate-install-manifest.js
|
|
7
|
+
* @story 6.18 - Dynamic Manifest & Brownfield Upgrade System
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node scripts/generate-install-manifest.js
|
|
11
|
+
* npm run generate:manifest
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs-extra');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const yaml = require('js-yaml');
|
|
17
|
+
const { hashFile, hashString } = require('../src/installer/file-hasher');
|
|
18
|
+
|
|
19
|
+
// Import FOLDERS_TO_COPY from installer (same source of truth)
|
|
20
|
+
const FOLDERS_TO_COPY = [
|
|
21
|
+
// v2.1 Modular Structure
|
|
22
|
+
'core',
|
|
23
|
+
'development',
|
|
24
|
+
'product',
|
|
25
|
+
'infrastructure',
|
|
26
|
+
// v2.0 Legacy Flat Structure (backwards compatibility)
|
|
27
|
+
'agents',
|
|
28
|
+
'agent-teams',
|
|
29
|
+
'checklists',
|
|
30
|
+
'data',
|
|
31
|
+
'docs',
|
|
32
|
+
'elicitation',
|
|
33
|
+
'scripts',
|
|
34
|
+
'tasks',
|
|
35
|
+
'templates',
|
|
36
|
+
'tools',
|
|
37
|
+
'workflows',
|
|
38
|
+
// Additional directories
|
|
39
|
+
'cli',
|
|
40
|
+
'manifests',
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const ROOT_FILES_TO_COPY = [
|
|
44
|
+
'index.js',
|
|
45
|
+
'index.esm.js',
|
|
46
|
+
'index.d.ts',
|
|
47
|
+
'core-config.yaml',
|
|
48
|
+
'package.json',
|
|
49
|
+
'user-guide.md',
|
|
50
|
+
'working-in-the-brownfield.md',
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// Files/folders to exclude from manifest
|
|
54
|
+
const EXCLUDE_PATTERNS = [
|
|
55
|
+
/node_modules/,
|
|
56
|
+
/\.git/,
|
|
57
|
+
/\.DS_Store/,
|
|
58
|
+
/Thumbs\.db/,
|
|
59
|
+
/\.installed-manifest\.yaml$/, // Don't include installed manifest
|
|
60
|
+
/\.bak$/,
|
|
61
|
+
/\.tmp$/,
|
|
62
|
+
/~$/,
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Check if a path should be excluded
|
|
67
|
+
* @param {string} filePath - Path to check
|
|
68
|
+
* @returns {boolean} - True if should be excluded
|
|
69
|
+
*/
|
|
70
|
+
function shouldExclude(filePath) {
|
|
71
|
+
return EXCLUDE_PATTERNS.some(pattern => pattern.test(filePath));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Determine file type based on path
|
|
76
|
+
* @param {string} relativePath - Relative file path
|
|
77
|
+
* @returns {string} - File type identifier
|
|
78
|
+
*/
|
|
79
|
+
function getFileType(relativePath) {
|
|
80
|
+
const normalized = relativePath.replace(/\\/g, '/');
|
|
81
|
+
|
|
82
|
+
if (normalized.includes('/agents/') || normalized.startsWith('agents/')) {
|
|
83
|
+
return 'agent';
|
|
84
|
+
}
|
|
85
|
+
if (normalized.includes('/tasks/') || normalized.startsWith('tasks/')) {
|
|
86
|
+
return 'task';
|
|
87
|
+
}
|
|
88
|
+
if (normalized.includes('/workflows/') || normalized.startsWith('workflows/')) {
|
|
89
|
+
return 'workflow';
|
|
90
|
+
}
|
|
91
|
+
if (normalized.includes('/templates/') || normalized.startsWith('templates/')) {
|
|
92
|
+
return 'template';
|
|
93
|
+
}
|
|
94
|
+
if (normalized.includes('/checklists/') || normalized.startsWith('checklists/')) {
|
|
95
|
+
return 'checklist';
|
|
96
|
+
}
|
|
97
|
+
if (normalized.includes('/scripts/') || normalized.startsWith('scripts/')) {
|
|
98
|
+
return 'script';
|
|
99
|
+
}
|
|
100
|
+
if (normalized.includes('/tools/') || normalized.startsWith('tools/')) {
|
|
101
|
+
return 'tool';
|
|
102
|
+
}
|
|
103
|
+
if (normalized.includes('/data/') || normalized.startsWith('data/')) {
|
|
104
|
+
return 'data';
|
|
105
|
+
}
|
|
106
|
+
if (normalized.includes('/docs/') || normalized.startsWith('docs/')) {
|
|
107
|
+
return 'documentation';
|
|
108
|
+
}
|
|
109
|
+
if (normalized.includes('/elicitation/') || normalized.startsWith('elicitation/')) {
|
|
110
|
+
return 'elicitation';
|
|
111
|
+
}
|
|
112
|
+
if (normalized.includes('/manifests/') || normalized.startsWith('manifests/')) {
|
|
113
|
+
return 'manifest';
|
|
114
|
+
}
|
|
115
|
+
if (normalized.includes('/cli/') || normalized.startsWith('cli/')) {
|
|
116
|
+
return 'cli';
|
|
117
|
+
}
|
|
118
|
+
if (normalized.includes('/core/') || normalized.startsWith('core/')) {
|
|
119
|
+
return 'core';
|
|
120
|
+
}
|
|
121
|
+
if (normalized.includes('/infrastructure/') || normalized.startsWith('infrastructure/')) {
|
|
122
|
+
return 'infrastructure';
|
|
123
|
+
}
|
|
124
|
+
if (normalized.includes('/product/') || normalized.startsWith('product/')) {
|
|
125
|
+
return 'product';
|
|
126
|
+
}
|
|
127
|
+
if (normalized.includes('/development/') || normalized.startsWith('development/')) {
|
|
128
|
+
return 'development';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Root files
|
|
132
|
+
if (normalized.endsWith('.js') || normalized.endsWith('.ts')) {
|
|
133
|
+
return 'code';
|
|
134
|
+
}
|
|
135
|
+
if (normalized.endsWith('.yaml') || normalized.endsWith('.yml')) {
|
|
136
|
+
return 'config';
|
|
137
|
+
}
|
|
138
|
+
if (normalized.endsWith('.md')) {
|
|
139
|
+
return 'documentation';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return 'other';
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Recursively scan directory and collect file metadata
|
|
147
|
+
* @param {string} dirPath - Directory to scan
|
|
148
|
+
* @param {string} basePath - Base path for relative paths
|
|
149
|
+
* @param {string[]} files - Array to collect files
|
|
150
|
+
*/
|
|
151
|
+
function scanDirectory(dirPath, basePath, files = []) {
|
|
152
|
+
if (!fs.existsSync(dirPath)) {
|
|
153
|
+
return files;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
157
|
+
|
|
158
|
+
for (const entry of entries) {
|
|
159
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
160
|
+
const relativePath = path.relative(basePath, fullPath).replace(/\\/g, '/');
|
|
161
|
+
|
|
162
|
+
if (shouldExclude(relativePath)) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (entry.isDirectory()) {
|
|
167
|
+
scanDirectory(fullPath, basePath, files);
|
|
168
|
+
} else if (entry.isFile()) {
|
|
169
|
+
files.push(fullPath);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return files;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Generate the install manifest
|
|
178
|
+
* @returns {Object} - Manifest object
|
|
179
|
+
*/
|
|
180
|
+
async function generateManifest() {
|
|
181
|
+
const aiosCoreDir = path.join(__dirname, '..', '.aios-core');
|
|
182
|
+
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
183
|
+
|
|
184
|
+
if (!fs.existsSync(aiosCoreDir)) {
|
|
185
|
+
throw new Error(`.aios-core directory not found at ${aiosCoreDir}`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Get version from package.json
|
|
189
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
190
|
+
const version = packageJson.version;
|
|
191
|
+
|
|
192
|
+
console.log(`Generating manifest for aios-core v${version}...`);
|
|
193
|
+
|
|
194
|
+
const allFiles = [];
|
|
195
|
+
|
|
196
|
+
// Scan folders
|
|
197
|
+
for (const folder of FOLDERS_TO_COPY) {
|
|
198
|
+
const folderPath = path.join(aiosCoreDir, folder);
|
|
199
|
+
if (fs.existsSync(folderPath)) {
|
|
200
|
+
scanDirectory(folderPath, aiosCoreDir, allFiles);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Add root files
|
|
205
|
+
for (const file of ROOT_FILES_TO_COPY) {
|
|
206
|
+
const filePath = path.join(aiosCoreDir, file);
|
|
207
|
+
if (fs.existsSync(filePath)) {
|
|
208
|
+
allFiles.push(filePath);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Note: install-manifest.yaml itself is not included in the manifest
|
|
213
|
+
// as it would create a circular dependency during validation
|
|
214
|
+
|
|
215
|
+
console.log(`Found ${allFiles.length} files to include in manifest`);
|
|
216
|
+
|
|
217
|
+
// Generate file entries with metadata
|
|
218
|
+
const fileEntries = [];
|
|
219
|
+
let processedCount = 0;
|
|
220
|
+
|
|
221
|
+
for (const fullPath of allFiles) {
|
|
222
|
+
try {
|
|
223
|
+
const relativePath = path.relative(aiosCoreDir, fullPath).replace(/\\/g, '/');
|
|
224
|
+
const stats = fs.statSync(fullPath);
|
|
225
|
+
const hash = hashFile(fullPath);
|
|
226
|
+
const fileType = getFileType(relativePath);
|
|
227
|
+
|
|
228
|
+
fileEntries.push({
|
|
229
|
+
path: relativePath,
|
|
230
|
+
hash: `sha256:${hash}`,
|
|
231
|
+
type: fileType,
|
|
232
|
+
size: stats.size,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
processedCount++;
|
|
236
|
+
if (processedCount % 50 === 0) {
|
|
237
|
+
console.log(` Processed ${processedCount}/${allFiles.length} files...`);
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.warn(` Warning: Could not process ${fullPath}: ${error.message}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Sort files by path for consistent output
|
|
245
|
+
fileEntries.sort((a, b) => a.path.localeCompare(b.path));
|
|
246
|
+
|
|
247
|
+
// Build manifest object
|
|
248
|
+
const manifest = {
|
|
249
|
+
version: version,
|
|
250
|
+
generated_at: new Date().toISOString(),
|
|
251
|
+
generator: 'scripts/generate-install-manifest.js',
|
|
252
|
+
file_count: fileEntries.length,
|
|
253
|
+
files: fileEntries,
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
return manifest;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Write manifest to file
|
|
261
|
+
* @param {Object} manifest - Manifest object
|
|
262
|
+
*/
|
|
263
|
+
async function writeManifest(manifest) {
|
|
264
|
+
const manifestPath = path.join(__dirname, '..', '.aios-core', 'install-manifest.yaml');
|
|
265
|
+
|
|
266
|
+
// Generate YAML with custom options for readability
|
|
267
|
+
const yamlContent = yaml.dump(manifest, {
|
|
268
|
+
indent: 2,
|
|
269
|
+
lineWidth: 120,
|
|
270
|
+
noRefs: true,
|
|
271
|
+
sortKeys: false,
|
|
272
|
+
quotingType: '"',
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Add header comment
|
|
276
|
+
const header = `# AIOS-Core Install Manifest
|
|
277
|
+
# Auto-generated by scripts/generate-install-manifest.js
|
|
278
|
+
# DO NOT EDIT MANUALLY - regenerate with: npm run generate:manifest
|
|
279
|
+
#
|
|
280
|
+
# This manifest is used for brownfield upgrades to track:
|
|
281
|
+
# - Which files are part of the framework
|
|
282
|
+
# - SHA256 hashes for change detection
|
|
283
|
+
# - File types for categorization
|
|
284
|
+
#
|
|
285
|
+
`;
|
|
286
|
+
|
|
287
|
+
fs.writeFileSync(manifestPath, header + yamlContent, 'utf8');
|
|
288
|
+
|
|
289
|
+
console.log(`\nManifest written to: ${manifestPath}`);
|
|
290
|
+
console.log(` Version: ${manifest.version}`);
|
|
291
|
+
console.log(` Files: ${manifest.file_count}`);
|
|
292
|
+
console.log(` Generated: ${manifest.generated_at}`);
|
|
293
|
+
|
|
294
|
+
// Also compute and display manifest hash for integrity verification
|
|
295
|
+
const manifestHash = hashString(yamlContent);
|
|
296
|
+
console.log(` Manifest hash: sha256:${manifestHash.substring(0, 16)}...`);
|
|
297
|
+
|
|
298
|
+
return manifestPath;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Main execution
|
|
303
|
+
*/
|
|
304
|
+
async function main() {
|
|
305
|
+
try {
|
|
306
|
+
console.log('='.repeat(60));
|
|
307
|
+
console.log('AIOS-Core Install Manifest Generator');
|
|
308
|
+
console.log('='.repeat(60));
|
|
309
|
+
console.log('');
|
|
310
|
+
|
|
311
|
+
const manifest = await generateManifest();
|
|
312
|
+
await writeManifest(manifest);
|
|
313
|
+
|
|
314
|
+
console.log('\n✅ Manifest generated successfully!');
|
|
315
|
+
process.exit(0);
|
|
316
|
+
} catch (error) {
|
|
317
|
+
console.error('\n❌ Error generating manifest:', error.message);
|
|
318
|
+
if (process.env.DEBUG) {
|
|
319
|
+
console.error(error.stack);
|
|
320
|
+
}
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Run if called directly
|
|
326
|
+
if (require.main === module) {
|
|
327
|
+
main();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
module.exports = {
|
|
331
|
+
generateManifest,
|
|
332
|
+
writeManifest,
|
|
333
|
+
getFileType,
|
|
334
|
+
scanDirectory,
|
|
335
|
+
FOLDERS_TO_COPY,
|
|
336
|
+
ROOT_FILES_TO_COPY,
|
|
337
|
+
};
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Validate Install Manifest
|
|
4
|
+
* Ensures install-manifest.yaml is up-to-date with actual files
|
|
5
|
+
*
|
|
6
|
+
* @script scripts/validate-manifest.js
|
|
7
|
+
* @story 6.18 - Dynamic Manifest & Brownfield Upgrade System
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node scripts/validate-manifest.js
|
|
11
|
+
* npm run validate:manifest
|
|
12
|
+
*
|
|
13
|
+
* Exit codes:
|
|
14
|
+
* 0 - Manifest is valid and up-to-date
|
|
15
|
+
* 1 - Manifest is outdated or has issues
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs-extra');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const yaml = require('js-yaml');
|
|
21
|
+
const { hashFile } = require('../src/installer/file-hasher');
|
|
22
|
+
const {
|
|
23
|
+
scanDirectory,
|
|
24
|
+
FOLDERS_TO_COPY,
|
|
25
|
+
ROOT_FILES_TO_COPY,
|
|
26
|
+
getFileType,
|
|
27
|
+
} = require('./generate-install-manifest');
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Validation result object
|
|
31
|
+
* @typedef {Object} ValidationResult
|
|
32
|
+
* @property {boolean} valid - Whether manifest is valid
|
|
33
|
+
* @property {string[]} newFiles - Files in filesystem but not in manifest
|
|
34
|
+
* @property {string[]} removedFiles - Files in manifest but not in filesystem
|
|
35
|
+
* @property {string[]} modifiedFiles - Files with different hashes
|
|
36
|
+
* @property {string[]} errors - Error messages
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Load and parse the install manifest
|
|
41
|
+
* @returns {Object|null} - Parsed manifest or null if not found
|
|
42
|
+
*/
|
|
43
|
+
function loadManifest() {
|
|
44
|
+
const manifestPath = path.join(__dirname, '..', '.aios-core', 'install-manifest.yaml');
|
|
45
|
+
|
|
46
|
+
if (!fs.existsSync(manifestPath)) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const content = fs.readFileSync(manifestPath, 'utf8');
|
|
51
|
+
return yaml.load(content);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get current files from filesystem
|
|
56
|
+
* @returns {Map<string, Object>} - Map of relativePath -> file metadata
|
|
57
|
+
*/
|
|
58
|
+
function getCurrentFiles() {
|
|
59
|
+
const aiosCoreDir = path.join(__dirname, '..', '.aios-core');
|
|
60
|
+
const filesMap = new Map();
|
|
61
|
+
|
|
62
|
+
// Scan folders
|
|
63
|
+
const allFiles = [];
|
|
64
|
+
for (const folder of FOLDERS_TO_COPY) {
|
|
65
|
+
const folderPath = path.join(aiosCoreDir, folder);
|
|
66
|
+
if (fs.existsSync(folderPath)) {
|
|
67
|
+
scanDirectory(folderPath, aiosCoreDir, allFiles);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Add root files
|
|
72
|
+
for (const file of ROOT_FILES_TO_COPY) {
|
|
73
|
+
const filePath = path.join(aiosCoreDir, file);
|
|
74
|
+
if (fs.existsSync(filePath)) {
|
|
75
|
+
allFiles.push(filePath);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Build map
|
|
80
|
+
for (const fullPath of allFiles) {
|
|
81
|
+
const relativePath = path.relative(aiosCoreDir, fullPath).replace(/\\/g, '/');
|
|
82
|
+
try {
|
|
83
|
+
const hash = hashFile(fullPath);
|
|
84
|
+
filesMap.set(relativePath, {
|
|
85
|
+
path: relativePath,
|
|
86
|
+
hash: `sha256:${hash}`,
|
|
87
|
+
type: getFileType(relativePath),
|
|
88
|
+
});
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.warn(`Warning: Could not hash ${relativePath}: ${error.message}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return filesMap;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Validate manifest against current filesystem
|
|
99
|
+
* @returns {ValidationResult} - Validation results
|
|
100
|
+
*/
|
|
101
|
+
function validateManifest() {
|
|
102
|
+
const result = {
|
|
103
|
+
valid: true,
|
|
104
|
+
newFiles: [],
|
|
105
|
+
removedFiles: [],
|
|
106
|
+
modifiedFiles: [],
|
|
107
|
+
errors: [],
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Load manifest
|
|
111
|
+
const manifest = loadManifest();
|
|
112
|
+
if (!manifest) {
|
|
113
|
+
result.valid = false;
|
|
114
|
+
result.errors.push('install-manifest.yaml not found');
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!manifest.files || !Array.isArray(manifest.files)) {
|
|
119
|
+
result.valid = false;
|
|
120
|
+
result.errors.push('Manifest has no files array');
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Get current files
|
|
125
|
+
const currentFiles = getCurrentFiles();
|
|
126
|
+
|
|
127
|
+
// Build set of manifest paths
|
|
128
|
+
const manifestPaths = new Set();
|
|
129
|
+
const manifestMap = new Map();
|
|
130
|
+
|
|
131
|
+
for (const entry of manifest.files) {
|
|
132
|
+
const normalizedPath = entry.path.replace(/\\/g, '/');
|
|
133
|
+
manifestPaths.add(normalizedPath);
|
|
134
|
+
manifestMap.set(normalizedPath, entry);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check for new files (in filesystem but not in manifest)
|
|
138
|
+
for (const [filePath, _fileData] of currentFiles) {
|
|
139
|
+
if (!manifestPaths.has(filePath)) {
|
|
140
|
+
result.newFiles.push(filePath);
|
|
141
|
+
result.valid = false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Check for removed files and hash mismatches
|
|
146
|
+
for (const [manifestPath, manifestEntry] of manifestMap) {
|
|
147
|
+
const currentFile = currentFiles.get(manifestPath);
|
|
148
|
+
|
|
149
|
+
if (!currentFile) {
|
|
150
|
+
// File in manifest but not in filesystem
|
|
151
|
+
result.removedFiles.push(manifestPath);
|
|
152
|
+
result.valid = false;
|
|
153
|
+
} else if (currentFile.hash !== manifestEntry.hash) {
|
|
154
|
+
// Hash mismatch
|
|
155
|
+
result.modifiedFiles.push({
|
|
156
|
+
path: manifestPath,
|
|
157
|
+
manifestHash: manifestEntry.hash,
|
|
158
|
+
currentHash: currentFile.hash,
|
|
159
|
+
});
|
|
160
|
+
result.valid = false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Print validation report
|
|
169
|
+
* @param {ValidationResult} result - Validation results
|
|
170
|
+
*/
|
|
171
|
+
function printReport(result) {
|
|
172
|
+
console.log('='.repeat(60));
|
|
173
|
+
console.log('AIOS-Core Manifest Validation Report');
|
|
174
|
+
console.log('='.repeat(60));
|
|
175
|
+
console.log('');
|
|
176
|
+
|
|
177
|
+
if (result.errors.length > 0) {
|
|
178
|
+
console.log('❌ ERRORS:');
|
|
179
|
+
for (const error of result.errors) {
|
|
180
|
+
console.log(` - ${error}`);
|
|
181
|
+
}
|
|
182
|
+
console.log('');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (result.newFiles.length > 0) {
|
|
186
|
+
console.log(`📁 NEW FILES (${result.newFiles.length}) - not in manifest:`);
|
|
187
|
+
for (const file of result.newFiles.slice(0, 20)) {
|
|
188
|
+
console.log(` + ${file}`);
|
|
189
|
+
}
|
|
190
|
+
if (result.newFiles.length > 20) {
|
|
191
|
+
console.log(` ... and ${result.newFiles.length - 20} more`);
|
|
192
|
+
}
|
|
193
|
+
console.log('');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (result.removedFiles.length > 0) {
|
|
197
|
+
console.log(`🗑️ REMOVED FILES (${result.removedFiles.length}) - in manifest but missing:`);
|
|
198
|
+
for (const file of result.removedFiles.slice(0, 20)) {
|
|
199
|
+
console.log(` - ${file}`);
|
|
200
|
+
}
|
|
201
|
+
if (result.removedFiles.length > 20) {
|
|
202
|
+
console.log(` ... and ${result.removedFiles.length - 20} more`);
|
|
203
|
+
}
|
|
204
|
+
console.log('');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (result.modifiedFiles.length > 0) {
|
|
208
|
+
console.log(`📝 MODIFIED FILES (${result.modifiedFiles.length}) - hash mismatch:`);
|
|
209
|
+
for (const file of result.modifiedFiles.slice(0, 20)) {
|
|
210
|
+
console.log(` ~ ${file.path}`);
|
|
211
|
+
if (process.env.VERBOSE) {
|
|
212
|
+
console.log(` manifest: ${file.manifestHash}`);
|
|
213
|
+
console.log(` current: ${file.currentHash}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (result.modifiedFiles.length > 20) {
|
|
217
|
+
console.log(` ... and ${result.modifiedFiles.length - 20} more`);
|
|
218
|
+
}
|
|
219
|
+
console.log('');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Summary
|
|
223
|
+
console.log('-'.repeat(60));
|
|
224
|
+
if (result.valid) {
|
|
225
|
+
console.log('✅ Manifest is VALID and up-to-date');
|
|
226
|
+
} else {
|
|
227
|
+
console.log('❌ Manifest is OUTDATED');
|
|
228
|
+
console.log('');
|
|
229
|
+
console.log('To fix, run: npm run generate:manifest');
|
|
230
|
+
}
|
|
231
|
+
console.log('-'.repeat(60));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Main execution
|
|
236
|
+
*/
|
|
237
|
+
async function main() {
|
|
238
|
+
try {
|
|
239
|
+
const result = validateManifest();
|
|
240
|
+
printReport(result);
|
|
241
|
+
|
|
242
|
+
if (!result.valid) {
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
process.exit(0);
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error('\n❌ Error validating manifest:', error.message);
|
|
248
|
+
if (process.env.DEBUG) {
|
|
249
|
+
console.error(error.stack);
|
|
250
|
+
}
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Run if called directly
|
|
256
|
+
if (require.main === module) {
|
|
257
|
+
main();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
module.exports = {
|
|
261
|
+
validateManifest,
|
|
262
|
+
loadManifest,
|
|
263
|
+
getCurrentFiles,
|
|
264
|
+
printReport,
|
|
265
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Squad Design Blueprint
|
|
2
|
+
# Generated by *design-squad
|
|
3
|
+
# Source: Interactive input
|
|
4
|
+
# Created: 2025-12-18T23:01:55.322Z
|
|
5
|
+
|
|
6
|
+
squad:
|
|
7
|
+
name: duplicate-test
|
|
8
|
+
domain: test
|
|
9
|
+
analysis:
|
|
10
|
+
entities: []
|
|
11
|
+
workflows: []
|
|
12
|
+
integrations: []
|
|
13
|
+
stakeholders: []
|
|
14
|
+
recommendations:
|
|
15
|
+
agents: []
|
|
16
|
+
tasks: []
|
|
17
|
+
template: basic
|
|
18
|
+
config_mode: extend
|
|
19
|
+
metadata:
|
|
20
|
+
created_at: '2025-12-18T23:01:55.322Z'
|
|
21
|
+
source_docs: []
|
|
22
|
+
user_adjustments: 0
|
|
23
|
+
overall_confidence: 0.5
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Squad Design Blueprint
|
|
2
|
+
# Generated by *design-squad
|
|
3
|
+
# Source: Interactive input
|
|
4
|
+
# Created: 2025-12-18T23:01:55.323Z
|
|
5
|
+
|
|
6
|
+
squad:
|
|
7
|
+
name: force-test
|
|
8
|
+
domain: test-v1
|
|
9
|
+
analysis:
|
|
10
|
+
entities: []
|
|
11
|
+
workflows: []
|
|
12
|
+
integrations: []
|
|
13
|
+
stakeholders: []
|
|
14
|
+
recommendations:
|
|
15
|
+
agents: []
|
|
16
|
+
tasks: []
|
|
17
|
+
template: basic
|
|
18
|
+
config_mode: extend
|
|
19
|
+
metadata:
|
|
20
|
+
created_at: '2025-12-18T23:01:55.323Z'
|
|
21
|
+
source_docs: []
|
|
22
|
+
user_adjustments: 0
|
|
23
|
+
overall_confidence: 0.5
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Squad Design Blueprint
|
|
2
|
+
# Generated by *design-squad
|
|
3
|
+
# Source: Interactive input
|
|
4
|
+
# Created: 2025-12-18T23:01:55.321Z
|
|
5
|
+
|
|
6
|
+
squad:
|
|
7
|
+
name: nested-test
|
|
8
|
+
domain: test
|
|
9
|
+
analysis:
|
|
10
|
+
entities: []
|
|
11
|
+
workflows: []
|
|
12
|
+
integrations: []
|
|
13
|
+
stakeholders: []
|
|
14
|
+
recommendations:
|
|
15
|
+
agents: []
|
|
16
|
+
tasks: []
|
|
17
|
+
template: basic
|
|
18
|
+
config_mode: extend
|
|
19
|
+
metadata:
|
|
20
|
+
created_at: '2025-12-18T23:01:55.321Z'
|
|
21
|
+
source_docs: []
|
|
22
|
+
user_adjustments: 0
|
|
23
|
+
overall_confidence: 0.5
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Squad Design Blueprint
|
|
2
|
+
# Generated by *design-squad
|
|
3
|
+
# Source: Interactive input
|
|
4
|
+
# Created: 2025-12-18T23:01:55.317Z
|
|
5
|
+
|
|
6
|
+
squad:
|
|
7
|
+
name: test-squad
|
|
8
|
+
domain: test-domain
|
|
9
|
+
analysis:
|
|
10
|
+
entities: []
|
|
11
|
+
workflows: []
|
|
12
|
+
integrations: []
|
|
13
|
+
stakeholders: []
|
|
14
|
+
recommendations:
|
|
15
|
+
agents: []
|
|
16
|
+
tasks: []
|
|
17
|
+
template: basic
|
|
18
|
+
config_mode: extend
|
|
19
|
+
metadata:
|
|
20
|
+
created_at: '2025-12-18T23:01:55.317Z'
|
|
21
|
+
source_docs: []
|
|
22
|
+
user_adjustments: 0
|
|
23
|
+
overall_confidence: 0.5
|