aios-core 4.4.2 → 4.4.3

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.
@@ -1,9 +1,12 @@
1
1
  const fs = require('fs').promises;
2
2
  const path = require('path');
3
3
  const chalk = require('chalk');
4
- const { ESLint } = require('eslint');
5
- const prettier = require('prettier');
6
- const jscodeshift = require('jscodeshift');
4
+
5
+ // INS-4.12: Optional dev-time deps — wrap in try-catch for brownfield installs
6
+ let ESLint, prettier, jscodeshift;
7
+ try { ({ ESLint } = require('eslint')); } catch { ESLint = null; }
8
+ try { prettier = require('prettier'); } catch { prettier = null; }
9
+ try { jscodeshift = require('jscodeshift'); } catch { jscodeshift = null; }
7
10
 
8
11
  /**
9
12
  * Automated code quality improvement system
@@ -119,15 +122,22 @@ class CodeQualityImprover {
119
122
  * Initialize tools
120
123
  */
121
124
  async initialize() {
122
- // Initialize ESLint
123
- this.eslint = new ESLint({
124
- fix: true,
125
- baseConfig: await this.loadESLintConfig(),
126
- useEslintrc: true
127
- });
125
+ // INS-4.12: Guard optional deps — may be null in brownfield installs
126
+ if (ESLint) {
127
+ this.eslint = new ESLint({
128
+ fix: true,
129
+ baseConfig: await this.loadESLintConfig(),
130
+ useEslintrc: true
131
+ });
132
+ } else {
133
+ console.warn('⚠️ eslint not available — linting improvements disabled');
134
+ }
128
135
 
129
- // Load Prettier config
130
- this.prettierConfig = await this.loadPrettierConfig();
136
+ if (prettier) {
137
+ this.prettierConfig = await this.loadPrettierConfig();
138
+ } else {
139
+ console.warn('⚠️ prettier not available — formatting improvements disabled');
140
+ }
131
141
  }
132
142
 
133
143
  /**
@@ -231,6 +241,7 @@ class CodeQualityImprover {
231
241
 
232
242
  try {
233
243
  // Linting issues
244
+ if (!this.eslint) return metrics;
234
245
  const lintResults = await this.eslint.lintText(content, { _filePath });
235
246
  metrics.issues = lintResults[0]?.errorCount + lintResults[0]?.warningCount || 0;
236
247
 
@@ -254,9 +265,12 @@ class CodeQualityImprover {
254
265
 
255
266
  async improveFormatting(content, _filePath, options) {
256
267
  try {
268
+ if (!prettier) {
269
+ return { improved: false, content, changes: [] };
270
+ }
257
271
  const formatted = await prettier.format(content, {
258
272
  ...this.prettierConfig,
259
- filepath: filePath
273
+ filepath: _filePath
260
274
  });
261
275
 
262
276
  return {
@@ -276,6 +290,9 @@ class CodeQualityImprover {
276
290
 
277
291
  async improveLinting(content, _filePath, options) {
278
292
  try {
293
+ if (!this.eslint) {
294
+ return { improved: false, content, changes: [] };
295
+ }
279
296
  const results = await this.eslint.lintText(content, { _filePath });
280
297
 
281
298
  if (results[0]?.output) {
@@ -1,10 +1,13 @@
1
1
  const fs = require('fs').promises;
2
2
  const path = require('path');
3
3
  const chalk = require('chalk');
4
- const { parse } = require('@babel/parser');
5
- const traverse = require('@babel/traverse').default;
6
- const generate = require('@babel/generator').default;
7
- const _t = require('@babel/types');
4
+
5
+ // INS-4.12: Optional dev-time deps — wrap in try-catch for brownfield installs
6
+ let parse, traverse, generate, _t;
7
+ try { ({ parse } = require('@babel/parser')); } catch { parse = null; }
8
+ try { traverse = require('@babel/traverse').default; } catch { traverse = null; }
9
+ try { generate = require('@babel/generator').default; } catch { generate = null; }
10
+ try { _t = require('@babel/types'); } catch { _t = null; }
8
11
 
9
12
  /**
10
13
  * Automated refactoring suggestion system
@@ -118,12 +121,18 @@ class RefactoringSuggester {
118
121
  * Analyze code and suggest refactorings
119
122
  */
