aios-core 4.0.2 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.aios-core/cli/commands/migrate/analyze.js +6 -6
  2. package/.aios-core/cli/commands/migrate/backup.js +2 -2
  3. package/.aios-core/cli/commands/migrate/execute.js +4 -4
  4. package/.aios-core/cli/commands/migrate/index.js +5 -5
  5. package/.aios-core/cli/commands/migrate/rollback.js +6 -6
  6. package/.aios-core/cli/commands/migrate/update-imports.js +2 -2
  7. package/.aios-core/cli/commands/migrate/validate.js +2 -2
  8. package/.aios-core/cli/commands/pro/index.js +52 -0
  9. package/.aios-core/cli/index.js +1 -1
  10. package/.aios-core/core/ids/registry-updater.js +29 -3
  11. package/.aios-core/core/migration/migration-config.yaml +2 -2
  12. package/.aios-core/core/migration/module-mapping.yaml +2 -2
  13. package/.aios-core/core/registry/README.md +2 -2
  14. package/.aios-core/core/synapse/context/context-builder.js +34 -0
  15. package/.aios-core/core/synapse/diagnostics/collectors/consistency-collector.js +168 -0
  16. package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +129 -0
  17. package/.aios-core/core/synapse/diagnostics/collectors/manifest-collector.js +82 -0
  18. package/.aios-core/core/synapse/diagnostics/collectors/output-analyzer.js +134 -0
  19. package/.aios-core/core/synapse/diagnostics/collectors/pipeline-collector.js +75 -0
  20. package/.aios-core/core/synapse/diagnostics/collectors/quality-collector.js +252 -0
  21. package/.aios-core/core/synapse/diagnostics/collectors/relevance-matrix.js +174 -0
  22. package/.aios-core/core/synapse/diagnostics/collectors/safe-read-json.js +31 -0
  23. package/.aios-core/core/synapse/diagnostics/collectors/session-collector.js +102 -0
  24. package/.aios-core/core/synapse/diagnostics/collectors/timing-collector.js +126 -0
  25. package/.aios-core/core/synapse/diagnostics/collectors/uap-collector.js +83 -0
  26. package/.aios-core/core/synapse/diagnostics/report-formatter.js +484 -0
  27. package/.aios-core/core/synapse/diagnostics/synapse-diagnostics.js +95 -0
  28. package/.aios-core/core/synapse/engine.js +73 -20
  29. package/.aios-core/core/synapse/runtime/hook-runtime.js +60 -0
  30. package/.aios-core/core-config.yaml +6 -0
  31. package/.aios-core/data/agent-config-requirements.yaml +2 -2
  32. package/.aios-core/data/aios-kb.md +4 -4
  33. package/.aios-core/data/entity-registry.yaml +5 -5
  34. package/.aios-core/development/agents/architect.md +10 -10
  35. package/.aios-core/development/agents/devops.md +93 -50
  36. package/.aios-core/development/agents/qa.md +94 -40
  37. package/.aios-core/development/agents/ux-design-expert.md +25 -25
  38. package/.aios-core/development/scripts/activation-runtime.js +63 -0
  39. package/.aios-core/development/scripts/generate-greeting.js +9 -8
  40. package/.aios-core/development/scripts/unified-activation-pipeline.js +102 -2
  41. package/.aios-core/development/tasks/{db-expansion-pack-integration.md → db-squad-integration.md} +5 -5
  42. package/.aios-core/development/tasks/{integrate-expansion-pack.md → integrate-squad.md} +2 -2
  43. package/.aios-core/development/tasks/next.md +3 -3
  44. package/.aios-core/development/tasks/pr-automation.md +2 -2
  45. package/.aios-core/development/tasks/publish-npm.md +257 -0
  46. package/.aios-core/development/tasks/release-management.md +4 -4
  47. package/.aios-core/development/tasks/setup-github.md +1 -1
  48. package/.aios-core/development/tasks/squad-creator-migrate.md +1 -1
  49. package/.aios-core/development/tasks/squad-creator-sync-ide-command.md +14 -14
  50. package/.aios-core/development/tasks/update-aios.md +1 -1
  51. package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-QUICK-REFERENCE.md +1 -1
  52. package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-V2.1.md +5 -5
  53. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +21 -21
  54. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +25 -25
  55. package/.aios-core/docs/standards/OPEN-SOURCE-VS-SERVICE-DIFFERENCES.md +4 -4
  56. package/.aios-core/docs/standards/QUALITY-GATES-SPECIFICATION.md +3 -3
  57. package/.aios-core/docs/standards/STANDARDS-INDEX.md +13 -13
  58. package/.aios-core/docs/standards/STORY-TEMPLATE-V2-SPECIFICATION.md +1 -1
  59. package/.aios-core/framework-config.yaml +4 -0
  60. package/.aios-core/infrastructure/scripts/codex-skills-sync/index.js +182 -0
  61. package/.aios-core/infrastructure/scripts/codex-skills-sync/validate.js +172 -0
  62. package/.aios-core/infrastructure/scripts/ide-sync/README.md +14 -0
  63. package/.aios-core/infrastructure/scripts/ide-sync/index.js +6 -0
  64. package/.aios-core/infrastructure/scripts/tool-resolver.js +4 -4
  65. package/.aios-core/infrastructure/scripts/validate-paths.js +142 -0
  66. package/.aios-core/infrastructure/templates/aios-sync.yaml.template +11 -11
  67. package/.aios-core/infrastructure/templates/github-workflows/README.md +1 -1
  68. package/.aios-core/install-manifest.yaml +190 -106
  69. package/.aios-core/local-config.yaml.template +2 -0
  70. package/.aios-core/product/README.md +2 -2
  71. package/.aios-core/product/data/integration-patterns.md +1 -1
  72. package/.aios-core/product/templates/ide-rules/cline-rules.md +1 -1
  73. package/.aios-core/product/templates/ide-rules/codex-rules.md +65 -0
  74. package/.aios-core/product/templates/ide-rules/copilot-rules.md +1 -1
  75. package/.aios-core/product/templates/ide-rules/roo-rules.md +1 -1
  76. package/.aios-core/user-guide.md +15 -14
  77. package/.aios-core/workflow-intelligence/engine/output-formatter.js +1 -1
  78. package/.claude/hooks/enforce-architecture-first.py +196 -0
  79. package/.claude/hooks/install-hooks.sh +41 -0
  80. package/.claude/hooks/mind-clone-governance.py +192 -0
  81. package/.claude/hooks/pre-commit-mmos-guard.sh +99 -0
  82. package/.claude/hooks/pre-commit-version-check.sh +156 -0
  83. package/.claude/hooks/read-protection.py +151 -0
  84. package/.claude/hooks/slug-validation.py +176 -0
  85. package/.claude/hooks/sql-governance.py +182 -0
  86. package/.claude/hooks/synapse-engine.js +9 -20
  87. package/.claude/hooks/write-path-validation.py +194 -0
  88. package/README.md +44 -14
  89. package/bin/aios-init.js +255 -184
  90. package/bin/aios-minimal.js +2 -2
  91. package/bin/aios.js +19 -19
  92. package/package.json +7 -4
  93. package/packages/aios-pro-cli/bin/aios-pro.js +75 -2
  94. package/packages/aios-pro-cli/package.json +5 -1
  95. package/packages/aios-pro-cli/src/recover.js +100 -0
  96. package/packages/installer/src/__tests__/performance-benchmark.js +382 -0
  97. package/packages/installer/src/config/ide-configs.js +12 -1
  98. package/packages/installer/src/config/templates/core-config-template.js +2 -2
  99. package/packages/installer/src/installer/aios-core-installer.js +2 -2
  100. package/packages/installer/src/installer/file-hasher.js +97 -0
  101. package/packages/installer/src/installer/post-install-validator.js +41 -1
  102. package/packages/installer/src/pro/pro-scaffolder.js +335 -0
  103. package/packages/installer/src/utils/aios-colors.js +2 -2
  104. package/packages/installer/src/wizard/feedback.js +1 -1
  105. package/packages/installer/src/wizard/ide-config-generator.js +2 -2
  106. package/packages/installer/src/wizard/index.js +58 -19
  107. package/packages/installer/src/wizard/pro-setup.js +547 -0
  108. package/packages/installer/src/wizard/questions.js +20 -14
  109. package/packages/installer/src/wizard/validators.js +1 -1
  110. package/scripts/package-synapse.js +323 -0
  111. package/scripts/validate-package-completeness.js +317 -0
