oxe-cc 1.14.0 → 1.16.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 (40) hide show
  1. package/.github/dependabot.yml +31 -0
  2. package/.github/workflows/ci.yml +141 -56
  3. package/.github/workflows/release.yml +114 -89
  4. package/CHANGELOG.md +866 -779
  5. package/README.md +600 -736
  6. package/bin/lib/oxe-agent-install.cjs +299 -284
  7. package/bin/lib/oxe-artifact-catalog.cjs +376 -0
  8. package/bin/lib/oxe-command-registry.cjs +31 -0
  9. package/bin/lib/oxe-context-engine.cjs +11 -11
  10. package/bin/lib/oxe-core-command-handlers.cjs +82 -0
  11. package/bin/lib/oxe-dashboard.cjs +140 -140
  12. package/bin/lib/oxe-manifest.cjs +20 -20
  13. package/bin/lib/oxe-npm-version.cjs +6 -4
  14. package/bin/lib/oxe-plugin-cli.cjs +95 -0
  15. package/bin/lib/oxe-plugins.cjs +94 -3
  16. package/bin/lib/oxe-process.cjs +67 -0
  17. package/bin/lib/oxe-project-health.cjs +2846 -2856
  18. package/bin/lib/oxe-runtime-semantics.cjs +68 -69
  19. package/bin/oxe-cc.js +179 -325
  20. package/docs/INTEGRATION.md +182 -152
  21. package/docs/QUALITY-GATES.md +46 -0
  22. package/docs/RELEASE-READINESS.md +86 -61
  23. package/docs/RUNTIME-SMOKE-MATRIX.md +137 -135
  24. package/docs/oxe-artifact-map.html +1172 -1172
  25. package/lib/sdk/index.cjs +18 -0
  26. package/lib/sdk/index.d.ts +969 -900
  27. package/lib/sdk/index.types.ts +933 -0
  28. package/oxe/templates/PLUGINS.md +8 -1
  29. package/oxe/templates/STATE-REFERENCE.md +125 -0
  30. package/oxe/templates/STATE.md +11 -121
  31. package/oxe/workflows/help.md +2 -0
  32. package/package.json +129 -108
  33. package/packages/runtime/package.json +18 -18
  34. package/packages/runtime/src/evidence/evidence-store.ts +2 -2
  35. package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +728 -728
  36. package/packages/runtime/src/workspace/strategies/git-worktree.ts +24 -24
  37. package/packages/runtime/tsconfig.json +8 -2
  38. package/vscode-extension/.vscodeignore +2 -0
  39. package/vscode-extension/package.json +193 -185
  40. package/vscode-extension/src/extension.js +11 -1
