aios-core 4.4.2 → 4.4.4

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.4
11
+ generated_at: "2026-02-25T01:37:46.325Z"
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.4",
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) {
@@ -122,6 +122,7 @@ const TRANSLATIONS = {
122
122
  proKeyInvalid: 'Invalid format. Expected: PRO-XXXX-XXXX-XXXX-XXXX',
123
123
  proKeyValidated: 'License validated: {key}',
124
124
  proModuleNotAvailable: 'Pro license module not available. Ensure @aios-fullstack/pro is installed.',
125
+ proModuleBootstrap: 'Pro license module not found locally. Installing @aios-fullstack/pro to bootstrap...',
125
126
  proServerUnreachable: 'License server is unreachable. Check your internet connection and try again.',
126
127
  proVerifyingAccessShort: 'Verifying access...',
127
128
  proAccessConfirmed: 'Pro access confirmed.',
@@ -279,6 +280,7 @@ const TRANSLATIONS = {
279
280
  proKeyInvalid: 'Formato inválido. Esperado: PRO-XXXX-XXXX-XXXX-XXXX',
280
281
  proKeyValidated: 'Licença validada: {key}',
281
282
  proModuleNotAvailable: 'Módulo de licença Pro não disponível. Certifique-se de que @aios-fullstack/pro está instalado.',
283
+ proModuleBootstrap: 'Módulo de licença Pro não encontrado localmente. Instalando @aios-fullstack/pro...',
282
284
  proServerUnreachable: 'Servidor de licenças inacessível. Verifique sua conexão com a internet e tente novamente.',
283
285
  proVerifyingAccessShort: 'Verificando acesso...',
284
286
  proAccessConfirmed: 'Acesso Pro confirmado.',
@@ -435,6 +437,7 @@ const TRANSLATIONS = {
435
437
  proKeyInvalid: 'Formato inválido. Esperado: PRO-XXXX-XXXX-XXXX-XXXX',
436
438
  proKeyValidated: 'Licencia validada: {key}',
437
439
  proModuleNotAvailable: 'Módulo de licencia Pro no disponible. Asegúrese de que @aios-fullstack/pro esté instalado.',
440
+ proModuleBootstrap: 'Módulo de licencia Pro no encontrado localmente. Instalando @aios-fullstack/pro...',
438
441
  proServerUnreachable: 'Servidor de licencias inaccesible. Verifique su conexión a internet e intente nuevamente.',
439
442
  proVerifyingAccessShort: 'Verificando acceso...',
440
443
  proAccessConfirmed: 'Acceso Pro confirmado.',
@@ -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';
@@ -145,30 +145,56 @@ function showStep(current, total, label) {
145
145
  console.log(colors.dim(' ' + '─'.repeat(44)));
146
146
  }
147
147
 
148
+ /**
149
+ * Try to load a pro license module via multiple resolution paths.
150
+ *
151
+ * Resolution order:
152
+ * 1. Relative path (framework-dev mode: ../../../../pro/license/{name})
153
+ * 2. @aios-fullstack/pro package (brownfield: node_modules/@aios-fullstack/pro/license/{name})
154
+ * 3. Absolute path via aios-core in node_modules (brownfield upgrade)
155
+ *
156
+ * @param {string} moduleName - Module filename without extension (e.g., 'license-api')
157
+ * @returns {Object|null} Loaded module or null
158
+ */
159
+ function loadProModule(moduleName) {
160
+ // 1. Framework-dev mode (cloned repo with pro/ submodule)
161
+ try {
162
+ return require(`../../../../pro/license/${moduleName}`);
163
+ } catch { /* not available */ }
164
+
165
+ // 2. @aios-fullstack/pro installed in user project
166
+ try {
167
+ return require(`@aios-fullstack/pro/license/${moduleName}`);
168
+ } catch { /* not available */ }
169
+
170
+ // 3. aios-core in node_modules (brownfield upgrade from >= v4.2.15)
171
+ try {
172
+ const path = require('path');
173
+ const absPath = path.join(process.cwd(), 'node_modules', 'aios-core', 'pro', 'license', moduleName);
174
+ return require(absPath);
175
+ } catch { /* not available */ }
176
+
177
+ return null;
178
+ }
179
+
148
180
  /**
149
181
  * Try to load the license API client via lazy import.
182
+ * Attempts multiple resolution paths for framework-dev, greenfield, and brownfield.
150
183
  *
151
184
  * @returns {{ LicenseApiClient: Function, licenseApi: Object }|null} License API or null
152
185
  */
153
186
  function loadLicenseApi() {
154
- try {
155
- return require('../../../../pro/license/license-api');
156
- } catch {
157
- return null;
158
- }
187
+ return loadProModule('license-api');
159
188
  }
160
189
 
161
190
  /**
162
191
  * Try to load the feature gate via lazy import.
192
+ * Attempts multiple resolution paths for framework-dev, greenfield, and brownfield.
163
193
  *
164
194
  * @returns {{ featureGate: Object }|null} Feature gate or null
165
195
  */
166
196
  function loadFeatureGate() {
167
- try {
168
- return require('../../../../pro/license/feature-gate');
169
- } catch {
170
- return null;
171
- }
197
+ return loadProModule('feature-gate');
172
198
  }
173
199
 
174
200
  /**
@@ -1192,6 +1218,48 @@ async function runProWizard(options = {}) {
1192
1218
  showProHeader();
1193
1219
  }
1194
1220
 
1221
+ // Pre-check: If license module is not available (brownfield upgrade from older version),
1222
+ // install @aios-fullstack/pro first to get the license API, then proceed with gate.
1223
+ if (!loadLicenseApi()) {
1224
+ const fs = require('fs');
1225
+ const path = require('path');
1226
+ const { execSync } = require('child_process');
1227
+
1228
+ showInfo(t('proModuleBootstrap'));
1229
+
1230
+ // Ensure package.json exists
1231
+ const packageJsonPath = path.join(targetDir, 'package.json');
1232
+ if (!fs.existsSync(packageJsonPath)) {
1233
+ execSync('npm init -y', { cwd: targetDir, stdio: 'pipe' });
1234
+ }
1235
+
1236
+ // Install @aios-fullstack/pro to get license module
1237
+ const proDir = path.join(targetDir, 'node_modules', '@aios-fullstack', 'pro');
1238
+ if (!fs.existsSync(proDir)) {
1239
+ const installSpinner = createSpinner(t('proInstallingPackage'));
1240
+ installSpinner.start();
1241
+ try {
1242
+ execSync('npm install @aios-fullstack/pro', {
1243
+ cwd: targetDir,
1244
+ stdio: 'pipe',
1245
+ timeout: 120000,
1246
+ });
1247
+ installSpinner.succeed(t('proPackageInstalled'));
1248
+ } catch (err) {
1249
+ installSpinner.fail(t('proPackageInstallFailed'));
1250
+ result.error = tf('proNpmInstallFailed', { message: err.message });
1251
+ return result;
1252
+ }
1253
+ }
1254
+
1255
+ // Clear require cache so loadLicenseApi() picks up newly installed module
1256
+ Object.keys(require.cache).forEach((key) => {
1257
+ if (key.includes('license-api') || key.includes('@aios-fullstack')) {
1258
+ delete require.cache[key];
1259
+ }
1260
+ });
1261
+ }
1262
+
1195
1263
  // Step 1: License Gate
1196
1264
  const licenseResult = await stepLicenseGate({
1197
1265
  key: options.key || process.env.AIOS_PRO_KEY,
@@ -1255,6 +1323,7 @@ module.exports = {
1255
1323
  stepLicenseGateWithKey,
1256
1324
  stepLicenseGateWithKeyInteractive,
1257
1325
  stepLicenseGateWithEmail,
1326
+ loadProModule,
1258
1327
  loadLicenseApi,
1259
1328
  loadFeatureGate,
1260
1329
  loadProScaffolder,
@@ -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 () => {