@@ -0,0 +1,382 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * AIOS Installer Performance Benchmark
5
+ * Story INS-2: Installer Performance Optimization
6
+ *
7
+ * Measures baseline performance metrics for the installer to track optimization progress.
8
+ *
9
+ * Usage:
10
+ * node performance-benchmark.js [--output <file>] [--runs <n>]
11
+ *
12
+ * Output:
13
+ * JSON report with phase timings and statistics
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const fse = require('fs-extra');
18
+ const path = require('path');
19
+ const crypto = require('crypto');
20
+ const { performance } = require('perf_hooks');
21
+
22
+ // Configuration
23
+ const CONFIG = {
24
+ runs: 3, // Number of runs for averaging
25
+ outputFile: null, // Output file path (null = stdout)
26
+ testProjectSize: 1000, // Number of files for test project
27
+ verbose: false,
28
+ };
29
+
30
+ // Parse CLI arguments
31
+ process.argv.slice(2).forEach((arg, i, arr) => {
32
+ if (arg === '--output' && arr[i + 1]) CONFIG.outputFile = arr[i + 1];
33
+ if (arg === '--runs' && arr[i + 1]) CONFIG.runs = parseInt(arr[i + 1], 10);
34
+ if (arg === '--verbose' || arg === '-v') CONFIG.verbose = true;
35
+ if (arg === '--help' || arg === '-h') {
36
+ console.log(`
37
+ AIOS Installer Performance Benchmark
38
+
39
+ Usage: node performance-benchmark.js [options]
40
+
41
+ Options:
42
+ --output <file> Save JSON report to file (default: stdout)
43
+ --runs <n> Number of benchmark runs (default: 3)
44
+ --verbose, -v Show detailed progress
45
+ --help, -h Show this help
46
+
47
+ Example:
48
+ node performance-benchmark.js --output baseline.json --runs 5
49
+ `);
50
+ process.exit(0);
51
+ }
52
+ });
53
+
54
+ // Benchmark results structure
55
+ const results = {
56
+ timestamp: new Date().toISOString(),
57
+ system: {
58
+ platform: process.platform,
59
+ arch: process.arch,
60
+ nodeVersion: process.version,
61
+ cpus: require('os').cpus().length,
62
+ totalMemory: Math.round(require('os').totalmem() / 1024 / 1024) + ' MB',
63
+ },
64
+ config: { ...CONFIG },
65
+ phases: {},
66
+ summary: {},
67
+ };
68
+
69
+ /**
70
+ * Timer utility for measuring phase durations
71
+ */
72
+ class Timer {
73
+ constructor(name) {
74
+ this.name = name;
75
+ this.start = null;
76
+ this.end = null;
77
+ this.runs = [];
78
+ }
79
+
80
+ begin() {
81
+ this.start = performance.now();
82
+ }
83
+
84
+ stop() {
85
+ this.end = performance.now();
86
+ const duration = this.end - this.start;
87
+ this.runs.push(duration);
88
+ return duration;
89
+ }
90
+
91
+ getStats() {
92
+ if (this.runs.length === 0) return null;
93
+ const sorted = [...this.runs].sort((a, b) => a - b);
94
+ return {
95
+ min: Math.round(sorted[0]),
96
+ max: Math.round(sorted[sorted.length - 1]),
97
+ avg: Math.round(this.runs.reduce((a, b) => a + b, 0) / this.runs.length),
98
+ median: Math.round(sorted[Math.floor(sorted.length / 2)]),
99
+ runs: this.runs.map((r) => Math.round(r)),
100
+ unit: 'ms',
101
+ };
102
+ }
103
+ }
104
+
105
+ // Phase timers
106
+ const timers = {
107
+ directoryRead: new Timer('Directory Read (readdirSync)'),
108
+ directoryReadWithTypes: new Timer('Directory Read (withFileTypes)'),
109
+ statLoop: new Timer('Stat Loop (statSync per file)'),
110
+ realpathSingle: new Timer('Realpath (single call)'),
111
+ realpathDouble: new Timer('Realpath (double call - current)'),
112
+ hashSequential: new Timer('Hash Files (sequential)'),
113
+ hashParallelBatch: new Timer('Hash Files (parallel batch)'),
114
+ fileCopySequential: new Timer('File Copy (sequential)'),
115
+ fileCopyParallel: new Timer('File Copy (parallel)'),
116
+ totalInstallSimulation: new Timer('Total Install Simulation'),
117
+ };
118
+
119
+ /**
120
+ * Log if verbose mode is enabled
121
+ */
122
+ function log(msg) {
123
+ if (CONFIG.verbose) console.log(`[benchmark] ${msg}`);
124
+ }
125
+
126
+ /**
127
+ * Get the .aios-core directory for benchmarking
128
+ */
129
+ function getAiosCoreDir() {
130
+ const projectRoot = path.resolve(__dirname, '../../../../');
131
+ return path.join(projectRoot, '.aios-core');
132
+ }
133
+
134
+ /**
135
+ * Benchmark: Directory read comparison
136
+ */
137
+ async function benchmarkDirectoryRead(dir) {
138
+ const files = fs.readdirSync(dir);
139
+
140
+ // Method 1: readdirSync + statSync for each
141
+ timers.statLoop.begin();
142
+ for (const file of files) {
143
+ const fullPath = path.join(dir, file);
144
+ fs.statSync(fullPath).isDirectory();
145
+ }
146
+ timers.statLoop.stop();
147
+
148
+ // Method 2: readdirSync with withFileTypes
149
+ timers.directoryReadWithTypes.begin();
150
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
151
+ for (const entry of entries) {
152
+ entry.isDirectory();
153
+ }
154
+ timers.directoryReadWithTypes.stop();
155
+
156
+ return files.length;
157
+ }
158
+
159
+ /**
160
+ * Benchmark: Realpath comparison
161
+ */
162
+ async function benchmarkRealpath(files) {
163
+ const sampleFiles = files.slice(0, 100); // Sample 100 files
164
+
165
+ // Method 1: Single realpath call
166
+ timers.realpathSingle.begin();
167
+ for (const file of sampleFiles) {
168
+ fs.realpathSync(file);
169
+ }
170
+ timers.realpathSingle.stop();
171
+
172
+ // Method 2: Double realpath call (current behavior)
173
+ timers.realpathDouble.begin();
174
+ for (const file of sampleFiles) {
175
+ fs.realpathSync(file);
176
+ fs.realpathSync(path.dirname(file)); // Simulates the duplicate call
177
+ }
178
+ timers.realpathDouble.stop();
179
+ }
180
+
181
+ /**
182
+ * Benchmark: File hashing comparison
183
+ */
184
+ async function benchmarkHashing(files) {
185
+ const sampleFiles = files.slice(0, 200); // Sample 200 files for hashing
186
+
187
+ // Method 1: Sequential hashing
188
+ timers.hashSequential.begin();
189
+ for (const file of sampleFiles) {
190
+ try {
191
+ const content = fs.readFileSync(file);
192
+ crypto.createHash('sha256').update(content).digest('hex');
193
+ } catch {
194
+ // Skip files that can't be read
195
+ }
196
+ }
197
+ timers.hashSequential.stop();
198
+
199
+ // Method 2: Parallel batch hashing
200
+ timers.hashParallelBatch.begin();
201
+ const batchSize = 50;
202
+ for (let i = 0; i < sampleFiles.length; i += batchSize) {
203
+ const batch = sampleFiles.slice(i, i + batchSize);
204
+ await Promise.all(
205
+ batch.map(async (file) => {
206
+ try {
207
+ const content = await fse.readFile(file);
208
+ crypto.createHash('sha256').update(content).digest('hex');
209
+ } catch {
210
+ // Skip files that can't be read
211
+ }
212
+ }),
213
+ );
214
+ }
215
+ timers.hashParallelBatch.stop();
216
+ }
217
+
218
+ /**
219
+ * Collect all files recursively
220
+ */
221
+ function collectFiles(dir, maxFiles = 1000) {
222
+ const files = [];
223
+
224
+ function walk(currentDir) {
225
+ if (files.length >= maxFiles) return;
226
+
227
+ try {
228
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
229
+ for (const entry of entries) {
230
+ if (files.length >= maxFiles) break;
231
+
232
+ const fullPath = path.join(currentDir, entry.name);
233
+ if (entry.isDirectory()) {
234
+ walk(fullPath);
235
+ } else if (entry.isFile()) {
236
+ files.push(fullPath);
237
+ }
238
+ }
239
+ } catch {
240
+ // Skip directories we can't read
241
+ }
242
+ }
243
+
244
+ walk(dir);
245
+ return files;
246
+ }
247
+
248
+ /**
249
+ * Run all benchmarks
250
+ */
251
+ async function runBenchmarks() {
252
+ const aiosCoreDir = getAiosCoreDir();
253
+
254
+ if (!fs.existsSync(aiosCoreDir)) {
255
+ console.error(`Error: .aios-core directory not found at ${aiosCoreDir}`);
256
+ process.exit(1);
257
+ }
258
+
259
+ log(`Starting benchmark with ${CONFIG.runs} runs`);
260
+ log(`Using .aios-core at: ${aiosCoreDir}`);
261
+
262
+ // Collect files for benchmarking
263
+ log('Collecting files...');
264
+ const allFiles = collectFiles(aiosCoreDir, CONFIG.testProjectSize);
265
+ log(`Collected ${allFiles.length} files`);
266
+
267
+ results.fileCount = allFiles.length;
268
+
269
+ // Run benchmarks multiple times
270
+ for (let run = 1; run <= CONFIG.runs; run++) {
271
+ log(`\n--- Run ${run}/${CONFIG.runs} ---`);
272
+
273
+ // Directory read benchmarks
274
+ const agentsDir = path.join(aiosCoreDir, 'development', 'agents');
275
+ if (fs.existsSync(agentsDir)) {
276
+ log('Benchmarking directory read...');
277
+ await benchmarkDirectoryRead(agentsDir);
278
+ }
279
+
280
+ // Realpath benchmarks
281
+ log('Benchmarking realpath...');
282
+ await benchmarkRealpath(allFiles);
283
+
284
+ // Hashing benchmarks
285
+ log('Benchmarking file hashing...');
286
+ await benchmarkHashing(allFiles);
287
+
288
+ // Total simulation
289
+ log('Running total install simulation...');
290
+ timers.totalInstallSimulation.begin();
291
+
292
+ // Simulate full install: read dirs + hash files
293
+ const devDir = path.join(aiosCoreDir, 'development');
294
+ if (fs.existsSync(devDir)) {
295
+ const subdirs = fs.readdirSync(devDir, { withFileTypes: true });
296
+ for (const subdir of subdirs) {
297
+ if (subdir.isDirectory()) {
298
+ const fullSubdir = path.join(devDir, subdir.name);
299
+ fs.readdirSync(fullSubdir);
300
+ }
301
+ }
302
+ }
303
+
304
+ // Simulate sequential file processing
305
+ for (const file of allFiles.slice(0, 500)) {
306
+ try {
307
+ fs.statSync(file);
308
+ fs.readFileSync(file);
309
+ } catch {
310
+ // Skip
311
+ }
312
+ }
313
+
314
+ timers.totalInstallSimulation.stop();
315
+ }
316
+
317
+ // Compile results
318
+ log('\nCompiling results...');
319
+
320
+ for (const [name, timer] of Object.entries(timers)) {
321
+ const stats = timer.getStats();
322
+ if (stats) {
323
+ results.phases[name] = {
324
+ description: timer.name,
325
+ ...stats,
326
+ };
327
+ }
328
+ }
329
+
330
+ // Calculate summary
331
+ const hashSeq = results.phases.hashSequential?.avg || 0;
332
+ const hashPar = results.phases.hashParallelBatch?.avg || 0;
333
+ const realpathSingle = results.phases.realpathSingle?.avg || 0;
334
+ const realpathDouble = results.phases.realpathDouble?.avg || 0;
335
+ const statLoop = results.phases.statLoop?.avg || 0;
336
+ const withTypes = results.phases.directoryReadWithTypes?.avg || 0;
337
+
338
+ results.summary = {
339
+ totalFiles: results.fileCount,
340
+ hashingSpeedup: hashSeq > 0 ? `${(hashSeq / hashPar).toFixed(2)}x` : 'N/A',
341
+ realpathSavings: realpathDouble > 0 ? `${Math.round(((realpathDouble - realpathSingle) / realpathDouble) * 100)}%` : 'N/A',
342
+ statLoopSavings: statLoop > 0 ? `${Math.round(((statLoop - withTypes) / statLoop) * 100)}%` : 'N/A',
343
+ estimatedTotalTime: results.phases.totalInstallSimulation?.avg || 0,
344
+ target: '<30000ms for 1000 files',
345
+ baseline: `${results.phases.totalInstallSimulation?.avg || 'TBD'}ms`,
346
+ };
347
+
348
+ // Output results
349
+ const output = JSON.stringify(results, null, 2);
350
+
351
+ if (CONFIG.outputFile) {
352
+ fs.writeFileSync(CONFIG.outputFile, output);
353
+ console.log(`Benchmark results saved to: ${CONFIG.outputFile}`);
354
+ } else {
355
+ console.log(output);
356
+ }
357
+
358
+ // Print summary to stderr for visibility
359
+ console.error('\n' + '='.repeat(60));
360
+ console.error('AIOS Installer Performance Baseline');
361
+ console.error('='.repeat(60));
362
+ console.error(`Files analyzed: ${results.fileCount}`);
363
+ console.error(`Runs: ${CONFIG.runs}`);
364
+ console.error('');
365
+ console.error('Phase Results (avg ms):');
366
+ console.error(` Directory stat loop: ${statLoop}ms`);
367
+ console.error(` Directory withFileTypes: ${withTypes}ms (${results.summary.statLoopSavings} faster)`);
368
+ console.error(` Realpath single: ${realpathSingle}ms`);
369
+ console.error(` Realpath double: ${realpathDouble}ms (${results.summary.realpathSavings} overhead)`);
370
+ console.error(` Hash sequential: ${hashSeq}ms`);
371
+ console.error(` Hash parallel: ${hashPar}ms (${results.summary.hashingSpeedup} faster)`);
372
+ console.error('');
373
+ console.error(`Total Install Simulation: ${results.summary.baseline}`);
374
+ console.error(`Target: ${results.summary.target}`);
375
+ console.error('='.repeat(60));
376
+ }
377
+
378
+ // Run benchmarks
379
+ runBenchmarks().catch((err) => {
380
+ console.error('Benchmark failed:', err);
381
+ process.exit(1);
382
+ });
@@ -25,8 +25,9 @@ const path = require('path');
25
25
  /**
26
26
  * IDE Configuration Metadata
27
27
  *
28
- * Synkra AIOS v2.1 supports 5 main IDEs:
28
+ * Synkra AIOS v4 supports 6 main IDEs:
29
29
  * - Claude Code (Anthropic's official CLI) - Recommended
30
+ * - Codex CLI (OpenAI coding CLI)
30
31
  * - Cursor (AI-first code editor)
31
32
  * - Windsurf (AI-powered development)
32
33
  * - GitHub Copilot (GitHub's AI pair programmer)
@@ -43,6 +44,16 @@ const IDE_CONFIGS = {
43
44
  recommended: true,
44
45
  agentFolder: path.join('.claude', 'commands', 'AIOS', 'agents'),
45
46
  },
47
+ codex: {
48
+ name: 'Codex CLI',
49
+ description: '',
50
+ configFile: 'AGENTS.md',
51
+ template: 'ide-rules/codex-rules.md',
52
+ requiresDirectory: false,
53
+ format: 'text',
54
+ recommended: true,
55
+ agentFolder: path.join('.codex', 'agents'),
56
+ },
46
57
  cursor: {
47
58
  name: 'Cursor',
48
59
  description: '',
@@ -118,7 +118,7 @@ function generateCoreConfig(options = {}) {
118
118
  enabled: true,
119
119
  heavySections: [
120
120
  'pvMindContext',
121
- 'expansionPacks',
121
+ 'squads',
122
122
  'registry',
123
123
  ],
124
124
  },
@@ -146,7 +146,7 @@ function generateCoreConfig(options = {}) {
146
146
  scriptsLocation: '.aios-core/scripts',
147
147
  dataLocation: '.aios-core/data',
148
148
  elicitationLocation: '.aios-core/elicitation',
149
- expansionPacksLocation: 'expansion-packs',
149
+ squadsLocation: 'squads',
150
150
  mindsLocation: 'outputs/minds',
151
151
 
152
152
  // Project Status Configuration
@@ -24,11 +24,11 @@ function getAiosCoreSourcePath() {
24
24
 
25
25
  /**
26
26
  * Folders to copy from .aios-core
27
- * Includes both v2.1 modular structure and v2.0 legacy flat structure for compatibility
27
+ * Includes both v4 modular structure and v2.0 legacy flat structure for compatibility
28
28
  * @constant {string[]}
29
29
  */