package/bin/oxe-cc.js CHANGED
@@ -21,10 +21,13 @@ const oxeNpmVersion = require(path.join(__dirname, 'lib', 'oxe-npm-version.cjs')
21
21
  const oxeDashboard = require(path.join(__dirname, 'lib', 'oxe-dashboard.cjs'));
22
22
  const oxeOperational = require(path.join(__dirname, 'lib', 'oxe-operational.cjs'));
23
23
  const oxeAzure = require(path.join(__dirname, 'lib', 'oxe-azure.cjs'));
24
- const oxePlugins = require(path.join(__dirname, 'lib', 'oxe-plugins.cjs'));
24
+ const oxePluginCli = require(path.join(__dirname, 'lib', 'oxe-plugin-cli.cjs'));
25
25
  const oxeContext = require(path.join(__dirname, 'lib', 'oxe-context-engine.cjs'));
26
26
  const oxeRuntimeSemantics = require(path.join(__dirname, 'lib', 'oxe-runtime-semantics.cjs'));
27
27
  const oxeRelease = require(path.join(__dirname, 'lib', 'oxe-release.cjs'));
28
+ const oxeArtifactCatalog = require(path.join(__dirname, 'lib', 'oxe-artifact-catalog.cjs'));
29
+ const { runPackageManagerSync } = require(path.join(__dirname, 'lib', 'oxe-process.cjs'));
30
+ const { createCoreCommandRegistry } = require(path.join(__dirname, 'lib', 'oxe-core-command-handlers.cjs'));
28
31
 
29
32
  /** Merge markers for ~/.copilot/copilot-instructions.md (bloco OXE). */
30
33
  const OXE_INST_BEGIN = '<!-- oxe-cc:install-begin -->';
@@ -935,7 +938,7 @@ function applyInstallFromOxeConfig(opts, targetDir) {
935
938
  }
936
939
 
937
940
  /**
938
- * Mapa número da lista → chaves de runtime
941
+ * Mapa número da lista → chaves de runtime
939
942
  * @param {string} input
940
943
  * @returns {string[]}
941
944
  */
@@ -1291,20 +1294,48 @@ function ensureGitignoreIgnoresOxeDir(projectRoot, opts = {}) {
1291
1294
  }
1292
1295
 
1293
1296
  /**
1294
- * Create `.oxe/STATE.md` from template and ensure `.oxe/codebase/` exists.
1297
+ * Ensure a subdirectory under `.oxe/` exists, creating it (and parents) on
1298
+ * demand. This is the lazy-scaffolding primitive: writers call it right before
1299
+ * persisting so the `.oxe/` tree only grows as features are actually used,
1300
+ * instead of being scaffolded eagerly at install time.
1301
+ * @param {string} target project root
1302
+ * @param {string} sub path relative to `.oxe/` (e.g. 'runs', 'global/milestones')
1303
+ * @returns {string} absolute path of the ensured directory
1304
+ */
1305
+ function ensureOxeSubdir(target, sub) {
1306
+ const dir = path.join(target, '.oxe', ...String(sub).split(/[\\/]/).filter(Boolean));
1307
+ ensureDir(dir);
1308
+ return dir;
1309
+ }
1310
+
1311
+ /**
1312
+ * (Re)generate the `.oxe/README.md` legend from the single artifact catalog.
1313
+ * Written at init and refreshed by `doctor`, so the local documentation always
1314
+ * reflects the canonical structure without the empty folders cluttering disk.
1315
+ * @param {string} target project root
1316
+ * @param {{ quiet?: boolean }} [opts]
1317
+ */
1318
+ function writeOxeLegend(target, opts = {}) {
1319
+ const oxeDir = path.join(target, '.oxe');
1320
+ const dest = path.join(oxeDir, 'README.md');
1321
+ try {
1322
+ ensureDir(oxeDir);
1323
+ fs.writeFileSync(dest, oxeArtifactCatalog.renderLegend(), 'utf8');
1324
+ if (!opts.quiet) console.log(`${green}init${reset} ${dest}`);
1325
+ } catch {
1326
+ /* best-effort: a missing legend is never fatal */
1327
+ }
1328
+ }
1329
+
1330
+ /**
1331
+ * Create the lean `.oxe/` core: STATE.md, config.json and the generated
1332
+ * README.md legend. Every other artifact/folder is created lazily, on first
1333
+ * use, by the workflow that owns it (see `bin/lib/oxe-artifact-catalog.cjs`).
1295
1334
  * @param {string} target
1296
1335
  * @param {{ dryRun: boolean, force: boolean }} opts
1297
1336
  */
1298
1337
  function bootstrapOxe(target, opts) {
1299
1338
  const oxeDir = path.join(target, '.oxe');
1300
- const codebaseDir = path.join(oxeDir, 'codebase');
1301
- const capabilitiesDir = path.join(oxeDir, 'capabilities');
1302
- const investigationsDir = path.join(oxeDir, 'investigations');
1303
- const dashboardDir = path.join(oxeDir, 'dashboard');
1304
- const contextDir = path.join(oxeDir, 'context');
1305
- const contextPacksDir = path.join(contextDir, 'packs');
1306
- const contextSummariesDir = path.join(contextDir, 'summaries');
1307
- const installDir = path.join(oxeDir, 'install');
1308
1339
  const stateSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'STATE.md');
1309
1340
  const stateDest = path.join(oxeDir, 'STATE.md');
1310
1341
  const configSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'config.template.json');
@@ -1316,18 +1347,12 @@ function bootstrapOxe(target, opts) {
1316
1347
  }
1317
1348
 
1318
1349
  if (opts.dryRun) {
1319
- console.log(`${dim}init${reset} ${oxeDir}/ (STATE.md, config.json, codebase/, capabilities/, investigations/, dashboard/, context/, install/, runs/, OXE-EVENTS.ndjson, ACTIVE-RUN.json)`);
1350
+ console.log(`${dim}init${reset} ${oxeDir}/ (STATE.md, config.json, README.md demais artefatos sob demanda)`);
1320
1351
  ensureGitignoreIgnoresOxeDir(target, { dryRun: true });
1321
1352
  return;
1322
1353
  }
1323
1354
 
1324
- ensureDir(codebaseDir);
1325
- ensureDir(capabilitiesDir);
1326
- ensureDir(investigationsDir);
1327
- ensureDir(dashboardDir);
1328
- ensureDir(contextPacksDir);
1329
- ensureDir(contextSummariesDir);
1330
- ensureDir(installDir);
1355
+ ensureDir(oxeDir);
1331
1356
 
1332
1357
  if (!fs.existsSync(stateDest) || opts.force) {
1333
1358
  copyFile(stateSrc, stateDest, { dryRun: false });
@@ -1351,107 +1376,8 @@ function bootstrapOxe(target, opts) {
1351
1376
  }
1352
1377
  }
1353
1378
 
