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.
- package/.aios-core/core/doctor/checks/npm-packages.js +53 -10
- package/.aios-core/data/entity-registry.yaml +863 -785
- package/.aios-core/development/scripts/code-quality-improver.js +29 -12
- package/.aios-core/development/scripts/refactoring-suggester.js +15 -6
- package/.aios-core/install-manifest.yaml +12 -12
- package/.aios-core/package.json +9 -5
- package/README.md +3 -1
- package/bin/utils/validate-publish.js +26 -3
- package/package.json +1 -1
- package/packages/installer/src/installer/aios-core-installer.js +4 -1
- package/packages/installer/src/wizard/index.js +36 -21
- package/packages/installer/tests/unit/doctor/doctor-checks.test.js +1 -1
- package/scripts/validate-aios-core-deps.js +161 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
const fs = require('fs').promises;
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
130
|
-
|
|
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:
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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.
|
|
11
|
-
generated_at: "2026-02-
|
|
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:
|
|
336
|
+
hash: sha256:09e03cd3558acbdd6b2f4103c156c76a3c07fa2f0b8996739bec7a865d781922
|
|
337
337
|
type: core
|
|
338
|
-
size:
|
|
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:
|
|
1224
|
+
hash: sha256:fb17c8d978fd7bb483a2752ba285718d86af234f0412ecdc6d123ec0d0634032
|
|
1225
1225
|
type: data
|
|
1226
|
-
size:
|
|
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:
|
|
1468
|
+
hash: sha256:d0c844089e53dcd6c06755d4cb432a60fbebcedcf5a86ed635650573549a1941
|
|
1469
1469
|
type: script
|
|
1470
|
-
size:
|
|
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:
|
|
1568
|
+
hash: sha256:d50ea6b609c9cf8385979386fee4b4385d11ebcde15460260f66d04c705f6cd9
|
|
1569
1569
|
type: script
|
|
1570
|
-
size:
|
|
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:
|
|
3528
|
+
hash: sha256:ac6d72a82cecd2a98a443dc195e87cf815c2df58fb743737177670999a816b5f
|
|
3529
3529
|
type: other
|
|
3530
|
-
size:
|
|
3530
|
+
size: 2495
|
|
3531
3531
|
- path: product/checklists/accessibility-wcag-checklist.md
|
|
3532
3532
|
hash: sha256:56126182b25e9b7bdde43f75315e33167eb49b1f9a9cb0e9a37bc068af40aeab
|
|
3533
3533
|
type: checklist
|
package/.aios-core/package.json
CHANGED
|
@@ -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
|
|
42
|
+
"highlight.js": "^11.9.0",
|
|
39
43
|
"inquirer": "^8.2.6",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
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
|
[](CONTRIBUTING.md)
|
|
13
13
|
[](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/
|
|
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
|
@@ -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
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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).
|
|
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();
|