aios-core 4.0.4 → 4.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.
Files changed (107) 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 +210 -10
  34. package/.aios-core/data/registry-update-log.jsonl +52 -0
  35. package/.aios-core/development/agents/architect.md +10 -10
  36. package/.aios-core/development/agents/devops.md +93 -50
  37. package/.aios-core/development/agents/qa.md +94 -40
  38. package/.aios-core/development/agents/ux-design-expert.md +25 -25
  39. package/.aios-core/development/scripts/activation-runtime.js +63 -0
  40. package/.aios-core/development/scripts/generate-greeting.js +9 -8
  41. package/.aios-core/development/scripts/unified-activation-pipeline.js +102 -2
  42. package/.aios-core/development/tasks/{db-expansion-pack-integration.md → db-squad-integration.md} +5 -5
  43. package/.aios-core/development/tasks/{integrate-expansion-pack.md → integrate-squad.md} +2 -2
  44. package/.aios-core/development/tasks/next.md +3 -3
  45. package/.aios-core/development/tasks/pr-automation.md +2 -2
  46. package/.aios-core/development/tasks/publish-npm.md +257 -0
  47. package/.aios-core/development/tasks/release-management.md +4 -4
  48. package/.aios-core/development/tasks/setup-github.md +1 -1
  49. package/.aios-core/development/tasks/squad-creator-migrate.md +1 -1
  50. package/.aios-core/development/tasks/squad-creator-sync-ide-command.md +14 -14
  51. package/.aios-core/development/tasks/update-aios.md +1 -1
  52. package/.aios-core/development/tasks/validate-next-story.md +99 -2
  53. package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-QUICK-REFERENCE.md +1 -1
  54. package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-V2.1.md +5 -5
  55. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +21 -21
  56. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +25 -25
  57. package/.aios-core/docs/standards/OPEN-SOURCE-VS-SERVICE-DIFFERENCES.md +4 -4
  58. package/.aios-core/docs/standards/QUALITY-GATES-SPECIFICATION.md +3 -3
  59. package/.aios-core/docs/standards/STANDARDS-INDEX.md +13 -13
  60. package/.aios-core/docs/standards/STORY-TEMPLATE-V2-SPECIFICATION.md +1 -1
  61. package/.aios-core/framework-config.yaml +4 -0
  62. package/.aios-core/infrastructure/scripts/codex-skills-sync/index.js +182 -0
  63. package/.aios-core/infrastructure/scripts/codex-skills-sync/validate.js +172 -0
  64. package/.aios-core/infrastructure/scripts/ide-sync/README.md +14 -0
  65. package/.aios-core/infrastructure/scripts/ide-sync/index.js +6 -0
  66. package/.aios-core/infrastructure/scripts/tool-resolver.js +4 -4
  67. package/.aios-core/infrastructure/scripts/validate-paths.js +142 -0
  68. package/.aios-core/infrastructure/templates/aios-sync.yaml.template +11 -11
  69. package/.aios-core/infrastructure/templates/github-workflows/README.md +1 -1
  70. package/.aios-core/install-manifest.yaml +193 -109
  71. package/.aios-core/local-config.yaml.template +2 -0
  72. package/.aios-core/manifests/agents.csv +29 -1
  73. package/.aios-core/manifests/tasks.csv +80 -3
  74. package/.aios-core/product/README.md +2 -2
  75. package/.aios-core/product/data/integration-patterns.md +1 -1
  76. package/.aios-core/product/templates/ide-rules/cline-rules.md +1 -1
  77. package/.aios-core/product/templates/ide-rules/codex-rules.md +65 -0
  78. package/.aios-core/product/templates/ide-rules/copilot-rules.md +1 -1
  79. package/.aios-core/product/templates/ide-rules/roo-rules.md +1 -1
  80. package/.aios-core/user-guide.md +15 -14
  81. package/.aios-core/workflow-intelligence/engine/output-formatter.js +1 -1
  82. package/.claude/hooks/synapse-engine.js +9 -20
  83. package/README.md +14 -7
  84. package/bin/aios-init.js +255 -184
  85. package/bin/aios-minimal.js +2 -2
  86. package/bin/aios.js +4 -4
  87. package/package.json +6 -1
  88. package/packages/aios-pro-cli/bin/aios-pro.js +75 -2
  89. package/packages/aios-pro-cli/package.json +5 -1
  90. package/packages/aios-pro-cli/src/recover.js +100 -0
  91. package/packages/installer/src/__tests__/performance-benchmark.js +382 -0
  92. package/packages/installer/src/config/ide-configs.js +12 -1
  93. package/packages/installer/src/config/templates/core-config-template.js +2 -2
  94. package/packages/installer/src/installer/aios-core-installer.js +2 -2
  95. package/packages/installer/src/installer/file-hasher.js +97 -0
  96. package/packages/installer/src/installer/post-install-validator.js +41 -1
  97. package/packages/installer/src/pro/pro-scaffolder.js +335 -0
  98. package/packages/installer/src/utils/aios-colors.js +2 -2
  99. package/packages/installer/src/wizard/feedback.js +1 -1
  100. package/packages/installer/src/wizard/ide-config-generator.js +2 -2
  101. package/packages/installer/src/wizard/index.js +58 -19
  102. package/packages/installer/src/wizard/pro-setup.js +931 -0
  103. package/packages/installer/src/wizard/questions.js +20 -14
  104. package/packages/installer/src/wizard/validators.js +1 -1
  105. package/scripts/code-intel-health-check.js +343 -0
  106. package/scripts/package-synapse.js +323 -0
  107. package/scripts/validate-package-completeness.js +317 -0