1354
- // Criar estruturas opcionais: plugins/, workstreams/, memory/
1355
- const pluginsDir = path.join(oxeDir, 'plugins');
1356
- if (!fs.existsSync(pluginsDir)) {
1357
- ensureDir(pluginsDir);
1358
- const pluginsReadme = path.join(PKG_ROOT, 'oxe', 'templates', 'PLUGINS.md');
1359
- if (fs.existsSync(pluginsReadme)) {
1360
- const destPluginsReadme = path.join(pluginsDir, 'README.md');
1361
- if (!fs.existsSync(destPluginsReadme)) {
1362
- copyFile(pluginsReadme, destPluginsReadme, { dryRun: false });
1363
- console.log(`${green}init${reset} ${destPluginsReadme}`);
1364
- }
1365
- }
1366
- }
1367
-
1368
- const workstreamsDir = path.join(oxeDir, 'workstreams');
1369
- if (!fs.existsSync(workstreamsDir)) {
1370
- ensureDir(workstreamsDir);
1371
- }
1372
-
1373
- const sessionsDir = path.join(oxeDir, 'sessions');
1374
- if (!fs.existsSync(sessionsDir)) {
1375
- ensureDir(sessionsDir);
1376
- }
1377
-
1378
- const globalDir = path.join(oxeDir, 'global');
1379
- if (!fs.existsSync(globalDir)) {
1380
- ensureDir(globalDir);
1381
- }
1382
-
1383
- const globalMilestonesDir = path.join(globalDir, 'milestones');
1384
- if (!fs.existsSync(globalMilestonesDir)) {
1385
- ensureDir(globalMilestonesDir);
1386
- }
1387
-
1388
- const lessonsSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'LESSONS.template.md');
1389
- const lessonsDest = path.join(globalDir, 'LESSONS.md');
1390
- if (fs.existsSync(lessonsSrc) && !fs.existsSync(lessonsDest)) {
1391
- copyFile(lessonsSrc, lessonsDest, { dryRun: false });
1392
- console.log(`${green}init${reset} ${lessonsDest}`);
1393
- }
1394
-
1395
- const milestonesSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'MILESTONES.template.md');
1396
- const milestonesDest = path.join(globalDir, 'MILESTONES.md');
1397
- if (fs.existsSync(milestonesSrc) && !fs.existsSync(milestonesDest)) {
1398
- copyFile(milestonesSrc, milestonesDest, { dryRun: false });
1399
- console.log(`${green}init${reset} ${milestonesDest}`);
1400
- }
1401
-
1402
- const memoryDir = path.join(oxeDir, 'memory');
1403
- if (!fs.existsSync(memoryDir)) {
1404
- ensureDir(memoryDir);
1405
- }
1406
-
1407
- const runsDir = path.join(oxeDir, 'runs');
1408
- if (!fs.existsSync(runsDir)) {
1409
- ensureDir(runsDir);
1410
- }
1411
-
1412
- const runtimeSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'EXECUTION-RUNTIME.template.md');
1413
- const runtimeDest = path.join(oxeDir, 'EXECUTION-RUNTIME.md');
1414
- if (fs.existsSync(runtimeSrc) && !fs.existsSync(runtimeDest)) {
1415
- copyFile(runtimeSrc, runtimeDest, { dryRun: false });
1416
- console.log(`${green}init${reset} ${runtimeDest}`);
1417
- }
1418
-
1419
- const activeRunSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'ACTIVE-RUN.template.json');
1420
- const activeRunDest = path.join(oxeDir, 'ACTIVE-RUN.json');
1421
- if (fs.existsSync(activeRunSrc) && !fs.existsSync(activeRunDest)) {
1422
- copyFile(activeRunSrc, activeRunDest, { dryRun: false });
1423
- console.log(`${green}init${reset} ${activeRunDest}`);
1424
- }
1425
-
1426
- const eventsDest = path.join(oxeDir, 'OXE-EVENTS.ndjson');
1427
- if (!fs.existsSync(eventsDest)) {
1428
- fs.writeFileSync(eventsDest, '', 'utf8');
1429
- console.log(`${green}init${reset} ${eventsDest}`);
1430
- }
1431
-
1432
- const checkpointsSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'CHECKPOINTS.template.md');
1433
- const checkpointsDest = path.join(oxeDir, 'CHECKPOINTS.md');
1434
- if (fs.existsSync(checkpointsSrc) && !fs.existsSync(checkpointsDest)) {
1435
- copyFile(checkpointsSrc, checkpointsDest, { dryRun: false });
1436
- console.log(`${green}init${reset} ${checkpointsDest}`);
1437
- }
1438
-
1439
- const capabilitiesSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'CAPABILITIES.template.md');
1440
- const capabilitiesDest = path.join(oxeDir, 'CAPABILITIES.md');
1441
- if (fs.existsSync(capabilitiesSrc) && !fs.existsSync(capabilitiesDest)) {
1442
- copyFile(capabilitiesSrc, capabilitiesDest, { dryRun: false });
1443
- console.log(`${green}init${reset} ${capabilitiesDest}`);
1444
- }
1445
-
1446
- const investigationsIndexDest = path.join(oxeDir, 'INVESTIGATIONS.md');
1447
- if (!fs.existsSync(investigationsIndexDest)) {
1448
- fs.writeFileSync(
1449
- investigationsIndexDest,
1450
- '# OXE — Investigações\n\n| Data | Ficheiro | Objetivo | Modo | Estado |\n|------|----------|----------|------|--------|\n',
1451
- 'utf8'
1452
- );
1453
- console.log(`${green}init${reset} ${investigationsIndexDest}`);
1454
- }
1379
+ // Generated legend always (re)written so it tracks the current catalog.
1380
+ writeOxeLegend(target);
1455
1381
 