120
123
  async analyzeCode(filePath, options = {}) {
124
+ // INS-4.12: Guard — @babel deps may not be available in brownfield installs
125
+ if (!parse || !traverse) {
126
+ console.warn(chalk.yellow('⚠️ @babel/parser or @babel/traverse not available — refactoring analysis disabled'));
127
+ return { filePath, suggestions: [], error: '@babel dependencies not installed' };
128
+ }
129
+
121
130
  console.log(chalk.blue(`🔍 Analyzing: ${filePath}`));
122
-
131
+
123
132
  try {
124
133
  const _content = await fs.readFile(filePath, 'utf-8');
125
134
  const fileType = path.extname(filePath);
126
-
135
+
127
136
  if (!['.js', '.jsx', '.ts', '.tsx'].includes(fileType)) {
128
137
  return {
129
138
  filePath,
@@ -7,8 +7,8 @@
7
7
  # - SHA256 hashes for change detection
8
8
  # - File types for categorization
9
9
  #
10
- version: 4.4.2
11
- generated_at: "2026-02-24T16:53:34.099Z"
10
+ version: 4.4.3
11
+ generated_at: "2026-02-25T00:54:57.457Z"
12
12
  generator: scripts/generate-install-manifest.js
13
13
  file_count: 1084
14
14
  files:
@@ -333,9 +333,9 @@ files:
333
333
  type: core
334
334
  size: 674
335
335
  - path: core/doctor/checks/npm-packages.js
336
- hash: sha256:16f747d25d3185050c3b149d235dce707555038b51c0659e416a4d33a3672032
336
+ hash: sha256:09e03cd3558acbdd6b2f4103c156c76a3c07fa2f0b8996739bec7a865d781922
337
337
  type: core
338
- size: 702
338
+ size: 2265
339
339
  - path: core/doctor/checks/rules-files.js
340
340
  hash: sha256:6fe670f3759520f0d4b0abdad14f5f8b0eaa12cc9b15252c6de7e4cfa099d337
341
341
  type: core
@@ -1221,9 +1221,9 @@ files:
1221
1221
  type: data
1222
1222
  size: 9575
1223
1223
  - path: data/entity-registry.yaml
1224
- hash: sha256:1a3ebc7fb0c09631e18192e376903db69ef9e40ffc2a661ca87a100e6996051f
1224
+ hash: sha256:fb17c8d978fd7bb483a2752ba285718d86af234f0412ecdc6d123ec0d0634032
1225
1225
  type: data
1226
- size: 517912
1226
+ size: 519735
1227
1227
  - path: data/learned-patterns.yaml
1228
1228
  hash: sha256:24ac0b160615583a0ff783d3da8af80b7f94191575d6db2054ec8e10a3f945dc
1229
1229
  type: data
@@ -1465,9 +1465,9 @@ files:
1465
1465
  type: script
1466
1466
  size: 11536
1467
1467
  - path: development/scripts/code-quality-improver.js
1468
- hash: sha256:acdfea90590a2d0d566e720540a8aad4a360cd531c58ad4e67cc4126522b7455
1468
+ hash: sha256:d0c844089e53dcd6c06755d4cb432a60fbebcedcf5a86ed635650573549a1941
1469
1469
  type: script
1470
- size: 39827
1470
+ size: 40545
1471
1471
  - path: development/scripts/commit-message-generator.js
1472
1472
  hash: sha256:2e75d22307d0e3823b7762a6aff18c4c3842a632f876069215a221bc053336dc
1473
1473
  type: script
@@ -1565,9 +1565,9 @@ files:
1565
1565
  type: script
1566
1566
  size: 22558
1567
1567
  - path: development/scripts/refactoring-suggester.js
1568
- hash: sha256:d5183f79fae9dc4bf4d3c9136b3622e43a63643a7df622742c350931e45f18f4
1568
+ hash: sha256:d50ea6b609c9cf8385979386fee4b4385d11ebcde15460260f66d04c705f6cd9
1569
1569
  type: script
1570
- size: 34675
1570
+ size: 35227
1571
1571
  - path: development/scripts/rollback-handler.js
1572
1572
  hash: sha256:b18a9451fa3f8919733251857dbad2bc4b7ecbf782e6c114b88bc867358421a9
1573
1573
  type: script
@@ -3525,9 +3525,9 @@ files:
3525
3525
  type: monitor
3526
3526
  size: 818
3527
3527
  - path: package.json
3528
- hash: sha256:dff580c83cc1554c75162c9cabb711b5cd85e679c9c8f4968ee74499e99de0c0
3528
+ hash: sha256:ac6d72a82cecd2a98a443dc195e87cf815c2df58fb743737177670999a816b5f
3529
3529
  type: other
3530
- size: 2401
3530
+ size: 2495
3531
3531
  - path: product/checklists/accessibility-wcag-checklist.md
3532
3532
  hash: sha256:56126182b25e9b7bdde43f75315e33167eb49b1f9a9cb0e9a37bc068af40aeab
3533
3533
  type: checklist
@@ -31,16 +31,20 @@
31
31
  "typecheck": "tsc --noEmit"
32
32
  },
33
33
  "dependencies": {
34
+ "ajv": "^8.17.1",
34
35
  "chalk": "^4.1.2",
35
36
  "commander": "^12.1.0",
37
+ "diff": "^5.2.0",
38
+ "execa": "^5.1.1",
39
+ "fast-glob": "^3.3.3",
36
40
  "fs-extra": "^11.3.0",
37
41
  "glob": "^10.4.4",
38
- "js-yaml": "^4.1.0",
42
+ "highlight.js": "^11.9.0",
39
43
  "inquirer": "^8.2.6",
40
- "validator": "^13.15.15",
41
- "diff": "^5.2.0",
42
- "execa": "^5.1.1",
43
- "highlight.js": "^11.9.0"
44
+ "js-yaml": "^4.1.0",
45
+ "semver": "^7.7.2",
46
+ "tar": "^7.5.7",
47
+ "validator": "^13.15.15"
44
48
  },
45
49
  "peerDependencies": {
46
50
  "@aios-fullstack/memory": "^4.31.0",
package/README.md CHANGED
@@ -12,6 +12,8 @@
12
12
  [![Contributions Welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg)](CONTRIBUTING.md)
13
13
  [![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-Contributor%20Covenant-blue.svg)](CODE_OF_CONDUCT.md)
14
14
 
15
+ > 🌐 README por idioma: [EN](docs/README.md) | [PT](docs/pt/README.md) | [ES](docs/es/README.md) | [ZH](docs/zh/README.md)
16
+
15
17
  Framework de Desenvolvimento Auto-Modificável Alimentado por IA. Fundado em Desenvolvimento Ágil Dirigido por Agentes, oferecendo capacidades revolucionárias para desenvolvimento dirigido por IA e muito mais. Transforme qualquer domínio com expertise especializada de IA: desenvolvimento de software, entretenimento, escrita criativa, estratégia de negócios, bem-estar pessoal e muito mais.
16
18
 
17
19
  ## Comece Aqui (10 Min)
@@ -112,7 +114,7 @@ Esta abordagem de duas fases elimina tanto a **inconsistência de planejamento**
112
114
  - 📖 [Guia de Instalação para Windows](docs/installation/windows.md)
113
115
  - 📖 [Guia de Instalação para Linux](docs/installation/linux.md)
114
116
 
115
- **Documentação multilíngue disponível:** [Português](docs/pt/installation/) | [Español](docs/es/installation/)
117
+ **Documentação multilíngue disponível:** [English](docs/README.md) | [Português](docs/pt/README.md) | [Español](docs/es/README.md) | [中文](docs/zh/README.md)
116
118
 
117
119
  ## Navegação Rápida
118
120
 
@@ -2,13 +2,14 @@
2
2
  'use strict';
3
3
 
4
4
  /**
5
- * Publish Safety Gate — Submodule + File Count Validation
6
- * Story INS-4.10
5
+ * Publish Safety Gate — Submodule + File Count + Dependency Validation
6
+ * Story INS-4.10, INS-4.12
7
7
  *
8
8
  * Prevents publishing incomplete packages by validating:
9
9
  * 1. pro/ submodule is populated (not empty or uninitialized)
10
10
  * 2. Critical file pro/license/license-api.js exists
11
11
  * 3. Package file count meets minimum threshold (>= 50)
12
+ * 4. (INS-4.12) .aios-core/package.json dependency completeness
12
13
  *
13
14
  * Exit codes: 0 = PASS, 1 = FAIL
14
15
  * Usage: node bin/utils/validate-publish.js
@@ -81,7 +82,7 @@ try {
81
82
  line.includes('npm notice') && !line.includes('Tarball') && !line.includes('name:') &&
82
83
  !line.includes('version:') && !line.includes('filename:') && !line.includes('package size:') &&
83
84
  !line.includes('unpacked size:') && !line.includes('shasum:') && !line.includes('integrity:') &&
84
- !line.includes('total files:')
85
+ !line.includes('total files:'),
85
86
  );
86
87
  fileCount = fileLines.length;
87
88
 
@@ -97,6 +98,28 @@ try {
97
98
  passed = false;
98
99
  }
99
100
 
101
+ // Check 4 (INS-4.12): .aios-core dependency completeness
102
+ console.log('');
103
+ console.log('--- Dependency Completeness (INS-4.12) ---\n');
104
+ try {
105
+ const depValidatorPath = path.join(PROJECT_ROOT, 'scripts', 'validate-aios-core-deps.js');
106
+ if (fs.existsSync(depValidatorPath)) {
107
+ execSync(`node "${depValidatorPath}"`, {
108
+ encoding: 'utf8',
109
+ cwd: PROJECT_ROOT,
110
+ timeout: 30000,
111
+ stdio: 'inherit',
112
+ });
113
+ console.log('PASS: .aios-core dependency completeness validated');
114
+ } else {
115
+ console.log('SKIP: scripts/validate-aios-core-deps.js not found');
116
+ }
117
+ } catch (_depErr) {
118
+ console.error('FAIL: .aios-core dependency completeness check failed');
119
+ console.error(' Fix: Run "node scripts/validate-aios-core-deps.js" to see details');
120
+ passed = false;
121
+ }
122
+
100
123
  // Summary
101
124
  console.log('');
102
125
  if (passed) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aios-core",
3
- "version": "4.4.2",
3
+ "version": "4.4.3",
4
4
  "description": "Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework",
5
5
  "bin": {
6
6
  "aios": "bin/aios.js",
@@ -324,9 +324,11 @@ async function installAiosCore(options = {}) {
324
324
  // BUG-2 fix (INS-1): Install .aios-core dependencies after copy
325
325
  // The copied .aios-core/package.json has dependencies (js-yaml, execa, etc.)
326
326
  // that must be installed for the activation pipeline to work
327
+ // INS-4.12: Track dep install success for bootstrap guard
327
328
  const aiosCorePackageJson = path.join(targetAiosCore, 'package.json');
329
+ result.aiosCoreDepsInstalled = false;
328
330
  if (await fs.pathExists(aiosCorePackageJson)) {
329
- spinner.text = 'Installing .aios-core dependencies (js-yaml, etc.)...';
331
+ spinner.text = 'Installing .aios-core dependencies (js-yaml, fast-glob, etc.)...';
330
332
  try {
331
333
  const { exec } = require('child_process');
332
334
  const { promisify } = require('util');
@@ -335,6 +337,7 @@ async function installAiosCore(options = {}) {
335
337
  cwd: targetAiosCore,
336
338
  timeout: 60000,
337
339
  });
340
+ result.aiosCoreDepsInstalled = true;
338
341
  spinner.succeed('Installed .aios-core dependencies');
339
342
  spinner.start('Finishing installation...');
340
343
  } catch (depError) {
@@ -571,32 +571,47 @@ async function runWizard(options = {}) {
571
571
  }
572
572
 
573
573
  // Story INS-4.6: Entity Registry Bootstrap — populate entity-registry.yaml on install
574
+ // Story INS-4.12: Fix module resolution + bootstrap timing
575
+ // Bootstrap runs AFTER .aios-core deps are installed (aios-core-installer.js:324-345)
576
+ // NODE_PATH ensures spawned scripts can resolve packages from .aios-core/node_modules/
574
577
  console.log('\n📇 Bootstrapping entity registry...');
575
578
  try {
576
579
  const registryScript = path.join(process.cwd(), '.aios-core', 'development', 'scripts', 'populate-entity-registry.js');
577
580
  if (fse.existsSync(registryScript)) {
578
- const startMs = Date.now();
579
- execSync(`node "${registryScript}"`, {
580
- cwd: process.cwd(),
581
- encoding: 'utf8',
582
- timeout: 30000,
583
- stdio: 'pipe',
584
- });
585
- const elapsedMs = Date.now() - startMs;
586
-
587
- // Read entity count from generated registry
588
- const registryPath = path.join(process.cwd(), '.aios-core', 'data', 'entity-registry.yaml');
589
- let entityCount = 0;
590
- if (fse.existsSync(registryPath)) {
591
- const registryContent = fse.readFileSync(registryPath, 'utf8');
592
- const countMatch = registryContent.match(/entityCount:\s*(\d+)/);
593
- entityCount = countMatch ? parseInt(countMatch[1], 10) : 0;
594
- }
581
+ // INS-4.12 AC3: Guard — skip bootstrap if .aios-core deps are not installed
582
+ const aiosCoreNodeModules = path.join(process.cwd(), '.aios-core', 'node_modules');
583
+ if (!fse.existsSync(aiosCoreNodeModules)) {
584
+ console.warn('⚠️ .aios-core/node_modules/ not found — skipping entity registry bootstrap');
585
+ console.warn(' Run: cd .aios-core && npm install --production');
586
+ answers.entityRegistryStatus = 'skipped-no-deps';
587
+ } else {
588
+ // INS-4.12 AC2: Set NODE_PATH so spawned scripts resolve deps from .aios-core/node_modules/
589
+ const parentNodeModules = path.join(process.cwd(), 'node_modules');
590
+ const nodePath = [aiosCoreNodeModules, parentNodeModules].join(path.delimiter);
591
+ const startMs = Date.now();
592
+ execSync(`node "${registryScript}"`, {
593
+ cwd: process.cwd(),
594
+ encoding: 'utf8',
595
+ timeout: 30000,
596
+ stdio: 'pipe',
597
+ env: { ...process.env, NODE_PATH: nodePath },
598
+ });
599
+ const elapsedMs = Date.now() - startMs;
600
+
601
+ // Read entity count from generated registry
602
+ const registryPath = path.join(process.cwd(), '.aios-core', 'data', 'entity-registry.yaml');
603
+ let entityCount = 0;
604
+ if (fse.existsSync(registryPath)) {
605
+ const registryContent = fse.readFileSync(registryPath, 'utf8');
606
+ const countMatch = registryContent.match(/entityCount:\s*(\d+)/);
607
+ entityCount = countMatch ? parseInt(countMatch[1], 10) : 0;
608
+ }
595
609
 
596
- console.log(`✅ Entity registry: populated (${entityCount} entities, ${(elapsedMs / 1000).toFixed(1)}s)`);
597
- answers.entityRegistryStatus = 'populated';
598
- answers.entityRegistryCount = entityCount;
599
- answers.entityRegistryMs = elapsedMs;
610
+ console.log(`✅ Entity registry: populated (${entityCount} entities, ${(elapsedMs / 1000).toFixed(1)}s)`);
611
+ answers.entityRegistryStatus = 'populated';
612
+ answers.entityRegistryCount = entityCount;
613
+ answers.entityRegistryMs = elapsedMs;
614
+ } // end else (deps exist)
600
615
  } else {
601
616
  console.log(' ℹ️ Entity registry script not found (skipped)');
602
617
  answers.entityRegistryStatus = 'skipped';
@@ -53,7 +53,7 @@ describe('npm-packages check', () => {
53
53
  fs.existsSync.mockReturnValue(true);
54
54
  const result = await npmPackagesCheck.run(mockContext);
55
55
  expect(result.status).toBe('PASS');
56
- expect(result.message).toBe('node_modules present');
56
+ expect(result.message).toContain('node_modules present');
57
57
  });
58
58
 
59
59
  it('should FAIL when node_modules missing', async () => {
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Dependency Graph Validator — .aios-core/package.json completeness
6
+ * Story INS-4.12 (AC1, AC4, AC6)
7
+ *
8
+ * Scans all .js files in .aios-core/development/scripts/ for require() calls,
9
+ * then verifies each non-builtin, non-relative package is declared in
10
+ * .aios-core/package.json dependencies.
11
+ *
12
+ * Exit codes: 0 = PASS, 1 = FAIL (missing deps found)
13
+ * Usage: node scripts/validate-aios-core-deps.js
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const Module = require('module');
19
+
20
+ const PROJECT_ROOT = path.join(__dirname, '..');
21
+ const AIOS_CORE_DIR = path.join(PROJECT_ROOT, '.aios-core');
22
+ const SCRIPTS_DIR = path.join(AIOS_CORE_DIR, 'development', 'scripts');
23
+ const PACKAGE_JSON = path.join(AIOS_CORE_DIR, 'package.json');
24
+
25
+ // Node.js builtin modules
26
+ const BUILTINS = new Set(Module.builtinModules.concat(
27
+ Module.builtinModules.map(m => `node:${m}`),
28
+ ));
29
+
30
+ // Optional dev-time packages that should be wrapped in try-catch, not declared as deps
31
+ const ALLOWLIST = new Set([
32
+ 'eslint',
33
+ 'prettier',
34
+ 'jscodeshift',
35
+ '@babel/parser',
36
+ '@babel/traverse',
37
+ '@babel/generator',
38
+ '@babel/types',
39
+ '@jest/globals',
40
+ 'jest',
41
+ ]);
42
+
43
+ /**
44
+ * Extract require() calls from a JS file using regex.
45
+ * Handles: require('pkg'), require("pkg"), require(`pkg`)
46
+ * Skips: require('./relative'), require('fs'), dynamic require(variable)
47
+ */
48
+ function extractRequires(filePath) {
49
+ const content = fs.readFileSync(filePath, 'utf8');
50
+ const requires = new Set();
51
+
52
+ // Match require('...') and require("...")
53
+ const pattern = /require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
54
+ let match;
55
+ while ((match = pattern.exec(content)) !== null) {
56
+ const dep = match[1];
57
+ // Skip relative paths
58
+ if (dep.startsWith('.') || dep.startsWith('/')) continue;
59
+ // Skip template literal expressions (e.g., require('${dep.name}') in code generators)
60
+ if (dep.includes('${') || dep.includes('$')) continue;
61
+ // Skip builtins
62
+ if (BUILTINS.has(dep)) continue;
63
+
64
+ // Extract package name (handle scoped packages like @babel/parser)
65
+ const parts = dep.split('/');
66
+ const pkgName = dep.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
67
+ requires.add(pkgName);
68
+ }
69
+
70
+ return requires;
71
+ }
72
+
73
+ /**
74
+ * Recursively find all .js files in a directory
75
+ */
76
+ function findJsFiles(dir) {
77
+ const files = [];
78
+ if (!fs.existsSync(dir)) return files;
79
+
80
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
81
+ for (const entry of entries) {
82
+ const fullPath = path.join(dir, entry.name);
83
+ if (entry.isDirectory() && entry.name !== 'node_modules') {
84
+ files.push(...findJsFiles(fullPath));
85
+ } else if (entry.isFile() && entry.name.endsWith('.js')) {
86
+ files.push(fullPath);
87
+ }
88
+ }
89
+ return files;
90
+ }
91
+
92
+ function main() {
93
+ console.log('--- .aios-core Dependency Validation (INS-4.12) ---\n');
94
+
95
+ // Read .aios-core/package.json
96
+ if (!fs.existsSync(PACKAGE_JSON)) {
97
+ console.error('FAIL: .aios-core/package.json not found');
98
+ process.exit(1);
99
+ }
100
+
101
+ const pkg = JSON.parse(fs.readFileSync(PACKAGE_JSON, 'utf8'));
102
+ const declared = new Set(Object.keys(pkg.dependencies || {}));
103
+
104
+ // Find all JS scripts
105
+ const scripts = findJsFiles(SCRIPTS_DIR);
106
+ if (scripts.length === 0) {
107
+ console.log('WARN: No .js files found in .aios-core/development/scripts/');
108
+ process.exit(0);
109
+ }
110
+
111
+ console.log(`Scanning ${scripts.length} scripts in .aios-core/development/scripts/\n`);
112
+
113
+ const errors = [];
114
+ const allRequires = new Map(); // pkg -> [files that use it]
115
+
116
+ for (const script of scripts) {
117
+ const requires = extractRequires(script);
118
+ const relPath = path.relative(PROJECT_ROOT, script);
119
+
120
+ for (const pkg of requires) {
121
+ if (!allRequires.has(pkg)) allRequires.set(pkg, []);
122
+ allRequires.get(pkg).push(relPath);
123
+
124
+ if (!declared.has(pkg) && !ALLOWLIST.has(pkg)) {
125
+ errors.push({ script: relPath, package: pkg });
126
+ }
127
+ }
128
+ }
129
+
130
+ // Report
131
+ console.log(`Found ${allRequires.size} unique external packages across ${scripts.length} scripts`);
132
+ console.log(`Declared in .aios-core/package.json: ${declared.size}`);
133
+ console.log(`Allowlisted (optional/dev-time): ${ALLOWLIST.size}\n`);
134
+
135
+ if (errors.length > 0) {
136
+ console.error(`FAIL: ${errors.length} undeclared dependencies found:\n`);
137
+
138
+ // Group by package
139
+ const byPkg = new Map();
140
+ for (const err of errors) {
141
+ if (!byPkg.has(err.package)) byPkg.set(err.package, []);
142
+ byPkg.get(err.package).push(err.script);
143
+ }
144
+
145
+ for (const [pkg, files] of byPkg) {
146
+ console.error(` Missing: "${pkg}"`);
147
+ for (const f of files) {
148
+ console.error(` Used in: ${f}`);
149
+ }
150
+ }
151
+
152
+ console.error('\nFix: Add missing packages to .aios-core/package.json dependencies');
153
+ console.error('Or add to ALLOWLIST if they are optional dev-time tools (wrap in try-catch)\n');
154
+ process.exit(1);
155
+ }
156
+
157
+ console.log('PASS: All script dependencies are declared in .aios-core/package.json\n');
158
+ process.exit(0);
159
+ }
160
+
161
+ main();