oxe-cc 1.12.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.
- package/.github/dependabot.yml +31 -0
- package/.github/workflows/ci.yml +141 -56
- package/.github/workflows/release.yml +114 -89
- package/CHANGELOG.md +866 -754
- package/README.md +600 -736
- package/bin/lib/oxe-agent-install.cjs +299 -284
- package/bin/lib/oxe-artifact-catalog.cjs +376 -0
- package/bin/lib/oxe-command-registry.cjs +31 -0
- package/bin/lib/oxe-context-engine.cjs +11 -11
- package/bin/lib/oxe-core-command-handlers.cjs +82 -0
- package/bin/lib/oxe-dashboard.cjs +140 -140
- package/bin/lib/oxe-manifest.cjs +20 -20
- package/bin/lib/oxe-npm-version.cjs +6 -4
- package/bin/lib/oxe-plugin-cli.cjs +95 -0
- package/bin/lib/oxe-plugins.cjs +94 -3
- package/bin/lib/oxe-process.cjs +67 -0
- package/bin/lib/oxe-project-health.cjs +2846 -2781
- package/bin/lib/oxe-runtime-semantics.cjs +68 -69
- package/bin/oxe-cc.js +369 -353
- package/docs/INTEGRATION.md +182 -0
- package/docs/QUALITY-GATES.md +46 -0
- package/docs/RELEASE-READINESS.md +86 -61
- package/docs/RUNTIME-SMOKE-MATRIX.md +137 -135
- package/docs/oxe-artifact-map.html +1172 -0
- package/lib/sdk/index.cjs +20 -0
- package/lib/sdk/index.d.ts +971 -876
- package/lib/sdk/index.types.ts +933 -0
- package/oxe/templates/PLUGINS.md +8 -1
- package/oxe/templates/STATE-REFERENCE.md +125 -0
- package/oxe/templates/STATE.md +11 -121
- package/oxe/workflows/help.md +2 -0
- package/package.json +129 -108
- package/packages/runtime/package.json +18 -18
- package/packages/runtime/src/evidence/evidence-store.ts +2 -2
- package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +728 -728
- package/packages/runtime/src/workspace/strategies/git-worktree.ts +24 -24
- package/packages/runtime/tsconfig.json +8 -2
- package/vscode-extension/.vscodeignore +2 -0
- package/vscode-extension/package.json +193 -185
- package/vscode-extension/src/extension.js +11 -1
package/bin/oxe-cc.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* OXE — CLI em pt-BR: instala workflows no projeto, bootstrap `.oxe/`, doctor, uninstall, update.
|
|
4
4
|
* Uso: npx oxe-cc, doctor, status, init-oxe, uninstall, update (ver --help).
|
|
@@ -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
|
|
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 -->';
|
|
@@ -203,26 +206,26 @@ function buildInstallSummary(opts, fullLayout) {
|
|
|
203
206
|
cmd: 'npx oxe-cc status',
|
|
204
207
|
});
|
|
205
208
|
|
|
206
|
-
const agentHint = [];
|
|
207
|
-
if (opts.cursor) agentHint.push('Cursor');
|
|
208
|
-
if (opts.copilot) agentHint.push('Copilot no VS Code');
|
|
209
|
-
if (opts.copilotCli || opts.allAgents || anyGranularAgent(opts)) agentHint.push('CLIs / multi-agente');
|
|
210
|
-
const onlyCodexAgent =
|
|
211
|
-
opts.agentCodex &&
|
|
212
|
-
!opts.allAgents &&
|
|
213
|
-
!opts.copilotCli &&
|
|
214
|
-
!opts.cursor &&
|
|
215
|
-
!opts.copilot &&
|
|
216
|
-
!opts.agentOpenCode &&
|
|
217
|
-
!opts.agentGemini &&
|
|
218
|
-
!opts.agentWindsurf &&
|
|
219
|
-
!opts.agentAntigravity;
|
|
220
|
-
if (agentHint.length) {
|
|
221
|
-
nextSteps.push({
|
|
222
|
-
desc: `Entrar no fluxo OXE no agente (${agentHint.join(', ')}) e deixar o router indicar o primeiro passo:`,
|
|
223
|
-
cmd: onlyCodexAgent ? '/prompts:oxe' : '/oxe',
|
|
224
|
-
});
|
|
225
|
-
} else if (opts.oxeOnly) {
|
|
209
|
+
const agentHint = [];
|
|
210
|
+
if (opts.cursor) agentHint.push('Cursor');
|
|
211
|
+
if (opts.copilot) agentHint.push('Copilot no VS Code');
|
|
212
|
+
if (opts.copilotCli || opts.allAgents || anyGranularAgent(opts)) agentHint.push('CLIs / multi-agente');
|
|
213
|
+
const onlyCodexAgent =
|
|
214
|
+
opts.agentCodex &&
|
|
215
|
+
!opts.allAgents &&
|
|
216
|
+
!opts.copilotCli &&
|
|
217
|
+
!opts.cursor &&
|
|
218
|
+
!opts.copilot &&
|
|
219
|
+
!opts.agentOpenCode &&
|
|
220
|
+
!opts.agentGemini &&
|
|
221
|
+
!opts.agentWindsurf &&
|
|
222
|
+
!opts.agentAntigravity;
|
|
223
|
+
if (agentHint.length) {
|
|
224
|
+
nextSteps.push({
|
|
225
|
+
desc: `Entrar no fluxo OXE no agente (${agentHint.join(', ')}) e deixar o router indicar o primeiro passo:`,
|
|
226
|
+
cmd: onlyCodexAgent ? '/prompts:oxe' : '/oxe',
|
|
227
|
+
});
|
|
228
|
+
} else if (opts.oxeOnly) {
|
|
226
229
|
nextSteps.push({
|
|
227
230
|
desc: 'Para ativar integrações IDE/CLI neste repo, instale de novo sem --oxe-only:',
|
|
228
231
|
cmd: 'npx oxe-cc@latest',
|
|
@@ -234,19 +237,19 @@ function buildInstallSummary(opts, fullLayout) {
|
|
|
234
237
|
});
|
|
235
238
|
}
|
|
236
239
|
|
|
237
|
-
if (opts.copilotCli || opts.allAgents) {
|
|
238
|
-
nextSteps.push({
|
|
239
|
-
desc: 'No Copilot CLI: após instalar, rode /skills reload (ou reinicie o copilot) e use /oxe ou /oxe-scan:',
|
|
240
|
-
cmd: '/skills list',
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
if (opts.agentCodex || opts.allAgents) {
|
|
244
|
-
nextSteps.push({
|
|
245
|
-
desc: 'No Codex Desktop: reinicie o app ou abra uma nova conversa para recarregar prompts/skills; use a superfície de prompts do Codex:',
|
|
246
|
-
cmd: '/prompts:oxe ou /prompts:oxe-spec',
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
if (opts.allAgents || opts.agentGemini) {
|
|
240
|
+
if (opts.copilotCli || opts.allAgents) {
|
|
241
|
+
nextSteps.push({
|
|
242
|
+
desc: 'No Copilot CLI: após instalar, rode /skills reload (ou reinicie o copilot) e use /oxe ou /oxe-scan:',
|
|
243
|
+
cmd: '/skills list',
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
if (opts.agentCodex || opts.allAgents) {
|
|
247
|
+
nextSteps.push({
|
|
248
|
+
desc: 'No Codex Desktop: reinicie o app ou abra uma nova conversa para recarregar prompts/skills; use a superfície de prompts do Codex:',
|
|
249
|
+
cmd: '/prompts:oxe ou /prompts:oxe-spec',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
if (opts.allAgents || opts.agentGemini) {
|
|
250
253
|
nextSteps.push({
|
|
251
254
|
desc: 'No Gemini CLI: recarregar comandos personalizados (/oxe, /oxe:scan, …):',
|
|
252
255
|
cmd: '/commands reload',
|
|
@@ -390,6 +393,8 @@ function parseInstallArgs(argv) {
|
|
|
390
393
|
statusHints: false,
|
|
391
394
|
/** Visão extendida CLI-first: coverage matrix + readiness gate no terminal. */
|
|
392
395
|
statusFull: false,
|
|
396
|
+
/** Saída JSON compacta/versionada para hosts (status --json --summary). */
|
|
397
|
+
statusSummary: false,
|
|
393
398
|
restPositional: [],
|
|
394
399
|
};
|
|
395
400
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -435,6 +440,7 @@ function parseInstallArgs(argv) {
|
|
|
435
440
|
} else if (a === '--json') out.jsonOutput = true;
|
|
436
441
|
else if (a === '--hints') out.statusHints = true;
|
|
437
442
|
else if (a === '--full') out.statusFull = true;
|
|
443
|
+
else if (a === '--summary') out.statusSummary = true;
|
|
438
444
|
else if (a === '--release') out.releaseDoctor = true;
|
|
439
445
|
else if (a === '--write-manifest') out.writeManifest = true;
|
|
440
446
|
else if (!a.startsWith('-')) out.restPositional.push(a);
|
|
@@ -932,7 +938,7 @@ function applyInstallFromOxeConfig(opts, targetDir) {
|
|
|
932
938
|
}
|
|
933
939
|
|
|
934
940
|
/**
|
|
935
|
-
* Mapa número da lista → chaves de runtime
|
|
941
|
+
* Mapa número da lista → chaves de runtime
|
|
936
942
|
* @param {string} input
|
|
937
943
|
* @returns {string[]}
|
|
938
944
|
*/
|
|
@@ -1288,20 +1294,48 @@ function ensureGitignoreIgnoresOxeDir(projectRoot, opts = {}) {
|
|
|
1288
1294
|
}
|
|
1289
1295
|
|
|
1290
1296
|
/**
|
|
1291
|
-
*
|
|
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`).
|
|
1292
1334
|
* @param {string} target
|
|
1293
1335
|
* @param {{ dryRun: boolean, force: boolean }} opts
|
|
1294
1336
|
*/
|
|
1295
1337
|
function bootstrapOxe(target, opts) {
|
|
1296
1338
|
const oxeDir = path.join(target, '.oxe');
|
|
1297
|
-
const codebaseDir = path.join(oxeDir, 'codebase');
|
|
1298
|
-
const capabilitiesDir = path.join(oxeDir, 'capabilities');
|
|
1299
|
-
const investigationsDir = path.join(oxeDir, 'investigations');
|
|
1300
|
-
const dashboardDir = path.join(oxeDir, 'dashboard');
|
|
1301
|
-
const contextDir = path.join(oxeDir, 'context');
|
|
1302
|
-
const contextPacksDir = path.join(contextDir, 'packs');
|
|
1303
|
-
const contextSummariesDir = path.join(contextDir, 'summaries');
|
|
1304
|
-
const installDir = path.join(oxeDir, 'install');
|
|
1305
1339
|
const stateSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'STATE.md');
|
|
1306
1340
|
const stateDest = path.join(oxeDir, 'STATE.md');
|
|
1307
1341
|
const configSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'config.template.json');
|
|
@@ -1313,18 +1347,12 @@ function bootstrapOxe(target, opts) {
|
|
|
1313
1347
|
}
|
|
1314
1348
|
|
|
1315
1349
|
if (opts.dryRun) {
|
|
1316
|
-
console.log(`${dim}init${reset} ${oxeDir}/ (STATE.md, config.json,
|
|
1350
|
+
console.log(`${dim}init${reset} ${oxeDir}/ (STATE.md, config.json, README.md — demais artefatos sob demanda)`);
|
|
1317
1351
|
ensureGitignoreIgnoresOxeDir(target, { dryRun: true });
|
|
1318
1352
|
return;
|
|
1319
1353
|
}
|
|
1320
1354
|
|
|
1321
|
-
ensureDir(
|
|
1322
|
-
ensureDir(capabilitiesDir);
|
|
1323
|
-
ensureDir(investigationsDir);
|
|
1324
|
-
ensureDir(dashboardDir);
|
|
1325
|
-
ensureDir(contextPacksDir);
|
|
1326
|
-
ensureDir(contextSummariesDir);
|
|
1327
|
-
ensureDir(installDir);
|
|
1355
|
+
ensureDir(oxeDir);
|
|
1328
1356
|
|
|
1329
1357
|
if (!fs.existsSync(stateDest) || opts.force) {
|
|
1330
1358
|
copyFile(stateSrc, stateDest, { dryRun: false });
|
|
@@ -1348,107 +1376,8 @@ function bootstrapOxe(target, opts) {
|
|
|
1348
1376
|
}
|
|
1349
1377
|
}
|
|
1350
1378
|
|
|
1351
|
-
//
|
|
1352
|
-
|
|
1353
|
-
if (!fs.existsSync(pluginsDir)) {
|
|
1354
|
-
ensureDir(pluginsDir);
|
|
1355
|
-
const pluginsReadme = path.join(PKG_ROOT, 'oxe', 'templates', 'PLUGINS.md');
|
|
1356
|
-
if (fs.existsSync(pluginsReadme)) {
|
|
1357
|
-
const destPluginsReadme = path.join(pluginsDir, 'README.md');
|
|
1358
|
-
if (!fs.existsSync(destPluginsReadme)) {
|
|
1359
|
-
copyFile(pluginsReadme, destPluginsReadme, { dryRun: false });
|
|
1360
|
-
console.log(`${green}init${reset} ${destPluginsReadme}`);
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
const workstreamsDir = path.join(oxeDir, 'workstreams');
|
|
1366
|
-
if (!fs.existsSync(workstreamsDir)) {
|
|
1367
|
-
ensureDir(workstreamsDir);
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
const sessionsDir = path.join(oxeDir, 'sessions');
|
|
1371
|
-
if (!fs.existsSync(sessionsDir)) {
|
|
1372
|
-
ensureDir(sessionsDir);
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
const globalDir = path.join(oxeDir, 'global');
|
|
1376
|
-
if (!fs.existsSync(globalDir)) {
|
|
1377
|
-
ensureDir(globalDir);
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
const globalMilestonesDir = path.join(globalDir, 'milestones');
|
|
1381
|
-
if (!fs.existsSync(globalMilestonesDir)) {
|
|
1382
|
-
ensureDir(globalMilestonesDir);
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
|
-
const lessonsSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'LESSONS.template.md');
|
|
1386
|
-
const lessonsDest = path.join(globalDir, 'LESSONS.md');
|
|
1387
|
-
if (fs.existsSync(lessonsSrc) && !fs.existsSync(lessonsDest)) {
|
|
1388
|
-
copyFile(lessonsSrc, lessonsDest, { dryRun: false });
|
|
1389
|
-
console.log(`${green}init${reset} ${lessonsDest}`);
|
|
1390
|
-
}
|
|
1391
|
-
|
|
1392
|
-
const milestonesSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'MILESTONES.template.md');
|
|
1393
|
-
const milestonesDest = path.join(globalDir, 'MILESTONES.md');
|
|
1394
|
-
if (fs.existsSync(milestonesSrc) && !fs.existsSync(milestonesDest)) {
|
|
1395
|
-
copyFile(milestonesSrc, milestonesDest, { dryRun: false });
|
|
1396
|
-
console.log(`${green}init${reset} ${milestonesDest}`);
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
const memoryDir = path.join(oxeDir, 'memory');
|
|
1400
|
-
if (!fs.existsSync(memoryDir)) {
|
|
1401
|
-
ensureDir(memoryDir);
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
const runsDir = path.join(oxeDir, 'runs');
|
|
1405
|
-
if (!fs.existsSync(runsDir)) {
|
|
1406
|
-
ensureDir(runsDir);
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
const runtimeSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'EXECUTION-RUNTIME.template.md');
|
|
1410
|
-
const runtimeDest = path.join(oxeDir, 'EXECUTION-RUNTIME.md');
|
|
1411
|
-
if (fs.existsSync(runtimeSrc) && !fs.existsSync(runtimeDest)) {
|
|
1412
|
-
copyFile(runtimeSrc, runtimeDest, { dryRun: false });
|
|
1413
|
-
console.log(`${green}init${reset} ${runtimeDest}`);
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
const activeRunSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'ACTIVE-RUN.template.json');
|
|
1417
|
-
const activeRunDest = path.join(oxeDir, 'ACTIVE-RUN.json');
|
|
1418
|
-
if (fs.existsSync(activeRunSrc) && !fs.existsSync(activeRunDest)) {
|
|
1419
|
-
copyFile(activeRunSrc, activeRunDest, { dryRun: false });
|
|
1420
|
-
console.log(`${green}init${reset} ${activeRunDest}`);
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
const eventsDest = path.join(oxeDir, 'OXE-EVENTS.ndjson');
|
|
1424
|
-
if (!fs.existsSync(eventsDest)) {
|
|
1425
|
-
fs.writeFileSync(eventsDest, '', 'utf8');
|
|
1426
|
-
console.log(`${green}init${reset} ${eventsDest}`);
|
|
1427
|
-
}
|
|
1428
|
-
|
|
1429
|
-
const checkpointsSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'CHECKPOINTS.template.md');
|
|
1430
|
-
const checkpointsDest = path.join(oxeDir, 'CHECKPOINTS.md');
|
|
1431
|
-
if (fs.existsSync(checkpointsSrc) && !fs.existsSync(checkpointsDest)) {
|
|
1432
|
-
copyFile(checkpointsSrc, checkpointsDest, { dryRun: false });
|
|
1433
|
-
console.log(`${green}init${reset} ${checkpointsDest}`);
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
|
-
const capabilitiesSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'CAPABILITIES.template.md');
|
|
1437
|
-
const capabilitiesDest = path.join(oxeDir, 'CAPABILITIES.md');
|
|
1438
|
-
if (fs.existsSync(capabilitiesSrc) && !fs.existsSync(capabilitiesDest)) {
|
|
1439
|
-
copyFile(capabilitiesSrc, capabilitiesDest, { dryRun: false });
|
|
1440
|
-
console.log(`${green}init${reset} ${capabilitiesDest}`);
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
const investigationsIndexDest = path.join(oxeDir, 'INVESTIGATIONS.md');
|
|
1444
|
-
if (!fs.existsSync(investigationsIndexDest)) {
|
|
1445
|
-
fs.writeFileSync(
|
|
1446
|
-
investigationsIndexDest,
|
|
1447
|
-
'# OXE — Investigações\n\n| Data | Ficheiro | Objetivo | Modo | Estado |\n|------|----------|----------|------|--------|\n',
|
|
1448
|
-
'utf8'
|
|
1449
|
-
);
|
|
1450
|
-
console.log(`${green}init${reset} ${investigationsIndexDest}`);
|
|
1451
|
-
}
|
|
1379
|
+
// Generated legend — always (re)written so it tracks the current catalog.
|
|
1380
|
+
writeOxeLegend(target);
|
|
1452
1381
|
|
|
1453
1382
|
ensureGitignoreIgnoresOxeDir(target, { dryRun: false });
|
|
1454
1383
|
}
|
|
@@ -1892,6 +1821,18 @@ function runStatus(target, opts = {}) {
|
|
|
1892
1821
|
const routineHints = collectOxeRoutineHints(target, report, config);
|
|
1893
1822
|
const next = report.next;
|
|
1894
1823
|
|
|
1824
|
+
// Lightweight, versioned summary for host integrations (IDEs, OXESpace) —
|
|
1825
|
+
// a small stable payload instead of the full ~100KB status. Includes a
|
|
1826
|
+
// compact per-agent skills view so a host can decide whether to offer
|
|
1827
|
+
// "install OXE skills" before launching an agent.
|
|
1828
|
+
if (opts.json && opts.summary) {
|
|
1829
|
+
const summary = oxeHealth.buildStatusSummary(report);
|
|
1830
|
+
summary.projectRoot = path.resolve(target);
|
|
1831
|
+
summary.agentSkills = oxeHealth.agentSkillsReport(target).map((a) => ({ agent: a.agent, skillsInstalled: a.skillsInstalled }));
|
|
1832
|
+
console.log(JSON.stringify(summary));
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1895
1836
|
if (opts.json) {
|
|
1896
1837
|
/** @type {Record<string, unknown>} */
|
|
1897
1838
|
const payload = {
|
|
@@ -1947,6 +1888,7 @@ function runStatus(target, opts = {}) {
|
|
|
1947
1888
|
azure: report.azure,
|
|
1948
1889
|
copilot: report.copilot,
|
|
1949
1890
|
codex: report.codex,
|
|
1891
|
+
agentSkills: oxeHealth.agentSkillsReport(target),
|
|
1950
1892
|
contextPacks: report.contextPacks,
|
|
1951
1893
|
contextQuality: report.contextQuality,
|
|
1952
1894
|
semanticsDrift: report.semanticsDrift,
|
|
@@ -2119,6 +2061,11 @@ function runDoctor(target, options = {}) {
|
|
|
2119
2061
|
}
|
|
2120
2062
|
console.log(`${green}OK${reset} Node.js`);
|
|
2121
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
|
+
|
|
2122
2069
|
const wfPkg = path.join(PKG_ROOT, 'oxe', 'workflows');
|
|
2123
2070
|
const wfTgt = oxeWorkflows.resolveWorkflowsDir(target);
|
|
2124
2071
|
if (!fs.existsSync(wfPkg)) {
|
|
@@ -2352,9 +2299,8 @@ function installGlobalCliPackage() {
|
|
|
2352
2299
|
const dimOrEmpty = c ? dim : '';
|
|
2353
2300
|
const resetOrEmpty = c ? reset : '';
|
|
2354
2301
|
console.log(`\n ${dimOrEmpty}npm install -g ${spec}${resetOrEmpty}\n`);
|
|
2355
|
-
const r =
|
|
2302
|
+
const r = runPackageManagerSync('npm', ['install', '-g', spec], {
|
|
2356
2303
|
stdio: 'inherit',
|
|
2357
|
-
shell: true,
|
|
2358
2304
|
env: process.env,
|
|
2359
2305
|
});
|
|
2360
2306
|
if (r.error) {
|
|
@@ -2385,9 +2331,8 @@ function uninstallGlobalCliPackage() {
|
|
|
2385
2331
|
const dimOrEmpty = c ? dim : '';
|
|
2386
2332
|
const resetOrEmpty = c ? reset : '';
|
|
2387
2333
|
console.log(`\n ${dimOrEmpty}npm uninstall -g ${name}${resetOrEmpty}\n`);
|
|
2388
|
-
const r =
|
|
2334
|
+
const r = runPackageManagerSync('npm', ['uninstall', '-g', name], {
|
|
2389
2335
|
stdio: 'inherit',
|
|
2390
|
-
shell: true,
|
|
2391
2336
|
env: process.env,
|
|
2392
2337
|
});
|
|
2393
2338
|
if (r.status === 0) {
|
|
@@ -2409,9 +2354,8 @@ function uninstallGlobalCliPackage() {
|
|
|
2409
2354
|
*/
|
|
2410
2355
|
function isRunningFromGlobalNpmInstall() {
|
|
2411
2356
|
try {
|
|
2412
|
-
const r =
|
|
2357
|
+
const r = runPackageManagerSync('npm', ['root', '-g'], {
|
|
2413
2358
|
encoding: 'utf8',
|
|
2414
|
-
shell: true,
|
|
2415
2359
|
env: process.env,
|
|
2416
2360
|
});
|
|
2417
2361
|
if (r.status !== 0) return false;
|
|
@@ -2553,12 +2497,15 @@ ${cyan}oxe-cc${reset} — instala workflows OXE (núcleo .oxe/ + integrações:
|
|
|
2553
2497
|
npx oxe-cc init-oxe [opções] [pasta-do-projeto]
|
|
2554
2498
|
npx oxe-cc context <build|inspect> [opções] [pasta-do-projeto]
|
|
2555
2499
|
npx oxe-cc dashboard [opções] [pasta-do-projeto]
|
|
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]
|
|
2556
2502
|
npx oxe-cc runtime <status|start|pause|resume|replay|compile|verify|project|ci|promote|recover|gates|agents|execute> [opções] [pasta-do-projeto]
|
|
2557
2503
|
npx oxe-cc azure <status|doctor|auth|sync|find|servicebus|eventgrid|sql|operations> [opções] [pasta-do-projeto]
|
|
2558
2504
|
npx oxe-cc capabilities <list|install|remove|update> [opções] [id]
|
|
2505
|
+
npx oxe-cc plugins <list|install|remove> [opções]
|
|
2559
2506
|
npx oxe-cc uninstall [opções] [pasta-do-projeto]
|
|
2560
2507
|
npx oxe-cc update [opções] [argumentos extras…]
|
|
2561
|
-
|
|
2508
|
+
|
|
2562
2509
|
${green}doctor${reset}
|
|
2563
2510
|
--release valida release readiness: versões, changelog, wrappers, runtime build e relatórios obrigatórios
|
|
2564
2511
|
--write-manifest persiste .oxe/release/release-manifest.json durante doctor --release
|
|
@@ -2587,12 +2534,21 @@ ${green}update${reset} (executa npx oxe-cc@latest --force na pasta do projeto)
|
|
|
2587
2534
|
${dim}CI / sem rede:${reset} OXE_UPDATE_SKIP_REGISTRY=1 desativa consultas (--check sai 2; --if-newer sai 2 sem npx)
|
|
2588
2535
|
|
|
2589
2536
|
${green}dashboard${reset} (interface web local para revisão e aprovação do plano)
|
|
2590
|
-
--port <número> porta local (padrão: 4173)
|
|
2537
|
+
--port <número> porta local (padrão: 4173; use 0 para porta efêmera)
|
|
2591
2538
|
--no-open não abre o browser automaticamente
|
|
2539
|
+
--json emite {url,port,...} (oxeDashboardSchema:1) e segue servindo — para embutir num host
|
|
2540
|
+
--read-only UI visual sem persistência (embed seguro)
|
|
2592
2541
|
--session <sessions/sNNN-slug> força visualização de uma sessão específica
|
|
2593
2542
|
--dump-context imprime JSON consolidado e sai
|
|
2594
2543
|
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
2595
2544
|
|
|
2545
|
+
${green}events${reset} (projeção read-only do log .oxe/OXE-EVENTS.ndjson — para hosts)
|
|
2546
|
+
--tail <N> só os últimos N eventos (padrão: 50)
|
|
2547
|
+
--since <evt_id> só eventos posteriores a um event_id conhecido (leitura incremental)
|
|
2548
|
+
--json saída estruturada (oxeEventsSchema:1) com summary + events
|
|
2549
|
+
--session <sessions/sNNN-slug> força sessão específica (padrão: sessão ativa do STATE.md)
|
|
2550
|
+
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
2551
|
+
|
|
2596
2552
|
${green}context${reset} (Context Engine V2: seleção, compressão e inspeção determinística)
|
|
2597
2553
|
build [--workflow <slug>] gera pack(s) em .oxe/context/packs/
|
|
2598
2554
|
inspect [--workflow <slug>] lê um pack existente ou resolve sob demanda
|
|
@@ -2602,6 +2558,13 @@ ${green}context${reset} (Context Engine V2: seleção, compressão e inspeção
|
|
|
2602
2558
|
--json saída estruturada em JSON
|
|
2603
2559
|
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
2604
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
|
+
|
|
2605
2568
|
${green}runtime${reset} (controle operacional explícito do ACTIVE-RUN)
|
|
2606
2569
|
status mostra o run ativo resolvido para a sessão atual
|
|
2607
2570
|
start cria um novo run com tracing inicial
|
|
@@ -3458,11 +3421,6 @@ function runUninstall(u) {
|
|
|
3458
3421
|
ignoreInstallConfig: false,
|
|
3459
3422
|
ideLocal: false,
|
|
3460
3423
|
explicitIdeScope: true,
|
|
3461
|
-
agentOpenCode: false,
|
|
3462
|
-
agentGemini: false,
|
|
3463
|
-
agentCodex: false,
|
|
3464
|
-
agentWindsurf: false,
|
|
3465
|
-
agentAntigravity: false,
|
|
3466
3424
|
});
|
|
3467
3425
|
|
|
3468
3426
|
printSection('OXE ▸ uninstall');
|
|
@@ -3861,11 +3819,10 @@ function runUpdate(u) {
|
|
|
3861
3819
|
} else {
|
|
3862
3820
|
args.push('--no-global-cli', '-l', ...u.rest);
|
|
3863
3821
|
}
|
|
3864
|
-
const r =
|
|
3822
|
+
const r = runPackageManagerSync('npx', args, {
|
|
3865
3823
|
cwd: u.dir,
|
|
3866
3824
|
stdio: 'inherit',
|
|
3867
3825
|
env: { ...process.env, OXE_NO_PROMPT: '1' },
|
|
3868
|
-
shell: process.platform === 'win32',
|
|
3869
3826
|
});
|
|
3870
3827
|
if (r.error) {
|
|
3871
3828
|
console.error(`${red}Falha ao executar npx:${reset}`, r.error.message);
|
|
@@ -3924,7 +3881,7 @@ function parseCapabilitiesArgs(argv) {
|
|
|
3924
3881
|
}
|
|
3925
3882
|
|
|
3926
3883
|
/**
|
|
3927
|
-
* @typedef {{ help: boolean, dir: string, port: number, noOpen: boolean, readOnly: boolean, dumpContext: boolean, activeSession: string|null, parseError: boolean, unknownFlag: string }} DashboardOpts
|
|
3884
|
+
* @typedef {{ help: boolean, dir: string, port: number, noOpen: boolean, readOnly: boolean, dumpContext: boolean, json: boolean, activeSession: string|null, parseError: boolean, unknownFlag: string }} DashboardOpts
|
|
3928
3885
|
*/
|
|
3929
3886
|
|
|
3930
3887
|
/**
|
|
@@ -3985,6 +3942,7 @@ function parseDashboardArgs(argv) {
|
|
|
3985
3942
|
noOpen: false,
|
|
3986
3943
|
readOnly: false,
|
|
3987
3944
|
dumpContext: false,
|
|
3945
|
+
json: false,
|
|
3988
3946
|
activeSession: null,
|
|
3989
3947
|
parseError: false,
|
|
3990
3948
|
unknownFlag: '',
|
|
@@ -3993,10 +3951,15 @@ function parseDashboardArgs(argv) {
|
|
|
3993
3951
|
const a = argv[i];
|
|
3994
3952
|
if (a === '-h' || a === '--help') out.help = true;
|
|
3995
3953
|
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
3996
|
-
else if (a === '--port' && argv[i + 1]
|
|
3997
|
-
|
|
3954
|
+
else if (a === '--port' && argv[i + 1] !== undefined) {
|
|
3955
|
+
// Allow `--port 0` (OS picks an ephemeral port — used by hosts that embed
|
|
3956
|
+
// the dashboard and read the real port back from `--json`).
|
|
3957
|
+
const n = Number(argv[++i]);
|
|
3958
|
+
out.port = Number.isFinite(n) && n >= 0 ? n : 4173;
|
|
3959
|
+
} else if (a === '--no-open') out.noOpen = true;
|
|
3998
3960
|
else if (a === '--read-only') out.readOnly = true;
|
|
3999
3961
|
else if (a === '--dump-context') out.dumpContext = true;
|
|
3962
|
+
else if (a === '--json') out.json = true;
|
|
4000
3963
|
else if (a === '--session' && argv[i + 1]) out.activeSession = String(argv[++i]).replace(/\\/g, '/');
|
|
4001
3964
|
else if (!a.startsWith('-') && i === 0) out.dir = path.resolve(a);
|
|
4002
3965
|
else {
|
|
@@ -4357,7 +4320,7 @@ async function runDashboard(opts) {
|
|
|
4357
4320
|
return;
|
|
4358
4321
|
}
|
|
4359
4322
|
const c = useAnsiColors();
|
|
4360
|
-
printSection('OXE ▸ dashboard');
|
|
4323
|
+
if (!opts.json) printSection('OXE ▸ dashboard');
|
|
4361
4324
|
const server = oxeDashboard.createDashboardServer(target, { activeSession: opts.activeSession });
|
|
4362
4325
|
await new Promise((resolve, reject) => {
|
|
4363
4326
|
server.once('error', reject);
|
|
@@ -4366,6 +4329,24 @@ async function runDashboard(opts) {
|
|
|
4366
4329
|
const address = server.address();
|
|
4367
4330
|
const port = address && typeof address === 'object' ? address.port : opts.port;
|
|
4368
4331
|
const url = `http://127.0.0.1:${port}/`;
|
|
4332
|
+
// Host-integration mode: emit one stable, versioned line with the live URL/port
|
|
4333
|
+
// as soon as the server is listening, then keep serving until killed. Lets a
|
|
4334
|
+
// host (OXESpace) embed the dashboard in a webview without scraping the
|
|
4335
|
+
// human-readable banner. Pair with `--no-open --port 0` for an ephemeral port.
|
|
4336
|
+
if (opts.json) {
|
|
4337
|
+
console.log(JSON.stringify({
|
|
4338
|
+
oxeDashboardSchema: 1,
|
|
4339
|
+
projectRoot: path.resolve(target),
|
|
4340
|
+
url,
|
|
4341
|
+
port,
|
|
4342
|
+
readOnly: Boolean(opts.readOnly),
|
|
4343
|
+
}));
|
|
4344
|
+
if (!opts.noOpen) {
|
|
4345
|
+
try { openUrlInBrowser(url); } catch { /* host owns presentation */ }
|
|
4346
|
+
}
|
|
4347
|
+
await new Promise(() => {});
|
|
4348
|
+
return;
|
|
4349
|
+
}
|
|
4369
4350
|
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${target}${c ? reset : ''}`);
|
|
4370
4351
|
console.log(` ${c ? green : ''}URL:${c ? reset : ''} ${c ? cyan : ''}${url}${c ? reset : ''}`);
|
|
4371
4352
|
if (opts.readOnly) {
|
|
@@ -4383,6 +4364,163 @@ async function runDashboard(opts) {
|
|
|
4383
4364
|
await new Promise(() => {});
|
|
4384
4365
|
}
|
|
4385
4366
|
|
|
4367
|
+
/**
|
|
4368
|
+
* @typedef {{ help: boolean, dir: string, tail: number, since: string, activeSession: string|null, json: boolean, parseError: boolean, unknownFlag: string }} EventsOpts
|
|
4369
|
+
*/
|
|
4370
|
+
|
|
4371
|
+
/**
|
|
4372
|
+
* @param {string[]} argv
|
|
4373
|
+
* @returns {EventsOpts}
|
|
4374
|
+
*/
|
|
4375
|
+
function parseEventsArgs(argv) {
|
|
4376
|
+
/** @type {EventsOpts} */
|
|
4377
|
+
const out = {
|
|
4378
|
+
help: false,
|
|
4379
|
+
dir: process.cwd(),
|
|
4380
|
+
tail: 50,
|
|
4381
|
+
since: '',
|
|
4382
|
+
activeSession: null,
|
|
4383
|
+
json: false,
|
|
4384
|
+
parseError: false,
|
|
4385
|
+
unknownFlag: '',
|
|
4386
|
+
};
|
|
4387
|
+
for (let i = 0; i < argv.length; i++) {
|
|
4388
|
+
const a = argv[i];
|
|
4389
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
4390
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
4391
|
+
else if (a === '--tail') {
|
|
4392
|
+
// `--tail` alone keeps the default; `--tail N` overrides the count.
|
|
4393
|
+
const next = argv[i + 1];
|
|
4394
|
+
if (next && /^\d+$/.test(next)) out.tail = Number(argv[++i]);
|
|
4395
|
+
} else if (a === '--since' && argv[i + 1]) out.since = String(argv[++i]);
|
|
4396
|
+
else if (a === '--json') out.json = true;
|
|
4397
|
+
else if (a === '--session' && argv[i + 1]) out.activeSession = String(argv[++i]).replace(/\\/g, '/');
|
|
4398
|
+
else if (!a.startsWith('-') && i === 0) out.dir = path.resolve(a);
|
|
4399
|
+
else {
|
|
4400
|
+
out.parseError = true;
|
|
4401
|
+
out.unknownFlag = a;
|
|
4402
|
+
break;
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
return out;
|
|
4406
|
+
}
|
|
4407
|
+
|
|
4408
|
+
/**
|
|
4409
|
+
* Read-only projection of the append-only event log (.oxe/OXE-EVENTS.ndjson)
|
|
4410
|
+
* for host integrations. A host typically watches that file directly and then
|
|
4411
|
+
* calls `status --json --summary`; this command is the documented way to read
|
|
4412
|
+
* the tail of the log (optionally since a known event_id) without parsing the
|
|
4413
|
+
* whole file. Reuses the same SDK helpers the runtime uses.
|
|
4414
|
+
* @param {EventsOpts} opts
|
|
4415
|
+
*/
|
|
4416
|
+
function runEvents(opts) {
|
|
4417
|
+
const target = opts.dir;
|
|
4418
|
+
if (!fs.existsSync(target)) {
|
|
4419
|
+
if (opts.json) console.log(JSON.stringify({ oxeEventsSchema: 1, projectRoot: path.resolve(target), error: 'dir-not-found', events: [] }));
|
|
4420
|
+
else console.error(`${yellow}Diretório não encontrado: ${target}${reset}`);
|
|
4421
|
+
process.exit(1);
|
|
4422
|
+
}
|
|
4423
|
+
const stateText = fs.existsSync(oxeHealth.oxePaths(target).state)
|
|
4424
|
+
? fs.readFileSync(oxeHealth.oxePaths(target).state, 'utf8')
|
|
4425
|
+
: '';
|
|
4426
|
+
const activeSession = opts.activeSession || oxeHealth.parseActiveSession(stateText) || null;
|
|
4427
|
+
let events = oxeOperational.readEvents(target, activeSession);
|
|
4428
|
+
if (opts.since) {
|
|
4429
|
+
const idx = events.findIndex((e) => e && e.event_id === opts.since);
|
|
4430
|
+
if (idx >= 0) events = events.slice(idx + 1);
|
|
4431
|
+
}
|
|
4432
|
+
const tail = opts.tail > 0 ? events.slice(-opts.tail) : events;
|
|
4433
|
+
|
|
4434
|
+
if (opts.json) {
|
|
4435
|
+
console.log(JSON.stringify({
|
|
4436
|
+
oxeEventsSchema: 1,
|
|
4437
|
+
projectRoot: path.resolve(target),
|
|
4438
|
+
activeSession,
|
|
4439
|
+
summary: oxeOperational.summarizeEvents(events),
|
|
4440
|
+
events: tail,
|
|
4441
|
+
}));
|
|
4442
|
+
return;
|
|
4443
|
+
}
|
|
4444
|
+
|
|
4445
|
+
const c = useAnsiColors();
|
|
4446
|
+
printSection('OXE ▸ events');
|
|
4447
|
+
const summary = oxeOperational.summarizeEvents(events);
|
|
4448
|
+
console.log(` ${c ? green : ''}Total:${c ? reset : ''} ${summary.total}${activeSession ? ` ${c ? dim : ''}(sessão: ${activeSession})${c ? reset : ''}` : ''}`);
|
|
4449
|
+
if (!tail.length) {
|
|
4450
|
+
console.log(` ${c ? dim : ''}Sem eventos.${c ? reset : ''}\n`);
|
|
4451
|
+
return;
|
|
4452
|
+
}
|
|
4453
|
+
for (const e of tail) {
|
|
4454
|
+
const ts = e.timestamp ? String(e.timestamp).replace('T', ' ').replace(/\..*$/, '') : '';
|
|
4455
|
+
console.log(` ${c ? dim : ''}${ts}${c ? reset : ''} ${c ? cyan : ''}${e.type}${c ? reset : ''}${e.run_id ? ` ${c ? dim : ''}run=${e.run_id}${c ? reset : ''}` : ''}`);
|
|
4456
|
+
}
|
|
4457
|
+
console.log('');
|
|
4458
|
+
}
|
|
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
|
+
|
|
4386
4524
|
/**
|
|
4387
4525
|
* @param {RuntimeOpts} opts
|
|
4388
4526
|
*/
|
|
@@ -5473,6 +5611,8 @@ async function main() {
|
|
|
5473
5611
|
argv[0] === 'init-oxe' ||
|
|
5474
5612
|
argv[0] === 'context' ||
|
|
5475
5613
|
argv[0] === 'dashboard' ||
|
|
5614
|
+
argv[0] === 'events' ||
|
|
5615
|
+
argv[0] === 'map' ||
|
|
5476
5616
|
argv[0] === 'runtime' ||
|
|
5477
5617
|
argv[0] === 'azure' ||
|
|
5478
5618
|
argv[0] === 'uninstall' ||
|
|
@@ -5487,103 +5627,85 @@ async function main() {
|
|
|
5487
5627
|
argv.shift();
|
|
5488
5628
|
}
|
|
5489
5629
|
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
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;
|
|
5517
5657
|
|
|
5518
|
-
if (command === '
|
|
5519
|
-
const
|
|
5520
|
-
if (
|
|
5658
|
+
if (command === 'dashboard') {
|
|
5659
|
+
const d = parseDashboardArgs(argv);
|
|
5660
|
+
if (d.help) {
|
|
5521
5661
|
printBanner();
|
|
5522
5662
|
usage();
|
|
5523
5663
|
process.exit(0);
|
|
5524
5664
|
}
|
|
5525
|
-
if (
|
|
5526
|
-
printBanner();
|
|
5527
|
-
console.error(`${red}${u.conflictFlags}${reset}`);
|
|
5528
|
-
usage();
|
|
5529
|
-
process.exit(1);
|
|
5530
|
-
}
|
|
5531
|
-
if (u.parseError) {
|
|
5665
|
+
if (d.parseError) {
|
|
5532
5666
|
printBanner();
|
|
5533
|
-
console.error(`${red}Opção desconhecida:${reset} ${
|
|
5667
|
+
console.error(`${red}Opção desconhecida:${reset} ${d.unknownFlag}`);
|
|
5534
5668
|
usage();
|
|
5535
5669
|
process.exit(1);
|
|
5536
5670
|
}
|
|
5537
|
-
printBanner();
|
|
5538
|
-
|
|
5539
|
-
runUpdateVersionCheck(u);
|
|
5540
|
-
return;
|
|
5541
|
-
}
|
|
5542
|
-
if (!u.dryRun && !fs.existsSync(u.dir)) {
|
|
5543
|
-
console.error(`${yellow}Diretório não encontrado: ${u.dir}${reset}`);
|
|
5544
|
-
process.exit(1);
|
|
5545
|
-
}
|
|
5546
|
-
runUpdate(u);
|
|
5671
|
+
if (!d.json) printBanner();
|
|
5672
|
+
await runDashboard(d);
|
|
5547
5673
|
return;
|
|
5548
5674
|
}
|
|
5549
5675
|
|
|
5550
|
-
if (command === '
|
|
5551
|
-
const
|
|
5552
|
-
if (
|
|
5676
|
+
if (command === 'map') {
|
|
5677
|
+
const m = parseMapArgs(argv);
|
|
5678
|
+
if (m.help) {
|
|
5553
5679
|
printBanner();
|
|
5554
5680
|
usage();
|
|
5555
5681
|
process.exit(0);
|
|
5556
5682
|
}
|
|
5557
|
-
if (
|
|
5683
|
+
if (m.parseError) {
|
|
5558
5684
|
printBanner();
|
|
5559
|
-
console.error(`${red}Opção desconhecida:${reset} ${
|
|
5685
|
+
console.error(`${red}Opção desconhecida:${reset} ${m.unknownFlag}`);
|
|
5560
5686
|
usage();
|
|
5561
5687
|
process.exit(1);
|
|
5562
5688
|
}
|
|
5563
|
-
printBanner();
|
|
5564
|
-
|
|
5565
|
-
console.error(`${yellow}Diretório não encontrado: ${cap.dir}${reset}`);
|
|
5566
|
-
process.exit(1);
|
|
5567
|
-
}
|
|
5568
|
-
runCapabilities(cap);
|
|
5689
|
+
if (!m.json) printBanner();
|
|
5690
|
+
runMap(m);
|
|
5569
5691
|
return;
|
|
5570
5692
|
}
|
|
5571
5693
|
|
|
5572
|
-
if (command === '
|
|
5573
|
-
const
|
|
5574
|
-
if (
|
|
5694
|
+
if (command === 'events') {
|
|
5695
|
+
const e = parseEventsArgs(argv);
|
|
5696
|
+
if (e.help) {
|
|
5575
5697
|
printBanner();
|
|
5576
5698
|
usage();
|
|
5577
5699
|
process.exit(0);
|
|
5578
5700
|
}
|
|
5579
|
-
if (
|
|
5701
|
+
if (e.parseError) {
|
|
5580
5702
|
printBanner();
|
|
5581
|
-
console.error(`${red}Opção desconhecida:${reset} ${
|
|
5703
|
+
console.error(`${red}Opção desconhecida:${reset} ${e.unknownFlag}`);
|
|
5582
5704
|
usage();
|
|
5583
5705
|
process.exit(1);
|
|
5584
5706
|
}
|
|
5585
|
-
printBanner();
|
|
5586
|
-
|
|
5707
|
+
if (!e.json) printBanner();
|
|
5708
|
+
runEvents(e);
|
|
5587
5709
|
return;
|
|
5588
5710
|
}
|
|
5589
5711
|
|
|
@@ -5605,95 +5727,13 @@ async function main() {
|
|
|
5605
5727
|
return;
|
|
5606
5728
|
}
|
|
5607
5729
|
|
|
5608
|
-
if (command === 'runtime') {
|
|
5609
|
-
const runtime = parseRuntimeArgs(argv);
|
|
5610
|
-
if (runtime.help) {
|
|
5611
|
-
printBanner();
|
|
5612
|
-
usage();
|
|
5613
|
-
process.exit(0);
|
|
5614
|
-
}
|
|
5615
|
-
if (runtime.parseError) {
|
|
5616
|
-
printBanner();
|
|
5617
|
-
console.error(`${red}Opção desconhecida:${reset} ${runtime.unknownFlag}`);
|
|
5618
|
-
usage();
|
|
5619
|
-
process.exit(1);
|
|
5620
|
-
}
|
|
5621
|
-
printBanner();
|
|
5622
|
-
if (!fs.existsSync(runtime.dir)) {
|
|
5623
|
-
console.error(`${yellow}Diretório não encontrado: ${runtime.dir}${reset}`);
|
|
5624
|
-
process.exit(1);
|
|
5625
|
-
}
|
|
5626
|
-
runRuntime(runtime).catch((err) => {
|
|
5627
|
-
console.error(`${red}${err && err.message ? err.message : 'Falha ao executar runtime.'}${reset}`);
|
|
5628
|
-
process.exit(1);
|
|
5629
|
-
});
|
|
5630
|
-
return;
|
|
5631
|
-
}
|
|
5632
|
-
|
|
5633
5730
|
if (command === 'plugins') {
|
|
5634
5731
|
printBanner();
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
pluginsDir = path.resolve(pluginsArgv[++i]);
|
|
5641
|
-
pluginsArgv.splice(i - 1, 2);
|
|
5642
|
-
i -= 2;
|
|
5643
|
-
}
|
|
5644
|
-
}
|
|
5645
|
-
const c = useAnsiColors();
|
|
5646
|
-
const subCmd = pluginsArgv[0] || 'list';
|
|
5647
|
-
const pluginTarget = pluginsArgv[1] || '';
|
|
5648
|
-
|
|
5649
|
-
if (subCmd === 'list') {
|
|
5650
|
-
const result = oxePlugins.loadPlugins(pluginsDir);
|
|
5651
|
-
console.log(`\n ${c ? green : ''}Plugins carregados:${c ? reset : ''} ${result.plugins.length}`);
|
|
5652
|
-
for (const p of result.plugins) {
|
|
5653
|
-
console.log(` • ${p.name}${p.version ? ` (${p.version})` : ''} — hooks: ${Object.keys(p.hooks).join(', ')}`);
|
|
5654
|
-
}
|
|
5655
|
-
if (result.errors.length) {
|
|
5656
|
-
console.log(`\n ${c ? yellow : ''}Erros:${c ? reset : ''}`);
|
|
5657
|
-
for (const e of result.errors) {
|
|
5658
|
-
console.log(` ✗ ${e.file}: ${e.error}`);
|
|
5659
|
-
}
|
|
5660
|
-
}
|
|
5661
|
-
} else if (subCmd === 'install' && pluginTarget) {
|
|
5662
|
-
const src = pluginTarget.startsWith('npm:') ? pluginTarget.slice(4) : pluginTarget;
|
|
5663
|
-
const ver = pluginsArgv[2] || '';
|
|
5664
|
-
console.log(` Instalando plugin: ${src}${ver ? `@${ver}` : ''}...`);
|
|
5665
|
-
const result = oxePlugins.installNpmPlugin(pluginsDir, src, ver || undefined);
|
|
5666
|
-
if (result.ok) {
|
|
5667
|
-
console.log(` ${c ? green : ''}✓${c ? reset : ''} Instalado em: ${result.path}`);
|
|
5668
|
-
console.log(` ${c ? dim : ''}Adicione ao .oxe/config.json: "plugins": [{ "source": "npm:${src}" }]${c ? reset : ''}`);
|
|
5669
|
-
} else {
|
|
5670
|
-
console.error(` ${c ? red : ''}✗ Falha:${c ? reset : ''} ${result.error}`);
|
|
5671
|
-
process.exit(1);
|
|
5672
|
-
}
|
|
5673
|
-
} else if (subCmd === 'remove' && pluginTarget) {
|
|
5674
|
-
console.log(` ${c ? yellow : ''}Remove "${pluginTarget}" de .oxe/config.json → plugins[] manualmente.${c ? reset : ''}`);
|
|
5675
|
-
console.log(` ${c ? dim : ''}Arquivos npm: rm -rf .oxe/plugins/_npm/node_modules/${pluginTarget}${c ? reset : ''}`);
|
|
5676
|
-
} else {
|
|
5677
|
-
console.log(` ${c ? yellow : ''}Uso: oxe-cc plugins list | install <npm:pkg|path> | remove <id>${c ? reset : ''}`);
|
|
5678
|
-
}
|
|
5679
|
-
return;
|
|
5680
|
-
}
|
|
5681
|
-
|
|
5682
|
-
if (command === 'azure') {
|
|
5683
|
-
const azure = parseAzureArgs(argv);
|
|
5684
|
-
if (azure.help) {
|
|
5685
|
-
printBanner();
|
|
5686
|
-
usage();
|
|
5687
|
-
process.exit(0);
|
|
5688
|
-
}
|
|
5689
|
-
if (azure.parseError) {
|
|
5690
|
-
printBanner();
|
|
5691
|
-
console.error(`${red}Opção desconhecida:${reset} ${azure.unknownFlag}`);
|
|
5692
|
-
usage();
|
|
5693
|
-
process.exit(1);
|
|
5694
|
-
}
|
|
5695
|
-
printBanner();
|
|
5696
|
-
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);
|
|
5697
5737
|
return;
|
|
5698
5738
|
}
|
|
5699
5739
|
|
|
@@ -5746,33 +5786,9 @@ async function main() {
|
|
|
5746
5786
|
process.exit(0);
|
|
5747
5787
|
}
|
|
5748
5788
|
|
|
5749
|
-
|
|
5750
|
-
printBanner();
|
|
5751
|
-
}
|
|
5789
|
+
printBanner();
|
|
5752
5790
|
|
|
5753
5791
|
const target = opts.dir;
|
|
5754
|
-
if (command === 'doctor') {
|
|
5755
|
-
if (!fs.existsSync(target)) {
|
|
5756
|
-
console.error(`${yellow}Diretório não encontrado: ${target}${reset}`);
|
|
5757
|
-
process.exit(1);
|
|
5758
|
-
}
|
|
5759
|
-
runDoctor(target, { release: opts.releaseDoctor, json: opts.jsonOutput, writeManifest: opts.writeManifest });
|
|
5760
|
-
return;
|
|
5761
|
-
}
|
|
5762
|
-
|
|
5763
|
-
if (command === 'status') {
|
|
5764
|
-
if (!fs.existsSync(target)) {
|
|
5765
|
-
console.error(`${yellow}Diretório não encontrado: ${target}${reset}`);
|
|
5766
|
-
process.exit(1);
|
|
5767
|
-
}
|
|
5768
|
-
if (opts.statusFull) {
|
|
5769
|
-
runStatusFull(target);
|
|
5770
|
-
} else {
|
|
5771
|
-
runStatus(target, { json: opts.jsonOutput, hints: opts.statusHints });
|
|
5772
|
-
}
|
|
5773
|
-
return;
|
|
5774
|
-
}
|
|
5775
|
-
|
|
5776
5792
|
if (command === 'init-oxe') {
|
|
5777
5793
|
if (!opts.dryRun && !fs.existsSync(target)) {
|
|
5778
5794
|
console.error(`${yellow}Diretório não encontrado: ${target}${reset}`);
|
|
@@ -5785,8 +5801,8 @@ async function main() {
|
|
|
5785
5801
|
bootstrapOxe(target, { dryRun: opts.dryRun, force: opts.force });
|
|
5786
5802
|
printSummaryAndNextSteps(c0, {
|
|
5787
5803
|
bullets: opts.dryRun
|
|
5788
|
-
? ['[simulação] Seriam criados
|
|
5789
|
-
: ['.oxe/STATE.md, .oxe/config.json
|
|
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)'],
|
|
5790
5806
|
nextSteps: [
|
|
5791
5807
|
{ desc: 'Validar o projeto:', cmd: 'npx oxe-cc doctor' },
|
|
5792
5808
|
{ desc: 'Instalar integrações IDE/CLI (se ainda não fez):', cmd: 'npx oxe-cc@latest' },
|