@@ -4,7 +4,7 @@
4
4
  * AIOS-FullStack Minimal Installation
5
5
  *
6
6
  * DEPRECATED (since v3.11.1, scheduled for removal in v5.0.0):
7
- * The --minimal mode was designed for expansion-packs which have been
7
+ * The --minimal mode was designed for squads which have been
8
8
  * replaced by the Squads system (OSR-8). This command now runs the
9
9
  * standard wizard through the main router.
10
10
  */
@@ -14,7 +14,7 @@ const path = require('path');
14
14
 
15
15
  // Show deprecation warning
16
16
  console.log('\n⚠️ DEPRECATION WARNING: aios-minimal is deprecated.');
17
- console.log(' The --minimal mode (expansion-packs) was replaced by Squads.');
17
+ console.log(' The --minimal mode (squads) was replaced by Squads.');
18
18
  console.log(' Running standard installation wizard instead.\n');
19
19
 
20
20
  // Get the path to the main router (aios.js)
package/bin/aios.js CHANGED
@@ -20,7 +20,7 @@ const command = args[0];
20
20
 
21
21
  // Helper: Run initialization wizard
22
22
  async function runWizard(options = {}) {
23
- // Use the new v2.1 wizard from packages/installer/src/wizard/index.js
23
+ // Use the v4 wizard from packages/installer/src/wizard/index.js
24
24
  const wizardPath = path.join(__dirname, '..', 'packages', 'installer', 'src', 'wizard', 'index.js');
25
25
 
26
26
  if (!fs.existsSync(wizardPath)) {
@@ -43,7 +43,7 @@ async function runWizard(options = {}) {
43
43
  }
44
44
 
45
45
  try {
46
- // Run the new v2.1 wizard with options
46
+ // Run the v4 wizard with options
47
47
  const { runWizard: executeWizard } = require(wizardPath);
48
48
  await executeWizard(options);
49
49
  } catch (error) {
@@ -633,7 +633,7 @@ Options:
633
633
  What gets removed:
634
634
  - .aios-core/ Framework core files
635
635
  - docs/stories/ Story files (if created by AIOS)
636
- - squads/ Squad expansion packs
636
+ - squads/ Squad definitions
637
637
  - .gitignore AIOS-added entries only
638
638
 
639
639
  What is preserved (with --keep-data):
@@ -702,7 +702,7 @@ async function runUninstall(options = {}) {
702
702
  // Items to remove
703
703
  const itemsToRemove = [
704
704
  { path: '.aios-core', description: 'Framework core' },
705
- { path: 'squads', description: 'Squad expansion packs' },
705
+ { path: 'squads', description: 'Squad definitions' },
706
706
  ];
707
707
 
708
708
  // Optionally remove .aios
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aios-core",
3
- "version": "4.0.4",
3
+ "version": "4.2.0",
4
4
  "description": "Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework",
5
5
  "bin": {
6
6
  "aios": "bin/aios.js",
@@ -39,6 +39,11 @@
39
39
  "sync:ide": "node .aios-core/infrastructure/scripts/ide-sync/index.js sync",
40
40
  "sync:ide:validate": "node .aios-core/infrastructure/scripts/ide-sync/index.js validate",
41
41
  "sync:ide:check": "node .aios-core/infrastructure/scripts/ide-sync/index.js validate --strict",
42
+ "sync:ide:codex": "node .aios-core/infrastructure/scripts/ide-sync/index.js sync --ide codex",
43
+ "sync:skills:codex": "node .aios-core/infrastructure/scripts/codex-skills-sync/index.js",
44
+ "sync:skills:codex:global": "node .aios-core/infrastructure/scripts/codex-skills-sync/index.js --global --global-only",
45
+ "validate:codex-skills": "node .aios-core/infrastructure/scripts/codex-skills-sync/validate.js --strict",
46
+ "validate:paths": "node .aios-core/infrastructure/scripts/validate-paths.js",
42
47
  "sync:ide:cursor": "node .aios-core/infrastructure/scripts/ide-sync/index.js sync --ide cursor",
43
48
  "sync:ide:windsurf": "node .aios-core/infrastructure/scripts/ide-sync/index.js sync --ide windsurf",
44
49
  "prepublishOnly": "npm run generate:manifest && npm run validate:manifest",
@@ -13,12 +13,14 @@
13
13
  * status Show license status
14
14
  * features List available pro features
15
15
  * validate Force online license revalidation
16
+ * recover Recover lost license key via email
16
17
  * help Show help
17
18
  */
18
19
 
19
20
  const { execSync, spawnSync } = require('child_process');
20
21
  const path = require('path');
21
22
  const fs = require('fs');
23
+ const { recoverLicense } = require('../src/recover');
22
24
 
23
25
  const PRO_PACKAGE = '@aios-fullstack/pro';
24
26
  const VERSION = require('../package.json').version;
@@ -76,6 +78,51 @@ function delegateToAios(subcommand) {
76
78
  process.exit(result.status ?? 0);
77
79
  }
78
80
 
81
+ /**
82
+ * Get value of a CLI argument (e.g., --key VALUE).
83
+ *
84
+ * @param {string} flag - Flag name (e.g., '--key')
85
+ * @returns {string|null} Value or null
86
+ */
87
+ function getArgValue(flag) {
88
+ const idx = args.indexOf(flag);
89
+ if (idx !== -1 && idx + 1 < args.length) {
90
+ return args[idx + 1];
91
+ }
92
+ return null;
93
+ }
94
+
95
+ /**
96
+ * Run the Pro Installation Wizard.
97
+ *
98
+ * @param {string} [key] - Pre-provided license key
99
+ */
100
+ function runProWizard(key) {
101
+ // Lazy import to avoid requiring installer when not needed
102
+ let proSetup;
103
+ try {
104
+ proSetup = require('../../installer/src/wizard/pro-setup');
105
+ } catch {
106
+ console.error('Pro wizard module not found.');
107
+ console.error('Ensure aios-core installer is available.\n');
108
+ process.exit(1);
109
+ }
110
+
111
+ const options = {};
112
+ if (key) {
113
+ options.key = key;
114
+ }
115
+
116
+ proSetup.runProWizard(options).then((result) => {
117
+ if (!result.success) {
118
+ process.exit(1);
119
+ }
120
+ }).catch((err) => {
121
+ console.error(`\n Wizard failed: ${err.message}\n`);
122
+ process.exit(1);
123
+ });
124
+ }
125
+
79
126
  // ─── Commands ───────────────────────────────────────────────────────────────
80
127
 
81
128
  function showHelp() {
@@ -87,17 +134,23 @@ Usage:
87
134
 
88
135
  Commands:
89
136
  install Install ${PRO_PACKAGE} in the current project
137
+ install --wizard Install and run the setup wizard
138
+ setup, wizard Run Pro setup wizard (license gate + scaffold + verify)
90
139
  activate --key KEY Activate a license key
91
140
  deactivate Deactivate the current license
92
141
  status Show license status
93
142
  features List available pro features
94
143
  validate Force online license revalidation
144
+ recover Recover lost license key via email
95
145
  help Show this help message
96
146
 
97
147
  Examples:
98
148
  npx aios-pro install
149
+ npx aios-pro setup
150
+ npx aios-pro wizard --key PRO-XXXX-XXXX-XXXX-XXXX
99
151
  npx aios-pro activate --key PRO-XXXX-XXXX-XXXX-XXXX
100
152
  npx aios-pro status
153
+ npx aios-pro recover
101
154
 
102
155
  Documentation: https://synkra.ai/pro/docs
103
156
  `);
@@ -133,9 +186,29 @@ if (command === '--version' || command === '-v') {
133
186
  }
134
187
 
135
188
  switch (command) {
136
- case 'install':
137
- case 'setup':
189
+ case 'install': {
190
+ // Check for --wizard flag to run wizard after install
191
+ const runWizardAfter = args.includes('--wizard');
138
192
  installPro();
193
+ if (runWizardAfter) {
194
+ runProWizard();
195
+ }
196
+ break;
197
+ }
198
+
199
+ case 'setup':
200
+ case 'wizard': {
201
+ // Run the Pro Installation Wizard with license gate
202
+ const wizardKey = getArgValue('--key');
203
+ runProWizard(wizardKey);
204
+ break;
205
+ }
206
+
207
+ case 'recover':
208
+ recoverLicense().catch((err) => {
209
+ console.error(`\n Recovery failed: ${err.message}\n`);
210
+ process.exit(1);
211
+ });
139
212
  break;
140
213
 
141
214
  case 'activate':
@@ -6,8 +6,12 @@
6
6
  "aios-pro": "bin/aios-pro.js"
7
7
  },
8
8
  "files": [
9
- "bin/"
9
+ "bin/",
10
+ "src/"
10
11
  ],
12
+ "dependencies": {
13
+ "open": "^10.0.0"
14
+ },
11
15
  "keywords": [
12
16
  "aios",
13
17
  "pro",
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * License Recovery Flow
5
+ *
6
+ * Implements OWASP-compliant license key recovery via portal redirect.
7
+ * Decision 3 (D1 Section 4): Portal-first — CLI redirects to web portal.
8
+ *
9
+ * Security:
10
+ * - Anti-enumeration: identical message for any email input
11
+ * - No API calls — purely local CLI + browser redirect
12
+ * - Rate limiting is server-side (3/email/hour) — documented, not enforced here
13
+ */
14
+
15
+ const readline = require('readline');
16
+
17
+ const RECOVERY_URL = 'https://pro.synkra.ai/recover';
18
+
19
+ const RECOVERY_MESSAGE =
20
+ 'Se este email estiver associado a uma licenca, voce recebera instrucoes de recuperacao.';
21
+
22
+ /**
23
+ * Mask email for display: u***@example.com
24
+ * @param {string} email
25
+ * @returns {string}
26
+ */
27
+ function maskEmail(email) {
28
+ const atIndex = email.indexOf('@');
29
+ if (atIndex <= 0) {
30
+ return '***';
31
+ }
32
+ return email[0] + '***' + email.slice(atIndex);
33
+ }
34
+
35
+ /**
36
+ * Prompt user for email input via readline
37
+ * @returns {Promise<string>}
38
+ */
39
+ function promptEmail() {
40
+ const rl = readline.createInterface({
41
+ input: process.stdin,
42
+ output: process.stdout,
43
+ });
44
+
45
+ return new Promise((resolve) => {
46
+ rl.question(' Enter your email: ', (answer) => {
47
+ rl.close();
48
+ resolve(answer.trim());
49
+ });
50
+ });
51
+ }
52
+
53
+ /**
54
+ * Open URL in default browser with offline fallback
55
+ * @param {string} url
56
+ * @param {Function} [openFn] - Optional override for testing (defaults to dynamic import of 'open')
57
+ * @returns {Promise<boolean>} true if browser opened, false if fallback
58
+ */
59
+ async function openBrowser(url, openFn) {
60
+ try {
61
+ const open = openFn || (await import('open')).default;
62
+ await open(url);
63
+ return true;
64
+ } catch {
65
+ return false;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Main recovery flow
71
+ * @returns {Promise<void>}
72
+ */
73
+ async function recoverLicense() {
74
+ console.log('\naios-pro — License Recovery\n');
75
+
76
+ const email = await promptEmail();
77
+
78
+ if (!email) {
79
+ console.error('\n Error: Email is required.\n');
80
+ process.exit(1);
81
+ }
82
+
83
+ const masked = maskEmail(email);
84
+
85
+ // Anti-enumeration: identical message regardless of email validity
86
+ console.log(`\n ${RECOVERY_MESSAGE}`);
87
+ console.log(` Email: ${masked}\n`);
88
+
89
+ const browserOpened = await openBrowser(RECOVERY_URL);
90
+
91
+ if (browserOpened) {
92
+ console.log(' Recovery portal opened in your browser.');
93
+ } else {
94
+ console.log(' Could not open browser automatically.');
95
+ }
96
+
97
+ console.log(` You can also visit: ${RECOVERY_URL}\n`);
98
+ }
99
+
100
+ module.exports = { recoverLicense, maskEmail, promptEmail, openBrowser, RECOVERY_URL, RECOVERY_MESSAGE };
@@ -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
+ });