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.
- package/.aios-core/cli/commands/migrate/analyze.js +6 -6
- package/.aios-core/cli/commands/migrate/backup.js +2 -2
- package/.aios-core/cli/commands/migrate/execute.js +4 -4
- package/.aios-core/cli/commands/migrate/index.js +5 -5
- package/.aios-core/cli/commands/migrate/rollback.js +6 -6
- package/.aios-core/cli/commands/migrate/update-imports.js +2 -2
- package/.aios-core/cli/commands/migrate/validate.js +2 -2
- package/.aios-core/cli/commands/pro/index.js +52 -0
- package/.aios-core/cli/index.js +1 -1
- package/.aios-core/core/ids/registry-updater.js +29 -3
- package/.aios-core/core/migration/migration-config.yaml +2 -2
- package/.aios-core/core/migration/module-mapping.yaml +2 -2
- package/.aios-core/core/registry/README.md +2 -2
- package/.aios-core/core/synapse/context/context-builder.js +34 -0
- package/.aios-core/core/synapse/diagnostics/collectors/consistency-collector.js +168 -0
- package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +129 -0
- package/.aios-core/core/synapse/diagnostics/collectors/manifest-collector.js +82 -0
- package/.aios-core/core/synapse/diagnostics/collectors/output-analyzer.js +134 -0
- package/.aios-core/core/synapse/diagnostics/collectors/pipeline-collector.js +75 -0
- package/.aios-core/core/synapse/diagnostics/collectors/quality-collector.js +252 -0
- package/.aios-core/core/synapse/diagnostics/collectors/relevance-matrix.js +174 -0
- package/.aios-core/core/synapse/diagnostics/collectors/safe-read-json.js +31 -0
- package/.aios-core/core/synapse/diagnostics/collectors/session-collector.js +102 -0
- package/.aios-core/core/synapse/diagnostics/collectors/timing-collector.js +126 -0
- package/.aios-core/core/synapse/diagnostics/collectors/uap-collector.js +83 -0
- package/.aios-core/core/synapse/diagnostics/report-formatter.js +484 -0
- package/.aios-core/core/synapse/diagnostics/synapse-diagnostics.js +95 -0
- package/.aios-core/core/synapse/engine.js +73 -20
- package/.aios-core/core/synapse/runtime/hook-runtime.js +60 -0
- package/.aios-core/core-config.yaml +6 -0
- package/.aios-core/data/agent-config-requirements.yaml +2 -2
- package/.aios-core/data/aios-kb.md +4 -4
- package/.aios-core/data/entity-registry.yaml +5 -5
- package/.aios-core/development/agents/architect.md +10 -10
- package/.aios-core/development/agents/devops.md +93 -50
- package/.aios-core/development/agents/qa.md +94 -40
- package/.aios-core/development/agents/ux-design-expert.md +25 -25
- package/.aios-core/development/scripts/activation-runtime.js +63 -0
- package/.aios-core/development/scripts/generate-greeting.js +9 -8
- package/.aios-core/development/scripts/unified-activation-pipeline.js +102 -2
- package/.aios-core/development/tasks/{db-expansion-pack-integration.md → db-squad-integration.md} +5 -5
- package/.aios-core/development/tasks/{integrate-expansion-pack.md → integrate-squad.md} +2 -2
- package/.aios-core/development/tasks/next.md +3 -3
- package/.aios-core/development/tasks/pr-automation.md +2 -2
- package/.aios-core/development/tasks/publish-npm.md +257 -0
- package/.aios-core/development/tasks/release-management.md +4 -4
- package/.aios-core/development/tasks/setup-github.md +1 -1
- package/.aios-core/development/tasks/squad-creator-migrate.md +1 -1
- package/.aios-core/development/tasks/squad-creator-sync-ide-command.md +14 -14
- package/.aios-core/development/tasks/update-aios.md +1 -1
- package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-QUICK-REFERENCE.md +1 -1
- package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-V2.1.md +5 -5
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +21 -21
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +25 -25
- package/.aios-core/docs/standards/OPEN-SOURCE-VS-SERVICE-DIFFERENCES.md +4 -4
- package/.aios-core/docs/standards/QUALITY-GATES-SPECIFICATION.md +3 -3
- package/.aios-core/docs/standards/STANDARDS-INDEX.md +13 -13
- package/.aios-core/docs/standards/STORY-TEMPLATE-V2-SPECIFICATION.md +1 -1
- package/.aios-core/framework-config.yaml +4 -0
- package/.aios-core/infrastructure/scripts/codex-skills-sync/index.js +182 -0
- package/.aios-core/infrastructure/scripts/codex-skills-sync/validate.js +172 -0
- package/.aios-core/infrastructure/scripts/ide-sync/README.md +14 -0
- package/.aios-core/infrastructure/scripts/ide-sync/index.js +6 -0
- package/.aios-core/infrastructure/scripts/tool-resolver.js +4 -4
- package/.aios-core/infrastructure/scripts/validate-paths.js +142 -0
- package/.aios-core/infrastructure/templates/aios-sync.yaml.template +11 -11
- package/.aios-core/infrastructure/templates/github-workflows/README.md +1 -1
- package/.aios-core/install-manifest.yaml +190 -106
- package/.aios-core/local-config.yaml.template +2 -0
- package/.aios-core/product/README.md +2 -2
- package/.aios-core/product/data/integration-patterns.md +1 -1
- package/.aios-core/product/templates/ide-rules/cline-rules.md +1 -1
- package/.aios-core/product/templates/ide-rules/codex-rules.md +65 -0
- package/.aios-core/product/templates/ide-rules/copilot-rules.md +1 -1
- package/.aios-core/product/templates/ide-rules/roo-rules.md +1 -1
- package/.aios-core/user-guide.md +15 -14
- package/.aios-core/workflow-intelligence/engine/output-formatter.js +1 -1
- package/.claude/hooks/enforce-architecture-first.py +196 -0
- package/.claude/hooks/install-hooks.sh +41 -0
- package/.claude/hooks/mind-clone-governance.py +192 -0
- package/.claude/hooks/pre-commit-mmos-guard.sh +99 -0
- package/.claude/hooks/pre-commit-version-check.sh +156 -0
- package/.claude/hooks/read-protection.py +151 -0
- package/.claude/hooks/slug-validation.py +176 -0
- package/.claude/hooks/sql-governance.py +182 -0
- package/.claude/hooks/synapse-engine.js +9 -20
- package/.claude/hooks/write-path-validation.py +194 -0
- package/README.md +44 -14
- package/bin/aios-init.js +255 -184
- package/bin/aios-minimal.js +2 -2
- package/bin/aios.js +19 -19
- package/package.json +7 -4
- package/packages/aios-pro-cli/bin/aios-pro.js +75 -2
- package/packages/aios-pro-cli/package.json +5 -1
- package/packages/aios-pro-cli/src/recover.js +100 -0
- package/packages/installer/src/__tests__/performance-benchmark.js +382 -0
- package/packages/installer/src/config/ide-configs.js +12 -1
- package/packages/installer/src/config/templates/core-config-template.js +2 -2
- package/packages/installer/src/installer/aios-core-installer.js +2 -2
- package/packages/installer/src/installer/file-hasher.js +97 -0
- package/packages/installer/src/installer/post-install-validator.js +41 -1
- package/packages/installer/src/pro/pro-scaffolder.js +335 -0
- package/packages/installer/src/utils/aios-colors.js +2 -2
- package/packages/installer/src/wizard/feedback.js +1 -1
- package/packages/installer/src/wizard/ide-config-generator.js +2 -2
- package/packages/installer/src/wizard/index.js +58 -19
- package/packages/installer/src/wizard/pro-setup.js +547 -0
- package/packages/installer/src/wizard/questions.js +20 -14
- package/packages/installer/src/wizard/validators.js +1 -1
- package/scripts/package-synapse.js +323 -0
- 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
|
|
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
|
-
'
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
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 =
|
|
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
|