1456
1382
  ensureGitignoreIgnoresOxeDir(target, { dryRun: false });
1457
1383
  }
@@ -2135,6 +2061,11 @@ function runDoctor(target, options = {}) {
2135
2061
  }
2136
2062
  console.log(`${green}OK${reset} Node.js`);
2137
2063
 
2064
+ // Keep the generated `.oxe/README.md` legend fresh against the current catalog.
2065
+ if (fs.existsSync(path.join(target, '.oxe'))) {
2066
+ writeOxeLegend(target, { quiet: true });
2067
+ }
2068
+
2138
2069
  const wfPkg = path.join(PKG_ROOT, 'oxe', 'workflows');
2139
2070
  const wfTgt = oxeWorkflows.resolveWorkflowsDir(target);
2140
2071
  if (!fs.existsSync(wfPkg)) {
@@ -2368,9 +2299,8 @@ function installGlobalCliPackage() {
2368
2299
  const dimOrEmpty = c ? dim : '';
2369
2300
  const resetOrEmpty = c ? reset : '';
2370
2301
  console.log(`\n ${dimOrEmpty}npm install -g ${spec}${resetOrEmpty}\n`);
2371
- const r = spawnSync('npm', ['install', '-g', spec], {
2302
+ const r = runPackageManagerSync('npm', ['install', '-g', spec], {
2372
2303
  stdio: 'inherit',
2373
- shell: true,
2374
2304
  env: process.env,
2375
2305
  });
2376
2306
  if (r.error) {
@@ -2401,9 +2331,8 @@ function uninstallGlobalCliPackage() {
2401
2331
  const dimOrEmpty = c ? dim : '';
2402
2332
  const resetOrEmpty = c ? reset : '';
2403
2333
  console.log(`\n ${dimOrEmpty}npm uninstall -g ${name}${resetOrEmpty}\n`);
2404
- const r = spawnSync('npm', ['uninstall', '-g', name], {
2334
+ const r = runPackageManagerSync('npm', ['uninstall', '-g', name], {
2405
2335
  stdio: 'inherit',
2406
- shell: true,
2407
2336
  env: process.env,
2408
2337
  });
2409
2338
  if (r.status === 0) {
@@ -2425,9 +2354,8 @@ function uninstallGlobalCliPackage() {
2425
2354
  */
2426
2355
  function isRunningFromGlobalNpmInstall() {
2427
2356
  try {
2428
- const r = spawnSync('npm', ['root', '-g'], {
2357
+ const r = runPackageManagerSync('npm', ['root', '-g'], {
2429
2358
  encoding: 'utf8',
2430
- shell: true,
2431
2359
  env: process.env,
2432
2360
  });
2433
2361
  if (r.status !== 0) return false;
@@ -2570,12 +2498,14 @@ ${cyan}oxe-cc${reset} — instala workflows OXE (núcleo .oxe/ + integrações:
2570
2498
  npx oxe-cc context <build|inspect> [opções] [pasta-do-projeto]
2571
2499
  npx oxe-cc dashboard [opções] [pasta-do-projeto]
2572
2500
  npx oxe-cc events [--tail N] [--since <evt_id>] [--json] [opções] [pasta-do-projeto]
2501
+ npx oxe-cc map [--json] [opções] [pasta-do-projeto]
2573
2502
  npx oxe-cc runtime <status|start|pause|resume|replay|compile|verify|project|ci|promote|recover|gates|agents|execute> [opções] [pasta-do-projeto]
2574
2503
  npx oxe-cc azure <status|doctor|auth|sync|find|servicebus|eventgrid|sql|operations> [opções] [pasta-do-projeto]
2575
2504
  npx oxe-cc capabilities <list|install|remove|update> [opções] [id]
2505
+ npx oxe-cc plugins <list|install|remove> [opções]
2576
2506
  npx oxe-cc uninstall [opções] [pasta-do-projeto]
2577
2507
  npx oxe-cc update [opções] [argumentos extras…]
2578
-
2508
+
2579
2509
  ${green}doctor${reset}
2580
2510
  --release valida release readiness: versões, changelog, wrappers, runtime build e relatórios obrigatórios
2581
2511
  --write-manifest persiste .oxe/release/release-manifest.json durante doctor --release
@@ -2628,6 +2558,13 @@ ${green}context${reset} (Context Engine V2: seleção, compressão e inspeção
2628
2558
  --json saída estruturada em JSON
2629
2559
  --dir <pasta> raiz do projeto (padrão: diretório atual)
2630
2560
 
2561
+ ${green}plugins${reset} (plugins de lifecycle locais ou publicados no npm)
2562
+ list lista plugins carregados e erros de resolução
2563
+ install <npm-package> [version] instala somente pacote npm; prefixo npm: é opcional
2564
+ remove <id> mostra como remover a referência e os arquivos npm
2565
+ --dir <pasta> raiz do projeto (padrão: diretório atual)
2566
+ ${dim}Plugins por path:${reset} crie o .cjs localmente e referencie-o em .oxe/config.json; o comando install não aceita paths
2567
+
2631
2568
  ${green}runtime${reset} (controle operacional explícito do ACTIVE-RUN)
2632
2569
  status mostra o run ativo resolvido para a sessão atual
2633
2570
  start cria um novo run com tracing inicial
@@ -3484,11 +3421,6 @@ function runUninstall(u) {
3484
3421
  ignoreInstallConfig: false,
3485
3422
  ideLocal: false,
3486
3423
  explicitIdeScope: true,
3487
- agentOpenCode: false,
3488
- agentGemini: false,
3489
- agentCodex: false,
3490
- agentWindsurf: false,
3491
- agentAntigravity: false,
3492
3424
  });
3493
3425
 
3494
3426
  printSection('OXE ▸ uninstall');
@@ -3887,11 +3819,10 @@ function runUpdate(u) {
3887
3819
  } else {
3888
3820
  args.push('--no-global-cli', '-l', ...u.rest);
3889
3821
  }
3890
- const r = spawnSync('npx', args, {
3822
+ const r = runPackageManagerSync('npx', args, {
3891
3823
  cwd: u.dir,
3892
3824
  stdio: 'inherit',
3893
3825
  env: { ...process.env, OXE_NO_PROMPT: '1' },
3894
- shell: process.platform === 'win32',
3895
3826
  });
3896
3827
  if (r.error) {
3897
3828
  console.error(`${red}Falha ao executar npx:${reset}`, r.error.message);
@@ -4526,6 +4457,70 @@ function runEvents(opts) {
4526
4457
  console.log('');
4527
4458
  }
4528
4459
 
4460
+ /**
4461
+ * @typedef {{ help: boolean, dir: string, json: boolean, parseError: boolean, unknownFlag: string }} MapOpts
4462
+ * @param {string[]} argv
4463
+ * @returns {MapOpts}
4464
+ */
4465
+ function parseMapArgs(argv) {
4466
+ /** @type {MapOpts} */
4467
+ const out = { help: false, dir: process.cwd(), json: false, parseError: false, unknownFlag: '' };
4468
+ for (let i = 0; i < argv.length; i++) {
4469
+ const a = argv[i];
4470
+ if (a === '-h' || a === '--help') out.help = true;
4471
+ else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
4472
+ else if (a === '--json') out.json = true;
4473
+ else if (!a.startsWith('-') && i === 0) out.dir = path.resolve(a);
4474
+ else {
4475
+ out.parseError = true;
4476
+ out.unknownFlag = a;
4477
+ break;
4478
+ }
4479
+ }
4480
+ return out;
4481
+ }
4482
+
4483
+ /**
4484
+ * Live, annotated map of the `.oxe/` artifacts: what already exists, what is
4485
+ * available on demand, and the state of each (active/empty/stale). Built from
4486
+ * the single artifact catalog so it never drifts from the legend or docs.
4487
+ * @param {MapOpts} opts
4488
+ */
4489
+ function runMap(opts) {
4490
+ const target = opts.dir;
4491
+ if (!fs.existsSync(target)) {
4492
+ if (opts.json) console.log(JSON.stringify({ oxeMapSchema: 1, projectRoot: path.resolve(target), error: 'dir-not-found', groups: [] }));
4493
+ else console.error(`${yellow}Diretório não encontrado: ${target}${reset}`);
4494
+ process.exit(1);
4495
+ }
4496
+ // Stale-scan signal reuses the same health helpers `status`/`doctor` rely on.
4497
+ let staleScan = false;
4498
+ try {
4499
+ const statePath = oxeHealth.oxePaths(target).state;
4500
+ const stateText = fs.existsSync(statePath) ? fs.readFileSync(statePath, 'utf8') : '';
4501
+ const cfg = oxeHealth.loadOxeConfigMerged(target);
4502
+ const scanDate = oxeHealth.parseLastScanDate(stateText);
4503
+ staleScan = oxeHealth.isStaleScan(scanDate, cfg.scan_max_age_days || 0);
4504
+ } catch {
4505
+ staleScan = false;
4506
+ }
4507
+ const model = oxeArtifactCatalog.buildMapModel(target, { staleScan });
4508
+
4509
+ if (opts.json) {
4510
+ console.log(JSON.stringify({ oxeMapSchema: 1, ...model }));
4511
+ return;
4512
+ }
4513
+
4514
+ const c = useAnsiColors();
4515
+ const palette = { active: green, empty: yellow, stale: yellow, absent: dim, group: cyan, dim, root: green };
4516
+ const paint = (s, kind) => (c && palette[kind] ? `${palette[kind]}${s}${reset}` : s);
4517
+ printSection('OXE ▸ map');
4518
+ console.log(` ${paint('Projeto:', 'group')} ${paint(model.projectRoot, 'group')}`);
4519
+ console.log('');
4520
+ console.log(oxeArtifactCatalog.renderMap(model, { paint }));
4521
+ console.log('');
4522
+ }
4523
+
4529
4524
  /**
4530
4525
  * @param {RuntimeOpts} opts
4531
4526
  */
@@ -5617,6 +5612,7 @@ async function main() {
5617
5612
  argv[0] === 'context' ||
5618
5613
  argv[0] === 'dashboard' ||
5619
5614
  argv[0] === 'events' ||
5615
+ argv[0] === 'map' ||
5620
5616
  argv[0] === 'runtime' ||
5621
5617
  argv[0] === 'azure' ||
5622
5618
  argv[0] === 'uninstall' ||
@@ -5631,103 +5627,67 @@ async function main() {
5631
5627
  argv.shift();
5632
5628
  }
5633
5629
 
5634
- if (command === 'uninstall') {
5635
- const u = parseUninstallArgs(argv);
5636
- if (u.help) {
5637
- printBanner();
5638
- usage();
5639
- process.exit(0);
5640
- }
5641
- if (u.conflictFlags) {
5642
- printBanner();
5643
- console.error(`${red}${u.conflictFlags}${reset}`);
5644
- usage();
5645
- process.exit(1);
5646
- }
5647
- if (u.parseError) {
5648
- printBanner();
5649
- console.error(`${red}Opção desconhecida:${reset} ${u.unknownFlag}`);
5650
- usage();
5651
- process.exit(1);
5652
- }
5653
- printBanner();
5654
- if (!u.dryRun && !fs.existsSync(u.dir)) {
5655
- console.error(`${yellow}Diretório não encontrado: ${u.dir}${reset}`);
5656
- process.exit(1);
5657
- }
5658
- runUninstall(u);
5659
- return;
5660
- }
5661
-
5662
- if (command === 'update') {
5663
- const u = parseUpdateArgs(argv);
5664
- if (u.help) {
5665
- printBanner();
5666
- usage();
5667
- process.exit(0);
5668
- }
5669
- if (u.conflictFlags) {
5670
- printBanner();
5671
- console.error(`${red}${u.conflictFlags}${reset}`);
5672
- usage();
5673
- process.exit(1);
5674
- }
5675
- if (u.parseError) {
5676
- printBanner();
5677
- console.error(`${red}Opção desconhecida:${reset} ${u.unknownFlag}`);
5678
- usage();
5679
- process.exit(1);
5680
- }
5681
- printBanner();
5682
- if (u.check) {
5683
- runUpdateVersionCheck(u);
5684
- return;
5685
- }
5686
- if (!u.dryRun && !fs.existsSync(u.dir)) {
5687
- console.error(`${yellow}Diretório não encontrado: ${u.dir}${reset}`);
5688
- process.exit(1);
5689
- }
5690
- runUpdate(u);
5691
- return;
5692
- }
5630
+ const coreRegistry = createCoreCommandRegistry({
5631
+ existsSync: fs.existsSync,
5632
+ printBanner,
5633
+ usage,
5634
+ log: console.log,
5635
+ error: console.error,
5636
+ exit: process.exit,
5637
+ colors: { red, yellow, reset },
5638
+ readPkgVersion,
5639
+ parseUninstallArgs,
5640
+ parseUpdateArgs,
5641
+ parseCapabilitiesArgs,
5642
+ parseRuntimeArgs,
5643
+ parseAzureArgs,
5644
+ parseInstallArgs,
5645
+ runUninstall,
5646
+ runUpdateVersionCheck,
5647
+ runUpdate,
5648
+ runCapabilities,
5649
+ runRuntime,
5650
+ runAzure,
5651
+ runDoctor,
5652
+ runStatus,
5653
+ runStatusFull,
5654
+ });
5655
+ const coreDispatch = await coreRegistry.dispatch(command, argv);
5656
+ if (coreDispatch.handled) return;
5693
5657
 
5694
- if (command === 'capabilities') {
5695
- const cap = parseCapabilitiesArgs(argv);
5696
- if (cap.help) {
5658
+ if (command === 'dashboard') {
5659
+ const d = parseDashboardArgs(argv);
5660
+ if (d.help) {
5697
5661
  printBanner();
5698
5662
  usage();
5699
5663
  process.exit(0);
5700
5664
  }
5701
- if (cap.parseError) {
5665
+ if (d.parseError) {
5702
5666
  printBanner();
5703
- console.error(`${red}Opção desconhecida:${reset} ${cap.unknownFlag}`);
5667
+ console.error(`${red}Opção desconhecida:${reset} ${d.unknownFlag}`);
5704
5668
  usage();
5705
5669
  process.exit(1);
5706
5670
  }
5707
- printBanner();
5708
- if (!fs.existsSync(cap.dir)) {
5709
- console.error(`${yellow}Diretório não encontrado: ${cap.dir}${reset}`);
5710
- process.exit(1);
5711
- }
5712
- runCapabilities(cap);
5671
+ if (!d.json) printBanner();
5672
+ await runDashboard(d);
5713
5673
  return;
5714
5674
  }
5715
5675
 
5716
- if (command === 'dashboard') {
5717
- const d = parseDashboardArgs(argv);
5718
- if (d.help) {
5676
+ if (command === 'map') {
5677
+ const m = parseMapArgs(argv);
5678
+ if (m.help) {
5719
5679
  printBanner();
5720
5680
  usage();
5721
5681
  process.exit(0);
5722
5682
  }
5723
- if (d.parseError) {
5683
+ if (m.parseError) {
5724
5684
  printBanner();
5725
- console.error(`${red}Opção desconhecida:${reset} ${d.unknownFlag}`);
5685
+ console.error(`${red}Opção desconhecida:${reset} ${m.unknownFlag}`);
5726
5686
  usage();
5727
5687
  process.exit(1);
5728
5688
  }
5729
- if (!d.json) printBanner();
5730
- await runDashboard(d);
5689
+ if (!m.json) printBanner();
5690
+ runMap(m);
5731
5691
  return;
5732
5692
  }
5733
5693
 
@@ -5767,95 +5727,13 @@ async function main() {
5767
5727
  return;
5768
5728
  }
5769
5729
 
5770
- if (command === 'runtime') {
5771
- const runtime = parseRuntimeArgs(argv);
5772
- if (runtime.help) {
5773
- printBanner();
5774
- usage();
5775
- process.exit(0);
5776
- }
5777
- if (runtime.parseError) {
5778
- printBanner();
5779
- console.error(`${red}Opção desconhecida:${reset} ${runtime.unknownFlag}`);
5780
- usage();
5781
- process.exit(1);
5782
- }
5783
- printBanner();
5784
- if (!fs.existsSync(runtime.dir)) {
5785
- console.error(`${yellow}Diretório não encontrado: ${runtime.dir}${reset}`);
5786
- process.exit(1);
5787
- }
5788
- runRuntime(runtime).catch((err) => {
5789
- console.error(`${red}${err && err.message ? err.message : 'Falha ao executar runtime.'}${reset}`);
5790
- process.exit(1);
5791
- });
5792
- return;
5793
- }
5794
-
5795
5730
  if (command === 'plugins') {
5796
5731
  printBanner();
5797
- // Parse --dir flag
5798
- let pluginsDir = process.cwd();
5799
- const pluginsArgv = argv.slice();
5800
- for (let i = 0; i < pluginsArgv.length; i++) {
5801
- if (pluginsArgv[i] === '--dir' && pluginsArgv[i + 1]) {
5802
- pluginsDir = path.resolve(pluginsArgv[++i]);
5803
- pluginsArgv.splice(i - 1, 2);
5804
- i -= 2;
5805
- }
5806
- }
5807
- const c = useAnsiColors();
5808
- const subCmd = pluginsArgv[0] || 'list';
5809
- const pluginTarget = pluginsArgv[1] || '';
5810
-
5811
- if (subCmd === 'list') {
5812
- const result = oxePlugins.loadPlugins(pluginsDir);
5813
- console.log(`\n ${c ? green : ''}Plugins carregados:${c ? reset : ''} ${result.plugins.length}`);
5814
- for (const p of result.plugins) {
5815
- console.log(` • ${p.name}${p.version ? ` (${p.version})` : ''} — hooks: ${Object.keys(p.hooks).join(', ')}`);
5816
- }
5817
- if (result.errors.length) {
5818
- console.log(`\n ${c ? yellow : ''}Erros:${c ? reset : ''}`);
5819
- for (const e of result.errors) {
5820
- console.log(` ✗ ${e.file}: ${e.error}`);
5821
- }
5822
- }
5823
- } else if (subCmd === 'install' && pluginTarget) {
5824
- const src = pluginTarget.startsWith('npm:') ? pluginTarget.slice(4) : pluginTarget;
5825
- const ver = pluginsArgv[2] || '';
5826
- console.log(` Instalando plugin: ${src}${ver ? `@${ver}` : ''}...`);
5827
- const result = oxePlugins.installNpmPlugin(pluginsDir, src, ver || undefined);
5828
- if (result.ok) {
5829
- console.log(` ${c ? green : ''}✓${c ? reset : ''} Instalado em: ${result.path}`);
5830
- console.log(` ${c ? dim : ''}Adicione ao .oxe/config.json: "plugins": [{ "source": "npm:${src}" }]${c ? reset : ''}`);
5831
- } else {
5832
- console.error(` ${c ? red : ''}✗ Falha:${c ? reset : ''} ${result.error}`);
5833
- process.exit(1);
5834
- }
5835
- } else if (subCmd === 'remove' && pluginTarget) {
5836
- console.log(` ${c ? yellow : ''}Remove "${pluginTarget}" de .oxe/config.json → plugins[] manualmente.${c ? reset : ''}`);
5837
- console.log(` ${c ? dim : ''}Arquivos npm: rm -rf .oxe/plugins/_npm/node_modules/${pluginTarget}${c ? reset : ''}`);
5838
- } else {
5839
- console.log(` ${c ? yellow : ''}Uso: oxe-cc plugins list | install <npm:pkg|path> | remove <id>${c ? reset : ''}`);
5840
- }
5841
- return;
5842
- }
5843
-
5844
- if (command === 'azure') {
5845
- const azure = parseAzureArgs(argv);
5846
- if (azure.help) {
5847
- printBanner();
5848
- usage();
5849
- process.exit(0);
5850
- }
5851
- if (azure.parseError) {
5852
- printBanner();
5853
- console.error(`${red}Opção desconhecida:${reset} ${azure.unknownFlag}`);
5854
- usage();
5855
- process.exit(1);
5856
- }
5857
- printBanner();
5858
- runAzure(azure);
5732
+ const exitCode = oxePluginCli.runPluginCommand(argv, {
5733
+ useAnsiColors,
5734
+ colors: { green, yellow, dim, red, reset },
5735
+ });
5736
+ if (exitCode !== 0) process.exit(exitCode);
5859
5737
  return;
5860
5738
  }
5861
5739
 
@@ -5908,33 +5786,9 @@ async function main() {
5908
5786
  process.exit(0);
5909
5787
  }
5910
5788
 
5911
- if (!((command === 'status' && opts.jsonOutput) || (command === 'doctor' && opts.jsonOutput))) {
5912
- printBanner();
5913
- }
5789
+ printBanner();
5914
5790
 
5915
5791
  const target = opts.dir;
5916
- if (command === 'doctor') {
5917
- if (!fs.existsSync(target)) {
5918
- console.error(`${yellow}Diretório não encontrado: ${target}${reset}`);
5919
- process.exit(1);
5920
- }
5921
- runDoctor(target, { release: opts.releaseDoctor, json: opts.jsonOutput, writeManifest: opts.writeManifest });
5922
- return;
5923
- }
5924
-
5925
- if (command === 'status') {
5926
- if (!fs.existsSync(target)) {
5927
- console.error(`${yellow}Diretório não encontrado: ${target}${reset}`);
5928
- process.exit(1);
5929
- }
5930
- if (opts.statusFull) {
5931
- runStatusFull(target);
5932
- } else {
5933
- runStatus(target, { json: opts.jsonOutput, hints: opts.statusHints, summary: opts.statusSummary });
5934
- }
5935
- return;
5936
- }
5937
-
5938
5792
  if (command === 'init-oxe') {
5939
5793
  if (!opts.dryRun && !fs.existsSync(target)) {
5940
5794
  console.error(`${yellow}Diretório não encontrado: ${target}${reset}`);
@@ -5947,8 +5801,8 @@ async function main() {
5947
5801
  bootstrapOxe(target, { dryRun: opts.dryRun, force: opts.force });
5948
5802
  printSummaryAndNextSteps(c0, {
5949
5803
  bullets: opts.dryRun
5950
- ? ['[simulação] Seriam criados ou atualizados .oxe/STATE.md, .oxe/config.json, .oxe/codebase/, .oxe/ACTIVE-RUN.json e .oxe/OXE-EVENTS.ndjson']
5951
- : ['.oxe/STATE.md, .oxe/config.json, .oxe/codebase/, .oxe/context/, .oxe/ACTIVE-RUN.json e .oxe/OXE-EVENTS.ndjson (criados ou atualizados conforme --force)'],
5804
+ ? ['[simulação] Seriam criados .oxe/STATE.md, .oxe/config.json e .oxe/README.md (legenda) — demais artefatos nascem sob demanda; veja com oxe-cc map']
5805
+ : ['.oxe/STATE.md, .oxe/config.json e .oxe/README.md (legenda) — núcleo enxuto; demais pastas/artefatos são criados sob demanda pelo workflow correspondente (veja oxe-cc map)'],
5952
5806
  nextSteps: [
5953
5807
  { desc: 'Validar o projeto:', cmd: 'npx oxe-cc doctor' },
5954
5808
  { desc: 'Instalar integrações IDE/CLI (se ainda não fez):', cmd: 'npx oxe-cc@latest' },