30
30
  const FOLDERS_TO_COPY = [
31
- // v2.1 Modular Structure (Story 2.15)
31
+ // v4.0.4 Modular Structure (Story 2.15)
32
32
  'core', // Framework utilities, config, registry, migration
33
33
  'development', // Agents, tasks, workflows, scripts, personas
34
34
  'product', // Templates, checklists, cli, api
@@ -87,6 +87,100 @@ function hashFile(filePath) {
87
87
  return crypto.createHash('sha256').update(content).digest('hex');
88
88
  }
89
89
 
90
+ /**
91
+ * Async version of hashFile for parallel processing.
92
+ * INS-2 Performance: Enables parallel hashing with Promise.all
93
+ *
94
+ * @param {string} filePath - Absolute path to the file
95
+ * @returns {Promise<string>} - SHA256 hash as hex string
96
+ * @throws {Error} - If file cannot be read
97
+ */
98
+ async function hashFileAsync(filePath) {
99
+ const exists = await fs.pathExists(filePath);
100
+ if (!exists) {
101
+ throw new Error(`File not found: ${filePath}`);
102
+ }
103
+
104
+ const stats = await fs.stat(filePath);
105
+ if (stats.isDirectory()) {
106
+ throw new Error(`Cannot hash directory: ${filePath}`);
107
+ }
108
+
109
+ let content;
110
+
111
+ if (isBinaryFile(filePath)) {
112
+ // Binary files: hash raw bytes
113
+ content = await fs.readFile(filePath);
114
+ } else {
115
+ // Text files: normalize line endings and remove BOM
116
+ const rawContent = await fs.readFile(filePath, 'utf8');
117
+ const withoutBOM = removeBOM(rawContent);
118
+ const normalized = normalizeLineEndings(withoutBOM);
119
+ content = Buffer.from(normalized, 'utf8');
120
+ }
121
+
122
+ return crypto.createHash('sha256').update(content).digest('hex');
123
+ }
124
+
125
+ /**
126
+ * Async version of hashesMatch using hashFileAsync.
127
+ *
128
+ * @param {string} filePath1 - First file path
129
+ * @param {string} filePath2 - Second file path
130
+ * @returns {Promise<boolean>} - True if file hashes match
131
+ */
132
+ async function hashFilesMatchAsync(filePath1, filePath2) {
133
+ try {
134
+ const [hash1, hash2] = await Promise.all([
135
+ hashFileAsync(filePath1),
136
+ hashFileAsync(filePath2),
137
+ ]);
138
+ return hashesMatch(hash1, hash2);
139
+ } catch {
140
+ return false;
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Hash multiple files in parallel with batch processing
146
+ * INS-2 Performance: Process files in batches to prevent memory exhaustion
147
+ *
148
+ * @param {string[]} filePaths - Array of absolute file paths
149
+ * @param {number} batchSize - Number of files to hash concurrently (default: 50)
150
+ * @param {Function} onProgress - Optional progress callback (current, total)
151
+ * @returns {Promise<Map<string, string>>} - Map of filePath -> hash
152
+ */
153
+ async function hashFilesParallel(filePaths, batchSize = 50, onProgress = null) {
154
+ const results = new Map();
155
+ const total = filePaths.length;
156
+
157
+ for (let i = 0; i < total; i += batchSize) {
158
+ const batch = filePaths.slice(i, i + batchSize);
159
+ const batchResults = await Promise.all(
160
+ batch.map(async (filePath) => {
161
+ try {
162
+ const hash = await hashFileAsync(filePath);
163
+ return { filePath, hash, error: null };
164
+ } catch (error) {
165
+ return { filePath, hash: null, error: error.message };
166
+ }
167
+ }),
168
+ );
169
+
170
+ for (const result of batchResults) {
171
+ if (result.hash) {
172
+ results.set(result.filePath, result.hash);
173
+ }
174
+ }
175
+
176
+ if (onProgress) {
177
+ onProgress(Math.min(i + batchSize, total), total);
178
+ }
179
+ }
180
+
181
+ return results;
182
+ }
183
+
90
184
  /**
91
185
  * Compute SHA256 hash of a string (for manifest integrity)
92
186
  * @param {string} content - String content to hash
@@ -127,8 +221,11 @@ function getFileMetadata(filePath, basePath) {
127
221
 
128
222
  module.exports = {
129
223
  hashFile,
224
+ hashFileAsync,
225
+ hashFilesParallel,
130
226
  hashString,
131
227
  hashesMatch,
228
+ hashFilesMatchAsync,
132
229
  getFileMetadata,
133
230
  isBinaryFile,
134
231
  normalizeLineEndings,
@@ -359,6 +359,27 @@ class PostInstallValidator {
359
359
  extraFiles: 0,
360
360
  skippedFiles: 0,
361
361
  };
362
+
363
+ // INS-2 Performance: Cache realpath of target directory (computed once, used for all files)
364
+ // This eliminates redundant fs.realpathSync() calls - 50% reduction in syscalls
365
+ this._realTargetDirCache = null;
366
+ }
367
+
368
+ /**
369
+ * Get the real path of the target directory (cached)
370
+ * INS-2 Performance Optimization: Reduces syscalls from 2 to 1 per file validation
371
+ * @returns {string|null} - Real path or null if resolution fails
372
+ */
373
+ _getRealTargetDir() {
374
+ if (this._realTargetDirCache === null) {
375
+ try {
376
+ this._realTargetDirCache = fs.realpathSync(this.aiosCoreTarget);
377
+ } catch {
378
+ // Will be handled by caller
379
+ return null;
380
+ }
381
+ }
382
+ return this._realTargetDirCache;
362
383
  }
363
384
 
364
385
  /**
@@ -681,9 +702,28 @@ class PostInstallValidator {
681
702
  // both the file path AND the target directory to their real paths, then
682
703
  // comparing containment. The key security check is: does the real path of the
683
704
  // file stay within the real path of the target directory?
705
+ //
706
+ // INS-2 Performance: realTargetDir is now cached via _getRealTargetDir()
707
+ // This reduces syscalls from 2 to 1 per file (50% reduction)
684
708
  try {
685
709
  const realPath = fs.realpathSync(absolutePath);
686
- const realTargetDir = fs.realpathSync(this.aiosCoreTarget);
710
+ const realTargetDir = this._getRealTargetDir();
711
+
712
+ // Handle case where target dir resolution failed
713
+ if (realTargetDir === null) {
714
+ this.log('SECURITY: Cannot resolve realpath for target directory');
715
+ result.issue = {
716
+ type: IssueType.PERMISSION_ERROR,
717
+ severity: Severity.CRITICAL,
718
+ message: 'Cannot resolve real path for target directory',
719
+ details: 'Target directory realpath resolution failed',
720
+ category,
721
+ remediation: 'Check directory permissions',
722
+ relativePath,
723
+ };
724
+ this.stats.skippedFiles++;
725
+ return result;
726
+ }
687
727
 
688
728
  // SECURITY: Verify realpath is still contained within REAL target directory
689
729
  // This handles system symlinks like /tmp -> /private/tmp correctly