oxe-cc 0.6.5 → 0.7.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/.cursor/commands/oxe-ask.md +11 -0
- package/.cursor/commands/oxe-capabilities.md +11 -0
- package/.cursor/commands/oxe-dashboard.md +11 -0
- package/.github/prompts/oxe-ask.prompt.md +12 -0
- package/.github/prompts/oxe-capabilities.prompt.md +12 -0
- package/.github/prompts/oxe-dashboard.prompt.md +12 -0
- package/CHANGELOG.md +33 -0
- package/README.md +189 -34
- package/assets/oxe-framework-artifacts-paper.png +0 -0
- package/bin/banner.txt +1 -1
- package/bin/lib/oxe-azure.cjs +1445 -0
- package/bin/lib/oxe-dashboard.cjs +588 -0
- package/bin/lib/oxe-install-resolve.cjs +4 -1
- package/bin/lib/oxe-operational.cjs +670 -0
- package/bin/lib/oxe-project-health.cjs +655 -118
- package/bin/oxe-cc.js +1404 -17
- package/commands/oxe/ask.md +14 -0
- package/commands/oxe/capabilities.md +13 -0
- package/commands/oxe/dashboard.md +14 -0
- package/lib/sdk/README.md +9 -7
- package/lib/sdk/index.cjs +56 -0
- package/lib/sdk/index.d.ts +73 -0
- package/oxe/templates/ACTIVE-RUN.template.json +32 -0
- package/oxe/templates/CAPABILITIES.template.md +7 -0
- package/oxe/templates/CAPABILITY.template.md +45 -0
- package/oxe/templates/CHECKPOINTS.template.md +7 -0
- package/oxe/templates/CONFIG.md +3 -2
- package/oxe/templates/EXECUTION-RUNTIME.template.md +68 -0
- package/oxe/templates/INVESTIGATION.template.md +38 -0
- package/oxe/templates/NOTES.template.md +16 -0
- package/oxe/templates/PLAN-REVIEW.template.md +31 -0
- package/oxe/templates/PLAN.template.md +22 -7
- package/oxe/templates/RESEARCH.template.md +11 -4
- package/oxe/templates/SPEC.template.md +6 -4
- package/oxe/templates/STATE.md +45 -7
- package/oxe/templates/config.template.json +14 -5
- package/oxe/workflows/ask.md +71 -0
- package/oxe/workflows/capabilities.md +23 -0
- package/oxe/workflows/dashboard.md +23 -0
- package/oxe/workflows/discuss.md +11 -9
- package/oxe/workflows/execute.md +46 -17
- package/oxe/workflows/help.md +273 -239
- package/oxe/workflows/next.md +10 -8
- package/oxe/workflows/obs.md +70 -20
- package/oxe/workflows/plan-agent.md +2 -1
- package/oxe/workflows/plan.md +70 -21
- package/oxe/workflows/quick.md +14 -6
- package/oxe/workflows/references/adaptive-discovery.md +27 -0
- package/oxe/workflows/references/flow-robustness-contract.md +80 -0
- package/oxe/workflows/research.md +12 -8
- package/oxe/workflows/retro.md +30 -5
- package/oxe/workflows/scan.md +1 -0
- package/oxe/workflows/spec.md +58 -33
- package/oxe/workflows/verify.md +40 -10
- package/package.json +2 -2
package/bin/oxe-cc.js
CHANGED
|
@@ -18,6 +18,9 @@ const oxeAgentInstall = require(path.join(__dirname, 'lib', 'oxe-agent-install.c
|
|
|
18
18
|
const oxeWorkflows = require(path.join(__dirname, 'lib', 'oxe-workflows.cjs'));
|
|
19
19
|
const oxeInstallResolve = require(path.join(__dirname, 'lib', 'oxe-install-resolve.cjs'));
|
|
20
20
|
const oxeNpmVersion = require(path.join(__dirname, 'lib', 'oxe-npm-version.cjs'));
|
|
21
|
+
const oxeDashboard = require(path.join(__dirname, 'lib', 'oxe-dashboard.cjs'));
|
|
22
|
+
const oxeOperational = require(path.join(__dirname, 'lib', 'oxe-operational.cjs'));
|
|
23
|
+
const oxeAzure = require(path.join(__dirname, 'lib', 'oxe-azure.cjs'));
|
|
21
24
|
|
|
22
25
|
/** Merge markers for ~/.copilot/copilot-instructions.md (bloco OXE). */
|
|
23
26
|
const OXE_INST_BEGIN = '<!-- oxe-cc:install-begin -->';
|
|
@@ -259,6 +262,7 @@ function buildUninstallFooter(u) {
|
|
|
259
262
|
`${p}${rm} integrações OXE no repositório (.cursor, .github, .claude, .copilot, .opencode, … conforme flags).`
|
|
260
263
|
);
|
|
261
264
|
}
|
|
265
|
+
if (u.globalCli) bullets.push(`${p}${rm} também o pacote npm global oxe-cc do PATH.`);
|
|
262
266
|
if (!u.noProject) {
|
|
263
267
|
bullets.push(
|
|
264
268
|
`${p}${u.dryRun ? 'Seriam removidas' : 'Removidas'} no repositório: .oxe/workflows, .oxe/templates, oxe/ e commands/oxe (o que existir).`
|
|
@@ -328,6 +332,8 @@ function parseInstallArgs(argv) {
|
|
|
328
332
|
jsonOutput: false,
|
|
329
333
|
/** Lembretes agregados scan/compact em `status`. */
|
|
330
334
|
statusHints: false,
|
|
335
|
+
/** Visão extendida CLI-first: coverage matrix + readiness gate no terminal. */
|
|
336
|
+
statusFull: false,
|
|
331
337
|
restPositional: [],
|
|
332
338
|
};
|
|
333
339
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -372,6 +378,7 @@ function parseInstallArgs(argv) {
|
|
|
372
378
|
out.dir = path.resolve(argv[++i]);
|
|
373
379
|
} else if (a === '--json') out.jsonOutput = true;
|
|
374
380
|
else if (a === '--hints') out.statusHints = true;
|
|
381
|
+
else if (a === '--full') out.statusFull = true;
|
|
375
382
|
else if (!a.startsWith('-')) out.restPositional.push(a);
|
|
376
383
|
else {
|
|
377
384
|
out.parseError = true;
|
|
@@ -1014,6 +1021,9 @@ function ensureGitignoreIgnoresOxeDir(projectRoot, opts = {}) {
|
|
|
1014
1021
|
function bootstrapOxe(target, opts) {
|
|
1015
1022
|
const oxeDir = path.join(target, '.oxe');
|
|
1016
1023
|
const codebaseDir = path.join(oxeDir, 'codebase');
|
|
1024
|
+
const capabilitiesDir = path.join(oxeDir, 'capabilities');
|
|
1025
|
+
const investigationsDir = path.join(oxeDir, 'investigations');
|
|
1026
|
+
const dashboardDir = path.join(oxeDir, 'dashboard');
|
|
1017
1027
|
const stateSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'STATE.md');
|
|
1018
1028
|
const stateDest = path.join(oxeDir, 'STATE.md');
|
|
1019
1029
|
const configSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'config.template.json');
|
|
@@ -1025,12 +1035,15 @@ function bootstrapOxe(target, opts) {
|
|
|
1025
1035
|
}
|
|
1026
1036
|
|
|
1027
1037
|
if (opts.dryRun) {
|
|
1028
|
-
console.log(`${dim}init${reset} ${oxeDir}/ (STATE.md, config.json, codebase
|
|
1038
|
+
console.log(`${dim}init${reset} ${oxeDir}/ (STATE.md, config.json, codebase/, capabilities/, investigations/, dashboard/, runs/, OXE-EVENTS.ndjson, ACTIVE-RUN.json)`);
|
|
1029
1039
|
ensureGitignoreIgnoresOxeDir(target, { dryRun: true });
|
|
1030
1040
|
return;
|
|
1031
1041
|
}
|
|
1032
1042
|
|
|
1033
1043
|
ensureDir(codebaseDir);
|
|
1044
|
+
ensureDir(capabilitiesDir);
|
|
1045
|
+
ensureDir(investigationsDir);
|
|
1046
|
+
ensureDir(dashboardDir);
|
|
1034
1047
|
|
|
1035
1048
|
if (!fs.existsSync(stateDest) || opts.force) {
|
|
1036
1049
|
copyFile(stateSrc, stateDest, { dryRun: false });
|
|
@@ -1067,14 +1080,107 @@ function bootstrapOxe(target, opts) {
|
|
|
1067
1080
|
ensureDir(workstreamsDir);
|
|
1068
1081
|
}
|
|
1069
1082
|
|
|
1083
|
+
const sessionsDir = path.join(oxeDir, 'sessions');
|
|
1084
|
+
if (!fs.existsSync(sessionsDir)) {
|
|
1085
|
+
ensureDir(sessionsDir);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
const globalDir = path.join(oxeDir, 'global');
|
|
1089
|
+
if (!fs.existsSync(globalDir)) {
|
|
1090
|
+
ensureDir(globalDir);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
const globalMilestonesDir = path.join(globalDir, 'milestones');
|
|
1094
|
+
if (!fs.existsSync(globalMilestonesDir)) {
|
|
1095
|
+
ensureDir(globalMilestonesDir);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
const lessonsSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'LESSONS.template.md');
|
|
1099
|
+
const lessonsDest = path.join(globalDir, 'LESSONS.md');
|
|
1100
|
+
if (fs.existsSync(lessonsSrc) && !fs.existsSync(lessonsDest)) {
|
|
1101
|
+
copyFile(lessonsSrc, lessonsDest, { dryRun: false });
|
|
1102
|
+
console.log(`${green}init${reset} ${lessonsDest}`);
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
const milestonesSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'MILESTONES.template.md');
|
|
1106
|
+
const milestonesDest = path.join(globalDir, 'MILESTONES.md');
|
|
1107
|
+
if (fs.existsSync(milestonesSrc) && !fs.existsSync(milestonesDest)) {
|
|
1108
|
+
copyFile(milestonesSrc, milestonesDest, { dryRun: false });
|
|
1109
|
+
console.log(`${green}init${reset} ${milestonesDest}`);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1070
1112
|
const memoryDir = path.join(oxeDir, 'memory');
|
|
1071
1113
|
if (!fs.existsSync(memoryDir)) {
|
|
1072
1114
|
ensureDir(memoryDir);
|
|
1073
1115
|
}
|
|
1074
1116
|
|
|
1117
|
+
const runsDir = path.join(oxeDir, 'runs');
|
|
1118
|
+
if (!fs.existsSync(runsDir)) {
|
|
1119
|
+
ensureDir(runsDir);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
const runtimeSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'EXECUTION-RUNTIME.template.md');
|
|
1123
|
+
const runtimeDest = path.join(oxeDir, 'EXECUTION-RUNTIME.md');
|
|
1124
|
+
if (fs.existsSync(runtimeSrc) && !fs.existsSync(runtimeDest)) {
|
|
1125
|
+
copyFile(runtimeSrc, runtimeDest, { dryRun: false });
|
|
1126
|
+
console.log(`${green}init${reset} ${runtimeDest}`);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
const activeRunSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'ACTIVE-RUN.template.json');
|
|
1130
|
+
const activeRunDest = path.join(oxeDir, 'ACTIVE-RUN.json');
|
|
1131
|
+
if (fs.existsSync(activeRunSrc) && !fs.existsSync(activeRunDest)) {
|
|
1132
|
+
copyFile(activeRunSrc, activeRunDest, { dryRun: false });
|
|
1133
|
+
console.log(`${green}init${reset} ${activeRunDest}`);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
const eventsDest = path.join(oxeDir, 'OXE-EVENTS.ndjson');
|
|
1137
|
+
if (!fs.existsSync(eventsDest)) {
|
|
1138
|
+
fs.writeFileSync(eventsDest, '', 'utf8');
|
|
1139
|
+
console.log(`${green}init${reset} ${eventsDest}`);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
const checkpointsSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'CHECKPOINTS.template.md');
|
|
1143
|
+
const checkpointsDest = path.join(oxeDir, 'CHECKPOINTS.md');
|
|
1144
|
+
if (fs.existsSync(checkpointsSrc) && !fs.existsSync(checkpointsDest)) {
|
|
1145
|
+
copyFile(checkpointsSrc, checkpointsDest, { dryRun: false });
|
|
1146
|
+
console.log(`${green}init${reset} ${checkpointsDest}`);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
const capabilitiesSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'CAPABILITIES.template.md');
|
|
1150
|
+
const capabilitiesDest = path.join(oxeDir, 'CAPABILITIES.md');
|
|
1151
|
+
if (fs.existsSync(capabilitiesSrc) && !fs.existsSync(capabilitiesDest)) {
|
|
1152
|
+
copyFile(capabilitiesSrc, capabilitiesDest, { dryRun: false });
|
|
1153
|
+
console.log(`${green}init${reset} ${capabilitiesDest}`);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
const investigationsIndexDest = path.join(oxeDir, 'INVESTIGATIONS.md');
|
|
1157
|
+
if (!fs.existsSync(investigationsIndexDest)) {
|
|
1158
|
+
fs.writeFileSync(
|
|
1159
|
+
investigationsIndexDest,
|
|
1160
|
+
'# OXE — Investigações\n\n| Data | Ficheiro | Objetivo | Modo | Estado |\n|------|----------|----------|------|--------|\n',
|
|
1161
|
+
'utf8'
|
|
1162
|
+
);
|
|
1163
|
+
console.log(`${green}init${reset} ${investigationsIndexDest}`);
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1075
1166
|
ensureGitignoreIgnoresOxeDir(target, { dryRun: false });
|
|
1076
1167
|
}
|
|
1077
1168
|
|
|
1169
|
+
/**
|
|
1170
|
+
* @param {string} url
|
|
1171
|
+
*/
|
|
1172
|
+
function openUrlInBrowser(url) {
|
|
1173
|
+
if (process.platform === 'win32') {
|
|
1174
|
+
spawnSync('cmd', ['/c', 'start', '', url], { stdio: 'ignore', detached: true, shell: false });
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
if (process.platform === 'darwin') {
|
|
1178
|
+
spawnSync('open', [url], { stdio: 'ignore', detached: true, shell: false });
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
spawnSync('xdg-open', [url], { stdio: 'ignore', detached: true, shell: false });
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1078
1184
|
/**
|
|
1079
1185
|
* Lembretes de rotina (scan/compact antigos) para `status --hints` ou JSON.
|
|
1080
1186
|
* @param {string} target
|
|
@@ -1120,6 +1226,7 @@ function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
|
|
|
1120
1226
|
const { config } = oxeHealth.loadOxeConfigMerged(target);
|
|
1121
1227
|
|
|
1122
1228
|
console.log(`\n ${c ? cyan : ''}▸ Coerência .oxe/ e config${reset}`);
|
|
1229
|
+
console.log(` ${c ? dim : ''}Saúde lógica:${c ? reset : ''} ${r.healthStatus}`);
|
|
1123
1230
|
|
|
1124
1231
|
if (r.configParseError) {
|
|
1125
1232
|
console.log(` ${red}FALHA${reset} config.json: ${r.configParseError}`);
|
|
@@ -1138,6 +1245,34 @@ function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
|
|
|
1138
1245
|
if (r.phase) {
|
|
1139
1246
|
console.log(` ${c ? dim : ''}Fase (STATE.md):${c ? reset : ''} ${r.phase}`);
|
|
1140
1247
|
}
|
|
1248
|
+
if (r.activeSession) {
|
|
1249
|
+
console.log(` ${c ? dim : ''}Sessão ativa:${c ? reset : ''} ${r.activeSession}`);
|
|
1250
|
+
}
|
|
1251
|
+
if (r.planReviewStatus) {
|
|
1252
|
+
console.log(` ${c ? dim : ''}Revisão do plano:${c ? reset : ''} ${r.planReviewStatus}`);
|
|
1253
|
+
}
|
|
1254
|
+
if (r.activeRun && r.activeRun.run_id) {
|
|
1255
|
+
console.log(` ${c ? dim : ''}Run ativo:${c ? reset : ''} ${r.activeRun.run_id} (${r.activeRun.status || 'planned'})`);
|
|
1256
|
+
}
|
|
1257
|
+
if (r.eventsSummary) {
|
|
1258
|
+
console.log(` ${c ? dim : ''}Tracing:${c ? reset : ''} ${r.eventsSummary.total} evento(s)`);
|
|
1259
|
+
}
|
|
1260
|
+
if (r.planSelfEvaluation && r.planSelfEvaluation.hasSection) {
|
|
1261
|
+
const best = r.planSelfEvaluation.bestPlan || '—';
|
|
1262
|
+
const conf =
|
|
1263
|
+
typeof r.planSelfEvaluation.confidence === 'number' ? `${r.planSelfEvaluation.confidence}%` : '—';
|
|
1264
|
+
console.log(` ${c ? dim : ''}Plano (autoavaliação):${c ? reset : ''} melhor=${best} | confiança=${conf}`);
|
|
1265
|
+
}
|
|
1266
|
+
if (r.azureActive && r.azure) {
|
|
1267
|
+
console.log(` ${c ? dim : ''}Azure:${c ? reset : ''} ${r.azure.authStatus && r.azure.authStatus.login_active ? 'login ativo' : 'sem login'} | subscription=${r.azure.profile && (r.azure.profile.subscription_name || r.azure.profile.subscription_id) || '—'}`);
|
|
1268
|
+
console.log(` ${c ? dim : ''}Azure inventory:${c ? reset : ''} total=${r.azure.inventorySummary ? r.azure.inventorySummary.total : 0} | pendências=${r.azure.pendingOperations || 0}`);
|
|
1269
|
+
if (r.azure.inventoryStale && r.azure.inventoryStale.stale) {
|
|
1270
|
+
console.log(` ${yellow}AVISO${reset} Inventário Azure stale — rode ${cyan}npx oxe-cc azure sync${reset}`);
|
|
1271
|
+
}
|
|
1272
|
+
for (const warning of r.azure.warnings || []) {
|
|
1273
|
+
console.log(` ${yellow}AVISO${reset} ${warning}`);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1141
1276
|
|
|
1142
1277
|
if (!skipAge) {
|
|
1143
1278
|
if (config.scan_max_age_days > 0 && r.scanDate && r.stale.stale) {
|
|
@@ -1175,6 +1310,24 @@ function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
|
|
|
1175
1310
|
for (const w of r.phaseWarn) {
|
|
1176
1311
|
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1177
1312
|
}
|
|
1313
|
+
for (const w of r.runtimeWarn) {
|
|
1314
|
+
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1315
|
+
}
|
|
1316
|
+
for (const w of r.reviewWarn) {
|
|
1317
|
+
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1318
|
+
}
|
|
1319
|
+
for (const w of r.capabilityWarn) {
|
|
1320
|
+
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1321
|
+
}
|
|
1322
|
+
for (const w of r.investigationWarn) {
|
|
1323
|
+
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1324
|
+
}
|
|
1325
|
+
for (const w of r.sessionWarn) {
|
|
1326
|
+
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1327
|
+
}
|
|
1328
|
+
for (const w of r.installWarn) {
|
|
1329
|
+
console.log(` ${yellow}AVISO${reset} ${w}`);
|
|
1330
|
+
}
|
|
1178
1331
|
if (r.summaryGapWarn) {
|
|
1179
1332
|
console.log(` ${yellow}AVISO${reset} ${r.summaryGapWarn}`);
|
|
1180
1333
|
}
|
|
@@ -1186,15 +1339,100 @@ function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
|
|
|
1186
1339
|
}
|
|
1187
1340
|
}
|
|
1188
1341
|
|
|
1342
|
+
/**
|
|
1343
|
+
* Imprime uma célula de coverage com cor ANSI.
|
|
1344
|
+
* @param {boolean} exists
|
|
1345
|
+
* @param {string} label
|
|
1346
|
+
*/
|
|
1347
|
+
function coverageCell(exists, label) {
|
|
1348
|
+
const c = useAnsiColors();
|
|
1349
|
+
return exists ? `${c ? green : ''}✓ ${label}${c ? reset : ''}` : `${c ? dim : ''}✗ ${label}${c ? reset : ''}`;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
/**
|
|
1353
|
+
* Visão CLI-first: health + coverage matrix + readiness gate no terminal.
|
|
1354
|
+
* @param {string} target
|
|
1355
|
+
*/
|
|
1356
|
+
function runStatusFull(target) {
|
|
1357
|
+
const c = useAnsiColors();
|
|
1358
|
+
const report = oxeHealth.buildHealthReport(target);
|
|
1359
|
+
const p = oxeHealth.oxePaths(target);
|
|
1360
|
+
const activeSession = report.activeSession || null;
|
|
1361
|
+
let sp = p;
|
|
1362
|
+
if (activeSession) {
|
|
1363
|
+
sp = oxeHealth.scopedOxePaths(target, activeSession);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
printSection('OXE ▸ status --full');
|
|
1367
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${target}${c ? reset : ''}`);
|
|
1368
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || 'modo legado'}${c ? reset : ''}`);
|
|
1369
|
+
console.log(` ${c ? green : ''}Fase:${c ? reset : ''} ${report.phase || '—'}`);
|
|
1370
|
+
|
|
1371
|
+
const healthColor = report.healthStatus === 'healthy' ? green : report.healthStatus === 'warning' ? yellow : red;
|
|
1372
|
+
console.log(` ${c ? green : ''}Saúde:${c ? reset : ''} ${c ? healthColor : ''}${report.healthStatus}${c ? reset : ''}`);
|
|
1373
|
+
|
|
1374
|
+
// Coverage matrix
|
|
1375
|
+
const specPath = activeSession && sp.spec ? sp.spec : p.spec;
|
|
1376
|
+
const planPath = activeSession && sp.plan ? sp.plan : p.plan;
|
|
1377
|
+
const verifyPath = activeSession && sp.verify ? sp.verify : p.verify;
|
|
1378
|
+
const specExists = fs.existsSync(specPath);
|
|
1379
|
+
const planExists = fs.existsSync(planPath);
|
|
1380
|
+
const verifyExists = fs.existsSync(verifyPath);
|
|
1381
|
+
const lessonsExists = fs.existsSync(p.globalLessons || p.lessons);
|
|
1382
|
+
const codebaseExists = fs.existsSync(p.codebase);
|
|
1383
|
+
|
|
1384
|
+
console.log(`\n ${c ? yellow : ''}Coverage matrix${c ? reset : ''}`);
|
|
1385
|
+
console.log(` ${coverageCell(codebaseExists, 'codebase scan')} ${coverageCell(specExists, 'SPEC.md')} ${coverageCell(planExists, 'PLAN.md')} ${coverageCell(verifyExists, 'VERIFY.md')} ${coverageCell(lessonsExists, 'LESSONS.md')}`);
|
|
1386
|
+
|
|
1387
|
+
// Readiness gate
|
|
1388
|
+
const ready = specExists && planExists && !report.planWarn.length && !report.runtimeWarn.length;
|
|
1389
|
+
const gateColor = ready ? green : yellow;
|
|
1390
|
+
console.log(`\n ${c ? yellow : ''}Readiness gate${c ? reset : ''}`);
|
|
1391
|
+
console.log(` ${c ? gateColor : ''}${ready ? '✓ Pronto para executar' : '✗ Não pronto para executar'}${c ? reset : ''}`);
|
|
1392
|
+
if (!specExists) console.log(` ${c ? dim : ''} • SPEC.md ausente — rode /oxe-spec${c ? reset : ''}`);
|
|
1393
|
+
if (!planExists) console.log(` ${c ? dim : ''} • PLAN.md ausente — rode /oxe-plan${c ? reset : ''}`);
|
|
1394
|
+
if (report.planWarn.length) {
|
|
1395
|
+
for (const w of report.planWarn) {
|
|
1396
|
+
console.log(` ${c ? yellow : ''} • ${w}${c ? reset : ''}`);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// Active run summary
|
|
1401
|
+
if (report.activeRun) {
|
|
1402
|
+
const ar = report.activeRun;
|
|
1403
|
+
console.log(`\n ${c ? yellow : ''}Active run${c ? reset : ''}`);
|
|
1404
|
+
console.log(` ${c ? dim : ''}Run:${c ? reset : ''} ${ar.run_id || '—'} ${c ? dim : ''}Estado:${c ? reset : ''} ${ar.status || '—'} ${c ? dim : ''}Onda:${c ? reset : ''} ${ar.current_wave != null ? ar.current_wave : '—'}`);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
// Plan self-evaluation
|
|
1408
|
+
if (report.planSelfEvaluation) {
|
|
1409
|
+
const pse = report.planSelfEvaluation;
|
|
1410
|
+
console.log(`\n ${c ? yellow : ''}Autoavaliação do plano${c ? reset : ''}`);
|
|
1411
|
+
if (pse.best_plan_current != null) {
|
|
1412
|
+
const bestColor = pse.best_plan_current ? green : red;
|
|
1413
|
+
console.log(` ${c ? dim : ''}Melhor plano atual:${c ? reset : ''} ${c ? bestColor : ''}${pse.best_plan_current ? 'sim' : 'não'}${c ? reset : ''}`);
|
|
1414
|
+
}
|
|
1415
|
+
if (pse.confidence != null) {
|
|
1416
|
+
const confColor = Number(pse.confidence) >= 70 ? green : Number(pse.confidence) >= 50 ? yellow : red;
|
|
1417
|
+
console.log(` ${c ? dim : ''}Confiança:${c ? reset : ''} ${c ? confColor : ''}${pse.confidence}%${c ? reset : ''}`);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
console.log(`\n ${c ? dim : ''}Próximo passo:${c ? reset : ''} ${c ? cyan : ''}${report.next && report.next.cursorCmd ? report.next.cursorCmd : '—'}${c ? reset : ''}`);
|
|
1422
|
+
console.log(` ${c ? dim : ''}Motivo:${c ? reset : ''} ${report.next && report.next.reason ? report.next.reason : '—'}`);
|
|
1423
|
+
console.log(`\n ${c ? dim : ''}Para visão operacional completa (web): ${cyan}oxe-cc dashboard${c ? reset : ''}`);
|
|
1424
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} status --full concluído.\n`);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1189
1427
|
/**
|
|
1190
1428
|
* @param {string} target
|
|
1191
|
-
* @param {{ json?: boolean, hints?: boolean }} [opts]
|
|
1429
|
+
* @param {{ json?: boolean, hints?: boolean, full?: boolean }} [opts]
|
|
1192
1430
|
*/
|
|
1193
1431
|
function runStatus(target, opts = {}) {
|
|
1194
1432
|
const { config } = oxeHealth.loadOxeConfigMerged(target);
|
|
1195
|
-
const next = oxeHealth.suggestNextStep(target, { discuss_before_plan: config.discuss_before_plan });
|
|
1196
1433
|
const report = oxeHealth.buildHealthReport(target);
|
|
1197
1434
|
const routineHints = collectOxeRoutineHints(target, report, config);
|
|
1435
|
+
const next = report.next;
|
|
1198
1436
|
|
|
1199
1437
|
if (opts.json) {
|
|
1200
1438
|
/** @type {Record<string, unknown>} */
|
|
@@ -1206,15 +1444,30 @@ function runStatus(target, opts = {}) {
|
|
|
1206
1444
|
reason: report.next.reason,
|
|
1207
1445
|
artifacts: report.next.artifacts,
|
|
1208
1446
|
phase: report.phase,
|
|
1447
|
+
healthStatus: report.healthStatus,
|
|
1448
|
+
activeSession: report.activeSession,
|
|
1209
1449
|
scanDate: report.scanDate,
|
|
1210
1450
|
staleScan: report.stale,
|
|
1211
1451
|
compactDate: report.compactDate,
|
|
1212
|
-
|
|
1213
|
-
|
|
1452
|
+
staleCompact: report.staleCompact,
|
|
1453
|
+
planSelfEvaluation: report.planSelfEvaluation,
|
|
1454
|
+
planReviewStatus: report.planReviewStatus,
|
|
1455
|
+
activeRun: report.activeRun,
|
|
1456
|
+
eventsSummary: report.eventsSummary,
|
|
1457
|
+
memoryLayers: report.memoryLayers,
|
|
1458
|
+
azureActive: report.azureActive,
|
|
1459
|
+
azure: report.azure,
|
|
1460
|
+
diagnostics: {
|
|
1214
1461
|
configParseError: report.configParseError,
|
|
1215
1462
|
typeErrors: report.typeErrors,
|
|
1216
1463
|
unknownConfigKeys: report.unknownConfigKeys,
|
|
1217
1464
|
phaseWarnings: report.phaseWarn,
|
|
1465
|
+
runtimeWarnings: report.runtimeWarn,
|
|
1466
|
+
reviewWarnings: report.reviewWarn,
|
|
1467
|
+
capabilityWarnings: report.capabilityWarn,
|
|
1468
|
+
investigationWarnings: report.investigationWarn,
|
|
1469
|
+
sessionWarnings: report.sessionWarn,
|
|
1470
|
+
installWarnings: report.installWarn,
|
|
1218
1471
|
summaryGapWarning: report.summaryGapWarn,
|
|
1219
1472
|
specWarnings: report.specWarn,
|
|
1220
1473
|
planWarnings: report.planWarn,
|
|
@@ -1255,7 +1508,10 @@ function runStatus(target, opts = {}) {
|
|
|
1255
1508
|
console.log(` ${c ? dim : ''}Motivo:${c ? reset : ''} ${next.reason}`);
|
|
1256
1509
|
|
|
1257
1510
|
printSummaryAndNextSteps(c, {
|
|
1258
|
-
bullets: [
|
|
1511
|
+
bullets: [
|
|
1512
|
+
`Saúde lógica: ${report.healthStatus}`,
|
|
1513
|
+
`Artefatos em jogo: ${next.artifacts.join(', ')}`,
|
|
1514
|
+
],
|
|
1259
1515
|
nextSteps: [
|
|
1260
1516
|
{ desc: 'Diagnóstico completo (inclui pacote de workflows):', cmd: 'npx oxe-cc doctor' },
|
|
1261
1517
|
{ desc: 'Ação sugerida no agente:', cmd: next.cursorCmd },
|
|
@@ -1367,13 +1623,17 @@ function runDoctor(target) {
|
|
|
1367
1623
|
}
|
|
1368
1624
|
|
|
1369
1625
|
printOxeHealthDiagnostics(target, c);
|
|
1370
|
-
|
|
1371
|
-
|
|
1626
|
+
const report = oxeHealth.buildHealthReport(target);
|
|
1627
|
+
const statusColor = report.healthStatus === 'healthy' ? green : report.healthStatus === 'warning' ? yellow : red;
|
|
1628
|
+
console.log(`\n ${statusColor}Diagnóstico ${report.healthStatus}${reset}`);
|
|
1629
|
+
if (report.healthStatus === 'broken') {
|
|
1630
|
+
process.exitCode = 1;
|
|
1631
|
+
}
|
|
1372
1632
|
printSummaryAndNextSteps(c, {
|
|
1373
1633
|
bullets: [
|
|
1374
1634
|
`Projeto em ${target}`,
|
|
1375
1635
|
`Workflows conferidos em ${wfLabel}`,
|
|
1376
|
-
|
|
1636
|
+
`Saúde lógica: ${report.healthStatus}`,
|
|
1377
1637
|
],
|
|
1378
1638
|
nextSteps: [
|
|
1379
1639
|
{ desc: 'Mapear ou atualizar o codebase no agente:', cmd: '/oxe-scan' },
|
|
@@ -1413,6 +1673,98 @@ function installGlobalCliPackage() {
|
|
|
1413
1673
|
return false;
|
|
1414
1674
|
}
|
|
1415
1675
|
|
|
1676
|
+
/**
|
|
1677
|
+
* `npm uninstall -g oxe-cc` com a mesma semântica cross-platform do instalador.
|
|
1678
|
+
* @returns {boolean}
|
|
1679
|
+
*/
|
|
1680
|
+
function uninstallGlobalCliPackage() {
|
|
1681
|
+
const name = readPkgName();
|
|
1682
|
+
const c = useAnsiColors();
|
|
1683
|
+
const dimOrEmpty = c ? dim : '';
|
|
1684
|
+
const resetOrEmpty = c ? reset : '';
|
|
1685
|
+
console.log(`\n ${dimOrEmpty}npm uninstall -g ${name}${resetOrEmpty}\n`);
|
|
1686
|
+
const r = spawnSync('npm', ['uninstall', '-g', name], {
|
|
1687
|
+
stdio: 'inherit',
|
|
1688
|
+
shell: true,
|
|
1689
|
+
env: process.env,
|
|
1690
|
+
});
|
|
1691
|
+
if (r.status === 0) {
|
|
1692
|
+
console.log(
|
|
1693
|
+
`\n ${c ? green : ''}✓${c ? reset : ''} pacote global ${c ? cyan : ''}${name}${c ? reset : ''} removido do npm global.\n`
|
|
1694
|
+
);
|
|
1695
|
+
return true;
|
|
1696
|
+
}
|
|
1697
|
+
console.log(
|
|
1698
|
+
`\n ${c ? yellow : ''}⚠${c ? reset : ''} npm uninstall -g falhou. Remova manualmente: ${c ? cyan : ''}npm uninstall -g ${name}${c ? reset : ''}\n`
|
|
1699
|
+
);
|
|
1700
|
+
return false;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
/**
|
|
1704
|
+
* Best-effort: detecta se esta execução vem de uma instalação global do npm.
|
|
1705
|
+
* Usa `npm root -g` para evitar confundir execução local do repositório com pacote global.
|
|
1706
|
+
* @returns {boolean}
|
|
1707
|
+
*/
|
|
1708
|
+
function isRunningFromGlobalNpmInstall() {
|
|
1709
|
+
try {
|
|
1710
|
+
const r = spawnSync('npm', ['root', '-g'], {
|
|
1711
|
+
encoding: 'utf8',
|
|
1712
|
+
shell: true,
|
|
1713
|
+
env: process.env,
|
|
1714
|
+
});
|
|
1715
|
+
if (r.status !== 0) return false;
|
|
1716
|
+
const root = String(r.stdout || '').trim();
|
|
1717
|
+
if (!root) return false;
|
|
1718
|
+
const rel = path.relative(path.resolve(root), PKG_ROOT);
|
|
1719
|
+
return rel && !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
1720
|
+
} catch {
|
|
1721
|
+
return false;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
/** @returns {string[]} */
|
|
1726
|
+
function updateForwardedInstallFlags() {
|
|
1727
|
+
return [
|
|
1728
|
+
'--cursor',
|
|
1729
|
+
'--copilot',
|
|
1730
|
+
'--copilot-cli',
|
|
1731
|
+
'--all-agents',
|
|
1732
|
+
'--opencode',
|
|
1733
|
+
'--gemini',
|
|
1734
|
+
'--codex',
|
|
1735
|
+
'--windsurf',
|
|
1736
|
+
'--antigravity',
|
|
1737
|
+
'--vscode',
|
|
1738
|
+
'--no-commands',
|
|
1739
|
+
'--no-agents',
|
|
1740
|
+
'--no-init-oxe',
|
|
1741
|
+
'--oxe-only',
|
|
1742
|
+
'--global',
|
|
1743
|
+
'--local',
|
|
1744
|
+
'--ide-global',
|
|
1745
|
+
'--ide-local',
|
|
1746
|
+
'--global-cli',
|
|
1747
|
+
'-g',
|
|
1748
|
+
'--no-global-cli',
|
|
1749
|
+
'-l',
|
|
1750
|
+
'--no-install-config',
|
|
1751
|
+
'--force',
|
|
1752
|
+
'-f',
|
|
1753
|
+
'--all',
|
|
1754
|
+
'-a',
|
|
1755
|
+
'--config-dir',
|
|
1756
|
+
'-c',
|
|
1757
|
+
];
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
/**
|
|
1761
|
+
* @param {string[]} rest
|
|
1762
|
+
* @returns {boolean}
|
|
1763
|
+
*/
|
|
1764
|
+
function updateArgsExplicitlyControlGlobalCli(rest) {
|
|
1765
|
+
return rest.includes('--global-cli') || rest.includes('-g') || rest.includes('--no-global-cli') || rest.includes('-l');
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1416
1768
|
/**
|
|
1417
1769
|
* After copying OXE into the project: optionally install the CLI globally (pergunta interativa ou flags).
|
|
1418
1770
|
* @param {InstallOpts} opts
|
|
@@ -1496,6 +1848,10 @@ ${green}Uso:${reset}
|
|
|
1496
1848
|
npx oxe-cc doctor [opções] [pasta-do-projeto]
|
|
1497
1849
|
npx oxe-cc status [opções] [pasta-do-projeto]
|
|
1498
1850
|
npx oxe-cc init-oxe [opções] [pasta-do-projeto]
|
|
1851
|
+
npx oxe-cc dashboard [opções] [pasta-do-projeto]
|
|
1852
|
+
npx oxe-cc runtime <status|start|pause|resume|replay> [opções] [pasta-do-projeto]
|
|
1853
|
+
npx oxe-cc azure <status|doctor|auth|sync|find|servicebus|eventgrid|sql|operations> [opções] [pasta-do-projeto]
|
|
1854
|
+
npx oxe-cc capabilities <list|install|remove|update> [opções] [id]
|
|
1499
1855
|
npx oxe-cc uninstall [opções] [pasta-do-projeto]
|
|
1500
1856
|
npx oxe-cc update [opções] [argumentos extras…]
|
|
1501
1857
|
|
|
@@ -1504,6 +1860,7 @@ ${green}uninstall${reset} (remove OXE da pasta do usuário + pastas de workflows
|
|
|
1504
1860
|
--all-agents também remove ficheiros multi-plataforma (com --copilot-cli implícito)
|
|
1505
1861
|
--ide-local remove integrações IDE neste repositório (.cursor, .github, .claude, .copilot, …)
|
|
1506
1862
|
--ide-only não apagar .oxe/workflows, oxe/, etc. no projeto
|
|
1863
|
+
--global-cli, -g também executa npm uninstall -g oxe-cc
|
|
1507
1864
|
--config-dir <caminho> com exatamente uma flag IDE acima (não combina com --ide-local)
|
|
1508
1865
|
--dry-run
|
|
1509
1866
|
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
@@ -1513,9 +1870,70 @@ ${green}update${reset} (executa npx oxe-cc@latest --force na pasta do projeto)
|
|
|
1513
1870
|
--if-newer só executa o npx se existir versão mais nova no npm (falha de rede/registry: saída 2, sem npx)
|
|
1514
1871
|
--dir <pasta> pasta em que o npx roda (padrão: atual; ignorada com --check)
|
|
1515
1872
|
--dry-run mostra o comando sem executar
|
|
1516
|
-
[argumentos extras…] repassados ao oxe-cc (ex.: --cursor --global)
|
|
1873
|
+
[argumentos extras…] repassados ao oxe-cc (ex.: --cursor --global, --ide-local, --global-cli)
|
|
1517
1874
|
${dim}CI / sem rede:${reset} OXE_UPDATE_SKIP_REGISTRY=1 desativa consultas (--check sai 2; --if-newer sai 2 sem npx)
|
|
1518
1875
|
|
|
1876
|
+
${green}dashboard${reset} (interface web local para revisão e aprovação do plano)
|
|
1877
|
+
--port <número> porta local (padrão: 4173)
|
|
1878
|
+
--no-open não abre o browser automaticamente
|
|
1879
|
+
--session <sessions/sNNN-slug> força visualização de uma sessão específica
|
|
1880
|
+
--dump-context imprime JSON consolidado e sai
|
|
1881
|
+
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
1882
|
+
|
|
1883
|
+
${green}runtime${reset} (controle operacional explícito do ACTIVE-RUN)
|
|
1884
|
+
status mostra o run ativo resolvido para a sessão atual
|
|
1885
|
+
start cria um novo run com tracing inicial
|
|
1886
|
+
pause pausa o run ativo e preserva o cursor
|
|
1887
|
+
resume retoma o run ativo
|
|
1888
|
+
replay marca replay parcial por onda ou tarefa
|
|
1889
|
+
--session <sessions/sNNN-slug> força sessão específica
|
|
1890
|
+
--wave <número> fixa onda atual/cursor
|
|
1891
|
+
--task <Tn> fixa tarefa atual/cursor
|
|
1892
|
+
--mode <complete|wave|task> modo operacional do cursor
|
|
1893
|
+
--reason <texto> motivo explícito da transição
|
|
1894
|
+
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
1895
|
+
|
|
1896
|
+
${green}azure${reset} (provider Azure nativo via Azure CLI no Windows)
|
|
1897
|
+
status estado compacto: CLI, login, subscription, inventário, pendências
|
|
1898
|
+
doctor valida Azure CLI, login, subscription e inventário
|
|
1899
|
+
auth login [--tenant <id>] login interativo via Azure CLI (Entra ID: use --tenant <tenant-id>)
|
|
1900
|
+
auth whoami mostra identidade, tenant, subscription e cloud
|
|
1901
|
+
auth set-subscription --subscription <id|nome>
|
|
1902
|
+
fixa a subscription operacional do projeto
|
|
1903
|
+
Fluxo corporativo: auth login --tenant <tenant-id> → auth set-subscription --subscription <dev-sub-id>
|
|
1904
|
+
sync sincroniza inventário via Azure Resource Graph
|
|
1905
|
+
sync --diff sincroniza e mostra recursos adicionados/removidos
|
|
1906
|
+
find <texto> busca recursos no inventário local materializado
|
|
1907
|
+
find <texto> --type <tipo> filtra por tipo de serviço (ex.: servicebus, eventgrid, sql)
|
|
1908
|
+
find <texto> --filter-rg <rg> filtra por resource group
|
|
1909
|
+
servicebus <list|show|plan|apply> namespace, queue, topic, subscription
|
|
1910
|
+
eventgrid <list|show|plan|apply> topic, system-topic, event-subscription
|
|
1911
|
+
sql <list|show|plan|apply> server, database, firewall-rule
|
|
1912
|
+
operations list histórico de operações planejadas/aplicadas/pendentes
|
|
1913
|
+
--kind <tipo> ex.: namespace, queue, topic, system-topic, database
|
|
1914
|
+
--resource-group <rg> resource group alvo
|
|
1915
|
+
--name <nome> nome principal do recurso
|
|
1916
|
+
--namespace <nome> namespace Service Bus
|
|
1917
|
+
--topic-name <nome> topic Service Bus
|
|
1918
|
+
--subscription-name <nome> subscription de topic Service Bus
|
|
1919
|
+
--source-resource-id <id> origem de event subscription
|
|
1920
|
+
--endpoint <url|id> endpoint de Event Grid
|
|
1921
|
+
--server <nome> / --database <nome> recursos Azure SQL
|
|
1922
|
+
--location <região> localização alvo
|
|
1923
|
+
--admin-user <user> admin do SQL server (plan/apply)
|
|
1924
|
+
--admin-password-env <ENV> variável de ambiente com password do SQL admin
|
|
1925
|
+
--start-ip-address / --end-ip-address faixa de firewall rule para Azure SQL
|
|
1926
|
+
--approve aplica mutação já planejada após checkpoint formal
|
|
1927
|
+
--dry-run pré-visualiza comando sem executar nem criar artefatos
|
|
1928
|
+
--diff (sync) exibe diff de recursos adicionados/removidos
|
|
1929
|
+
--type <tipo> (find) filtra por família de serviço
|
|
1930
|
+
--filter-rg <rg> (find) filtra por resource group
|
|
1931
|
+
--tenant <id> (auth login) tenant Entra ID para contas corporativas
|
|
1932
|
+
--vpn-confirmed confirma conexão VPN quando vpn_required está configurado
|
|
1933
|
+
--override-policy override explícito para policy deny_unless_overridden
|
|
1934
|
+
--session <sessions/sNNN-slug> associa a operação ao runtime da sessão ativa
|
|
1935
|
+
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
1936
|
+
|
|
1519
1937
|
${green}Opções da instalação:${reset}
|
|
1520
1938
|
--cursor Copia comandos e regras para ~/.cursor (padrão com --all)
|
|
1521
1939
|
--copilot Mescla instruções + prompts em ~/.copilot (global) ou .github/ (com --ide-local)
|
|
@@ -1555,6 +1973,13 @@ ${green}status${reset} (coerência .oxe/ + um próximo passo sugerido; não exig
|
|
|
1555
1973
|
--json imprime um único objeto JSON (próximo passo + diagnósticos) em stdout; adequado a CI
|
|
1556
1974
|
--hints lembretes de rotina (idade scan/compact quando configurado em config.json); com --json inclui array \`hints\`
|
|
1557
1975
|
|
|
1976
|
+
${green}capabilities${reset} (catálogo nativo de extensões do projeto)
|
|
1977
|
+
list lista capabilities instaladas em .oxe/capabilities/
|
|
1978
|
+
install <id> cria capability local a partir do template nativo
|
|
1979
|
+
remove <id> remove capability do catálogo local
|
|
1980
|
+
update regera .oxe/CAPABILITIES.md a partir dos manifestos locais
|
|
1981
|
+
--dir <pasta> raiz do projeto (padrão: diretório atual)
|
|
1982
|
+
|
|
1558
1983
|
${green}Atualizar (projeto já tem OXE):${reset}
|
|
1559
1984
|
/oxe-update no Cursor (outras IDEs: mesmo fluxo pelo terminal)
|
|
1560
1985
|
npx oxe-cc update --check só ver se há versão nova no npm
|
|
@@ -1856,7 +2281,7 @@ function runInstall(opts) {
|
|
|
1856
2281
|
console.log(` ${c ? green : ''}✓${c ? reset : ''} Instalação concluída com sucesso.\n`);
|
|
1857
2282
|
}
|
|
1858
2283
|
|
|
1859
|
-
/** @typedef {{ help: boolean, dryRun: boolean, cursor: boolean, copilot: boolean, copilotCli: boolean, allAgents: boolean, ideLocal: boolean, ideExplicit: boolean, noProject: boolean, dir: string, explicitConfigDir: string | null, parseError: boolean, unknownFlag: string, conflictFlags: string }} UninstallOpts */
|
|
2284
|
+
/** @typedef {{ help: boolean, dryRun: boolean, cursor: boolean, copilot: boolean, copilotCli: boolean, allAgents: boolean, globalCli: boolean, ideLocal: boolean, ideExplicit: boolean, noProject: boolean, dir: string, explicitConfigDir: string | null, parseError: boolean, unknownFlag: string, conflictFlags: string }} UninstallOpts */
|
|
1860
2285
|
|
|
1861
2286
|
/**
|
|
1862
2287
|
* @param {UninstallOpts} u
|
|
@@ -1965,6 +2390,7 @@ function parseUninstallArgs(argv) {
|
|
|
1965
2390
|
copilot: false,
|
|
1966
2391
|
copilotCli: false,
|
|
1967
2392
|
allAgents: false,
|
|
2393
|
+
globalCli: false,
|
|
1968
2394
|
ideLocal: false,
|
|
1969
2395
|
ideExplicit: false,
|
|
1970
2396
|
noProject: false,
|
|
@@ -1994,6 +2420,8 @@ function parseUninstallArgs(argv) {
|
|
|
1994
2420
|
out.allAgents = true;
|
|
1995
2421
|
out.copilotCli = true;
|
|
1996
2422
|
out.ideExplicit = true;
|
|
2423
|
+
} else if (a === '--global-cli' || a === '-g') {
|
|
2424
|
+
out.globalCli = true;
|
|
1997
2425
|
} else if (a === '--ide-local') out.ideLocal = true;
|
|
1998
2426
|
else if (a === '--ide-only') out.noProject = true;
|
|
1999
2427
|
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
@@ -2252,7 +2680,18 @@ function runUninstall(u) {
|
|
|
2252
2680
|
oxeManifest.writeFileManifest(home, next, readPkgVersion());
|
|
2253
2681
|
}
|
|
2254
2682
|
|
|
2683
|
+
if (u.globalCli && !u.dryRun) {
|
|
2684
|
+
uninstallGlobalCliPackage();
|
|
2685
|
+
} else if (u.globalCli && u.dryRun) {
|
|
2686
|
+
console.log(`${dim}npm${reset} npm uninstall -g ${readPkgName()}`);
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2255
2689
|
printSummaryAndNextSteps(c, buildUninstallFooter(u));
|
|
2690
|
+
if (!u.globalCli) {
|
|
2691
|
+
console.log(
|
|
2692
|
+
` ${c ? yellow : ''}Nota:${c ? reset : ''} o pacote npm global ${c ? cyan : ''}${readPkgName()}${c ? reset : ''} não é removido por padrão. Use ${c ? cyan : ''}oxe-cc uninstall --global-cli${c ? reset : ''} ou ${c ? cyan : ''}npm uninstall -g ${readPkgName()}${c ? reset : ''}.\n`
|
|
2693
|
+
);
|
|
2694
|
+
}
|
|
2256
2695
|
console.log(` ${c ? green : ''}✓${c ? reset : ''} Desinstalação concluída com sucesso.\n`);
|
|
2257
2696
|
}
|
|
2258
2697
|
|
|
@@ -2277,6 +2716,7 @@ function parseUpdateArgs(argv) {
|
|
|
2277
2716
|
};
|
|
2278
2717
|
let dirExplicit = false;
|
|
2279
2718
|
let firstPositionalConsumed = false;
|
|
2719
|
+
const passthroughFlags = new Set(updateForwardedInstallFlags());
|
|
2280
2720
|
for (let i = 0; i < argv.length; i++) {
|
|
2281
2721
|
const a = argv[i];
|
|
2282
2722
|
if (a === '-h' || a === '--help') out.help = true;
|
|
@@ -2291,6 +2731,11 @@ function parseUpdateArgs(argv) {
|
|
|
2291
2731
|
out.dir = path.resolve(a);
|
|
2292
2732
|
firstPositionalConsumed = true;
|
|
2293
2733
|
} else out.rest.push(a);
|
|
2734
|
+
} else if (passthroughFlags.has(a)) {
|
|
2735
|
+
out.rest.push(a);
|
|
2736
|
+
if ((a === '--config-dir' || a === '-c') && argv[i + 1]) {
|
|
2737
|
+
out.rest.push(argv[++i]);
|
|
2738
|
+
}
|
|
2294
2739
|
} else {
|
|
2295
2740
|
out.parseError = true;
|
|
2296
2741
|
out.unknownFlag = a;
|
|
@@ -2359,7 +2804,11 @@ function runUpdate(u) {
|
|
|
2359
2804
|
);
|
|
2360
2805
|
}
|
|
2361
2806
|
console.log(` ${dim}Comando que seria executado (instalação):${reset}`);
|
|
2362
|
-
|
|
2807
|
+
const dryRunArgs = ['-y', 'oxe-cc@latest', '--force'];
|
|
2808
|
+
if (updateArgsExplicitlyControlGlobalCli(u.rest)) dryRunArgs.push(...u.rest);
|
|
2809
|
+
else if (isRunningFromGlobalNpmInstall()) dryRunArgs.push('--global-cli');
|
|
2810
|
+
else dryRunArgs.push('--no-global-cli', '-l');
|
|
2811
|
+
console.log(` ${cyan}npx ${dryRunArgs.join(' ')}${reset}`);
|
|
2363
2812
|
console.log(` ${dim}Diretório:${reset} ${u.dir}`);
|
|
2364
2813
|
printSummaryAndNextSteps(c, {
|
|
2365
2814
|
bullets: [
|
|
@@ -2408,7 +2857,14 @@ function runUpdate(u) {
|
|
|
2408
2857
|
}
|
|
2409
2858
|
|
|
2410
2859
|
printSection('OXE ▸ update');
|
|
2411
|
-
const args = ['-y', 'oxe-cc@latest', '--force'
|
|
2860
|
+
const args = ['-y', 'oxe-cc@latest', '--force'];
|
|
2861
|
+
if (updateArgsExplicitlyControlGlobalCli(u.rest)) {
|
|
2862
|
+
args.push(...u.rest);
|
|
2863
|
+
} else if (isRunningFromGlobalNpmInstall()) {
|
|
2864
|
+
args.push('--global-cli', ...u.rest);
|
|
2865
|
+
} else {
|
|
2866
|
+
args.push('--no-global-cli', '-l', ...u.rest);
|
|
2867
|
+
}
|
|
2412
2868
|
const r = spawnSync('npx', args, {
|
|
2413
2869
|
cwd: u.dir,
|
|
2414
2870
|
stdio: 'inherit',
|
|
@@ -2434,6 +2890,823 @@ function runUpdate(u) {
|
|
|
2434
2890
|
console.log(` ${c ? green : ''}✓${c ? reset : ''} Atualização concluída com sucesso.\n`);
|
|
2435
2891
|
}
|
|
2436
2892
|
|
|
2893
|
+
/**
|
|
2894
|
+
* @typedef {{ help: boolean, dir: string, action: string, id: string, parseError: boolean, unknownFlag: string }} CapabilityOpts
|
|
2895
|
+
*/
|
|
2896
|
+
|
|
2897
|
+
/**
|
|
2898
|
+
* @param {string[]} argv
|
|
2899
|
+
* @returns {CapabilityOpts}
|
|
2900
|
+
*/
|
|
2901
|
+
function parseCapabilitiesArgs(argv) {
|
|
2902
|
+
/** @type {CapabilityOpts} */
|
|
2903
|
+
const out = {
|
|
2904
|
+
help: false,
|
|
2905
|
+
dir: process.cwd(),
|
|
2906
|
+
action: 'list',
|
|
2907
|
+
id: '',
|
|
2908
|
+
parseError: false,
|
|
2909
|
+
unknownFlag: '',
|
|
2910
|
+
};
|
|
2911
|
+
const positionals = [];
|
|
2912
|
+
for (let i = 0; i < argv.length; i++) {
|
|
2913
|
+
const a = argv[i];
|
|
2914
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
2915
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
2916
|
+
else if (!a.startsWith('-')) positionals.push(a);
|
|
2917
|
+
else {
|
|
2918
|
+
out.parseError = true;
|
|
2919
|
+
out.unknownFlag = a;
|
|
2920
|
+
break;
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
if (positionals.length) {
|
|
2924
|
+
out.action = positionals[0];
|
|
2925
|
+
out.id = positionals[1] || '';
|
|
2926
|
+
}
|
|
2927
|
+
return out;
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
/**
|
|
2931
|
+
* @typedef {{ help: boolean, dir: string, port: number, noOpen: boolean, readOnly: boolean, dumpContext: boolean, activeSession: string|null, parseError: boolean, unknownFlag: string }} DashboardOpts
|
|
2932
|
+
*/
|
|
2933
|
+
|
|
2934
|
+
/**
|
|
2935
|
+
* @typedef {{ help: boolean, dir: string, action: string, activeSession: string|null, wave: number|null, task: string, mode: string, reason: string, parseError: boolean, unknownFlag: string }} RuntimeOpts
|
|
2936
|
+
*/
|
|
2937
|
+
|
|
2938
|
+
/**
|
|
2939
|
+
* @typedef {{
|
|
2940
|
+
* help: boolean,
|
|
2941
|
+
* dir: string,
|
|
2942
|
+
* scope: string,
|
|
2943
|
+
* action: string,
|
|
2944
|
+
* query: string,
|
|
2945
|
+
* activeSession: string|null,
|
|
2946
|
+
* subscription: string,
|
|
2947
|
+
* kind: string,
|
|
2948
|
+
* resourceGroup: string,
|
|
2949
|
+
* name: string,
|
|
2950
|
+
* namespace: string,
|
|
2951
|
+
* topicName: string,
|
|
2952
|
+
* subscriptionName: string,
|
|
2953
|
+
* sourceResourceId: string,
|
|
2954
|
+
* endpoint: string,
|
|
2955
|
+
* location: string,
|
|
2956
|
+
* server: string,
|
|
2957
|
+
* database: string,
|
|
2958
|
+
* adminUser: string,
|
|
2959
|
+
* adminPasswordEnv: string,
|
|
2960
|
+
* startIpAddress: string,
|
|
2961
|
+
* endIpAddress: string,
|
|
2962
|
+
* approve: boolean,
|
|
2963
|
+
* overridePolicy: boolean,
|
|
2964
|
+
* dryRun: boolean,
|
|
2965
|
+
* diff: boolean,
|
|
2966
|
+
* filterType: string,
|
|
2967
|
+
* filterRg: string,
|
|
2968
|
+
* vpnConfirmed: boolean,
|
|
2969
|
+
* tenant: string,
|
|
2970
|
+
* parseError: boolean,
|
|
2971
|
+
* unknownFlag: string
|
|
2972
|
+
* }} AzureOpts
|
|
2973
|
+
*/
|
|
2974
|
+
|
|
2975
|
+
/**
|
|
2976
|
+
* @param {string[]} argv
|
|
2977
|
+
* @returns {DashboardOpts}
|
|
2978
|
+
*/
|
|
2979
|
+
function parseDashboardArgs(argv) {
|
|
2980
|
+
/** @type {DashboardOpts} */
|
|
2981
|
+
const out = {
|
|
2982
|
+
help: false,
|
|
2983
|
+
dir: process.cwd(),
|
|
2984
|
+
port: 4173,
|
|
2985
|
+
noOpen: false,
|
|
2986
|
+
readOnly: false,
|
|
2987
|
+
dumpContext: false,
|
|
2988
|
+
activeSession: null,
|
|
2989
|
+
parseError: false,
|
|
2990
|
+
unknownFlag: '',
|
|
2991
|
+
};
|
|
2992
|
+
for (let i = 0; i < argv.length; i++) {
|
|
2993
|
+
const a = argv[i];
|
|
2994
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
2995
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
2996
|
+
else if (a === '--port' && argv[i + 1]) out.port = Number(argv[++i]) || 4173;
|
|
2997
|
+
else if (a === '--no-open') out.noOpen = true;
|
|
2998
|
+
else if (a === '--read-only') out.readOnly = true;
|
|
2999
|
+
else if (a === '--dump-context') out.dumpContext = true;
|
|
3000
|
+
else if (a === '--session' && argv[i + 1]) out.activeSession = String(argv[++i]).replace(/\\/g, '/');
|
|
3001
|
+
else if (!a.startsWith('-') && i === 0) out.dir = path.resolve(a);
|
|
3002
|
+
else {
|
|
3003
|
+
out.parseError = true;
|
|
3004
|
+
out.unknownFlag = a;
|
|
3005
|
+
break;
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
return out;
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
/**
|
|
3012
|
+
* @param {string[]} argv
|
|
3013
|
+
* @returns {RuntimeOpts}
|
|
3014
|
+
*/
|
|
3015
|
+
function parseRuntimeArgs(argv) {
|
|
3016
|
+
/** @type {RuntimeOpts} */
|
|
3017
|
+
const out = {
|
|
3018
|
+
help: false,
|
|
3019
|
+
dir: process.cwd(),
|
|
3020
|
+
action: 'status',
|
|
3021
|
+
activeSession: null,
|
|
3022
|
+
wave: null,
|
|
3023
|
+
task: '',
|
|
3024
|
+
mode: '',
|
|
3025
|
+
reason: '',
|
|
3026
|
+
parseError: false,
|
|
3027
|
+
unknownFlag: '',
|
|
3028
|
+
};
|
|
3029
|
+
const positionals = [];
|
|
3030
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
3031
|
+
const a = argv[i];
|
|
3032
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
3033
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
3034
|
+
else if (a === '--session' && argv[i + 1]) out.activeSession = String(argv[++i]).replace(/\\/g, '/');
|
|
3035
|
+
else if (a === '--wave' && argv[i + 1]) out.wave = Number(argv[++i]);
|
|
3036
|
+
else if (a === '--task' && argv[i + 1]) out.task = String(argv[++i]);
|
|
3037
|
+
else if (a === '--mode' && argv[i + 1]) out.mode = String(argv[++i]);
|
|
3038
|
+
else if (a === '--reason' && argv[i + 1]) out.reason = String(argv[++i]);
|
|
3039
|
+
else if (!a.startsWith('-')) positionals.push(a);
|
|
3040
|
+
else {
|
|
3041
|
+
out.parseError = true;
|
|
3042
|
+
out.unknownFlag = a;
|
|
3043
|
+
break;
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
if (positionals[0]) out.action = positionals[0];
|
|
3047
|
+
if (positionals[1]) out.dir = path.resolve(positionals[1]);
|
|
3048
|
+
if (Number.isNaN(out.wave)) out.wave = null;
|
|
3049
|
+
return out;
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3052
|
+
/**
|
|
3053
|
+
* @param {string[]} argv
|
|
3054
|
+
* @returns {AzureOpts}
|
|
3055
|
+
*/
|
|
3056
|
+
function parseAzureArgs(argv) {
|
|
3057
|
+
/** @type {AzureOpts} */
|
|
3058
|
+
const out = {
|
|
3059
|
+
help: false,
|
|
3060
|
+
dir: process.cwd(),
|
|
3061
|
+
scope: 'doctor',
|
|
3062
|
+
action: '',
|
|
3063
|
+
query: '',
|
|
3064
|
+
activeSession: null,
|
|
3065
|
+
subscription: '',
|
|
3066
|
+
kind: '',
|
|
3067
|
+
resourceGroup: '',
|
|
3068
|
+
name: '',
|
|
3069
|
+
namespace: '',
|
|
3070
|
+
topicName: '',
|
|
3071
|
+
subscriptionName: '',
|
|
3072
|
+
sourceResourceId: '',
|
|
3073
|
+
endpoint: '',
|
|
3074
|
+
location: '',
|
|
3075
|
+
server: '',
|
|
3076
|
+
database: '',
|
|
3077
|
+
adminUser: '',
|
|
3078
|
+
adminPasswordEnv: '',
|
|
3079
|
+
startIpAddress: '',
|
|
3080
|
+
endIpAddress: '',
|
|
3081
|
+
approve: false,
|
|
3082
|
+
overridePolicy: false,
|
|
3083
|
+
dryRun: false,
|
|
3084
|
+
diff: false,
|
|
3085
|
+
filterType: '',
|
|
3086
|
+
filterRg: '',
|
|
3087
|
+
vpnConfirmed: false,
|
|
3088
|
+
tenant: '',
|
|
3089
|
+
parseError: false,
|
|
3090
|
+
unknownFlag: '',
|
|
3091
|
+
};
|
|
3092
|
+
const positionals = [];
|
|
3093
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
3094
|
+
const a = argv[i];
|
|
3095
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
3096
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
3097
|
+
else if (a === '--session' && argv[i + 1]) out.activeSession = String(argv[++i]).replace(/\\/g, '/');
|
|
3098
|
+
else if (a === '--subscription' && argv[i + 1]) out.subscription = String(argv[++i]);
|
|
3099
|
+
else if (a === '--kind' && argv[i + 1]) out.kind = String(argv[++i]);
|
|
3100
|
+
else if (a === '--resource-group' && argv[i + 1]) out.resourceGroup = String(argv[++i]);
|
|
3101
|
+
else if (a === '--name' && argv[i + 1]) out.name = String(argv[++i]);
|
|
3102
|
+
else if (a === '--namespace' && argv[i + 1]) out.namespace = String(argv[++i]);
|
|
3103
|
+
else if (a === '--topic-name' && argv[i + 1]) out.topicName = String(argv[++i]);
|
|
3104
|
+
else if (a === '--subscription-name' && argv[i + 1]) out.subscriptionName = String(argv[++i]);
|
|
3105
|
+
else if (a === '--source-resource-id' && argv[i + 1]) out.sourceResourceId = String(argv[++i]);
|
|
3106
|
+
else if (a === '--endpoint' && argv[i + 1]) out.endpoint = String(argv[++i]);
|
|
3107
|
+
else if (a === '--location' && argv[i + 1]) out.location = String(argv[++i]);
|
|
3108
|
+
else if (a === '--server' && argv[i + 1]) out.server = String(argv[++i]);
|
|
3109
|
+
else if (a === '--database' && argv[i + 1]) out.database = String(argv[++i]);
|
|
3110
|
+
else if (a === '--admin-user' && argv[i + 1]) out.adminUser = String(argv[++i]);
|
|
3111
|
+
else if (a === '--admin-password-env' && argv[i + 1]) out.adminPasswordEnv = String(argv[++i]);
|
|
3112
|
+
else if (a === '--start-ip-address' && argv[i + 1]) out.startIpAddress = String(argv[++i]);
|
|
3113
|
+
else if (a === '--end-ip-address' && argv[i + 1]) out.endIpAddress = String(argv[++i]);
|
|
3114
|
+
else if (a === '--approve') out.approve = true;
|
|
3115
|
+
else if (a === '--override-policy') out.overridePolicy = true;
|
|
3116
|
+
else if (a === '--dry-run') out.dryRun = true;
|
|
3117
|
+
else if (a === '--diff') out.diff = true;
|
|
3118
|
+
else if (a === '--type' && argv[i + 1]) out.filterType = String(argv[++i]);
|
|
3119
|
+
else if (a === '--filter-rg' && argv[i + 1]) out.filterRg = String(argv[++i]);
|
|
3120
|
+
else if (a === '--vpn-confirmed') out.vpnConfirmed = true;
|
|
3121
|
+
else if (a === '--tenant' && argv[i + 1]) out.tenant = String(argv[++i]);
|
|
3122
|
+
else if (!a.startsWith('-')) positionals.push(a);
|
|
3123
|
+
else {
|
|
3124
|
+
out.parseError = true;
|
|
3125
|
+
out.unknownFlag = a;
|
|
3126
|
+
break;
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
if (positionals[0]) out.scope = String(positionals[0]);
|
|
3130
|
+
if (positionals[1]) out.action = String(positionals[1]);
|
|
3131
|
+
if (out.scope === 'find') {
|
|
3132
|
+
out.query = positionals.slice(1).join(' ').trim();
|
|
3133
|
+
} else if (!out.action && positionals[2]) {
|
|
3134
|
+
out.query = positionals.slice(2).join(' ').trim();
|
|
3135
|
+
} else if (positionals[2]) {
|
|
3136
|
+
out.query = positionals.slice(2).join(' ').trim();
|
|
3137
|
+
}
|
|
3138
|
+
return out;
|
|
3139
|
+
}
|
|
3140
|
+
|
|
3141
|
+
/**
|
|
3142
|
+
* @param {DashboardOpts} opts
|
|
3143
|
+
*/
|
|
3144
|
+
async function runDashboard(opts) {
|
|
3145
|
+
assertNotWslWindowsNode();
|
|
3146
|
+
if (!fs.existsSync(opts.dir)) {
|
|
3147
|
+
console.error(`${yellow}Diretório não encontrado: ${opts.dir}${reset}`);
|
|
3148
|
+
process.exit(1);
|
|
3149
|
+
}
|
|
3150
|
+
const target = opts.dir;
|
|
3151
|
+
if (opts.dumpContext) {
|
|
3152
|
+
console.log(JSON.stringify(oxeDashboard.loadDashboardContext(target, { activeSession: opts.activeSession }), null, 2));
|
|
3153
|
+
return;
|
|
3154
|
+
}
|
|
3155
|
+
const c = useAnsiColors();
|
|
3156
|
+
printSection('OXE ▸ dashboard');
|
|
3157
|
+
const server = oxeDashboard.createDashboardServer(target, { activeSession: opts.activeSession });
|
|
3158
|
+
await new Promise((resolve, reject) => {
|
|
3159
|
+
server.once('error', reject);
|
|
3160
|
+
server.listen(opts.port, '127.0.0.1', () => resolve());
|
|
3161
|
+
});
|
|
3162
|
+
const address = server.address();
|
|
3163
|
+
const port = address && typeof address === 'object' ? address.port : opts.port;
|
|
3164
|
+
const url = `http://127.0.0.1:${port}/`;
|
|
3165
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${target}${c ? reset : ''}`);
|
|
3166
|
+
console.log(` ${c ? green : ''}URL:${c ? reset : ''} ${c ? cyan : ''}${url}${c ? reset : ''}`);
|
|
3167
|
+
if (opts.readOnly) {
|
|
3168
|
+
console.log(` ${c ? yellow : ''}Modo:${c ? reset : ''} read-only (UI visual; persistência deve ser evitada no uso desta flag)`);
|
|
3169
|
+
}
|
|
3170
|
+
if (!opts.noOpen) {
|
|
3171
|
+
try {
|
|
3172
|
+
openUrlInBrowser(url);
|
|
3173
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Browser aberto.`);
|
|
3174
|
+
} catch {
|
|
3175
|
+
console.log(` ${yellow}Não foi possível abrir o browser automaticamente.${reset}`);
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
console.log(` ${dim}Pressione Ctrl+C para encerrar o servidor local.${reset}\n`);
|
|
3179
|
+
await new Promise(() => {});
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3182
|
+
/**
|
|
3183
|
+
* @param {RuntimeOpts} opts
|
|
3184
|
+
*/
|
|
3185
|
+
function runRuntime(opts) {
|
|
3186
|
+
const c = useAnsiColors();
|
|
3187
|
+
printSection('OXE ▸ runtime');
|
|
3188
|
+
if (!fs.existsSync(opts.dir)) {
|
|
3189
|
+
console.error(`${yellow}Diretório não encontrado: ${opts.dir}${reset}`);
|
|
3190
|
+
process.exit(1);
|
|
3191
|
+
}
|
|
3192
|
+
bootstrapOxe(opts.dir, { dryRun: false, force: false });
|
|
3193
|
+
const statePath = oxeHealth.oxePaths(opts.dir).state;
|
|
3194
|
+
const stateText = fs.existsSync(statePath) ? fs.readFileSync(statePath, 'utf8') : '';
|
|
3195
|
+
const activeSession = opts.activeSession || oxeHealth.parseActiveSession(stateText) || null;
|
|
3196
|
+
const p = oxeOperational.operationalPaths(opts.dir, activeSession);
|
|
3197
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
3198
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || 'modo legado'}${c ? reset : ''}`);
|
|
3199
|
+
|
|
3200
|
+
if (opts.action === 'status') {
|
|
3201
|
+
const current = oxeOperational.readRunState(opts.dir, activeSession);
|
|
3202
|
+
if (!current) {
|
|
3203
|
+
console.log(` ${yellow}Nenhum ACTIVE-RUN encontrado.${reset}`);
|
|
3204
|
+
return;
|
|
3205
|
+
}
|
|
3206
|
+
console.log(` ${c ? green : ''}Run:${c ? reset : ''} ${current.run_id}`);
|
|
3207
|
+
console.log(` ${c ? green : ''}Estado:${c ? reset : ''} ${current.status}`);
|
|
3208
|
+
console.log(` ${c ? green : ''}Cursor:${c ? reset : ''} onda=${current.cursor && current.cursor.wave != null ? current.cursor.wave : '—'} tarefa=${current.cursor && current.cursor.task ? current.cursor.task : '—'} modo=${current.cursor && current.cursor.mode ? current.cursor.mode : '—'}`);
|
|
3209
|
+
console.log(` ${c ? green : ''}Arquivo:${c ? reset : ''} ${path.join(p.runsDir, `${current.run_id}.json`)}`);
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3213
|
+
try {
|
|
3214
|
+
const next = oxeOperational.applyRuntimeAction(opts.dir, activeSession, {
|
|
3215
|
+
action: opts.action,
|
|
3216
|
+
wave: opts.wave,
|
|
3217
|
+
task: opts.task || null,
|
|
3218
|
+
mode: opts.mode || null,
|
|
3219
|
+
reason: opts.reason || '',
|
|
3220
|
+
});
|
|
3221
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Runtime atualizado.`);
|
|
3222
|
+
console.log(` ${c ? green : ''}Run:${c ? reset : ''} ${next.run_id}`);
|
|
3223
|
+
console.log(` ${c ? green : ''}Estado:${c ? reset : ''} ${next.status}`);
|
|
3224
|
+
console.log(` ${c ? green : ''}Cursor:${c ? reset : ''} onda=${next.cursor && next.cursor.wave != null ? next.cursor.wave : '—'} tarefa=${next.cursor && next.cursor.task ? next.cursor.task : '—'} modo=${next.cursor && next.cursor.mode ? next.cursor.mode : '—'}`);
|
|
3225
|
+
console.log(` ${c ? green : ''}Trace:${c ? reset : ''} ${p.events}`);
|
|
3226
|
+
} catch (err) {
|
|
3227
|
+
console.error(`${red}${err && err.message ? err.message : 'Falha ao atualizar runtime.'}${reset}`);
|
|
3228
|
+
process.exit(1);
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
/**
|
|
3233
|
+
* @param {AzureOpts} opts
|
|
3234
|
+
*/
|
|
3235
|
+
function runAzure(opts) {
|
|
3236
|
+
const c = useAnsiColors();
|
|
3237
|
+
printSection('OXE ▸ azure');
|
|
3238
|
+
if (!fs.existsSync(opts.dir)) {
|
|
3239
|
+
console.error(`${yellow}Diretório não encontrado: ${opts.dir}${reset}`);
|
|
3240
|
+
process.exit(1);
|
|
3241
|
+
}
|
|
3242
|
+
bootstrapOxe(opts.dir, { dryRun: false, force: false });
|
|
3243
|
+
oxeAzure.ensureAzureArtifacts(opts.dir);
|
|
3244
|
+
oxeAzure.ensureAzureCapabilities(opts.dir);
|
|
3245
|
+
writeCapabilitiesIndex(opts.dir);
|
|
3246
|
+
const { config } = oxeHealth.loadOxeConfigMerged(opts.dir);
|
|
3247
|
+
const statePath = oxeHealth.oxePaths(opts.dir).state;
|
|
3248
|
+
const stateText = fs.existsSync(statePath) ? fs.readFileSync(statePath, 'utf8') : '';
|
|
3249
|
+
const activeSession = opts.activeSession || oxeHealth.parseActiveSession(stateText) || null;
|
|
3250
|
+
const azureCfg = config.azure && typeof config.azure === 'object' ? config.azure : {};
|
|
3251
|
+
const preferredLocation = Array.isArray(azureCfg.preferred_locations) && azureCfg.preferred_locations.length
|
|
3252
|
+
? String(azureCfg.preferred_locations[0])
|
|
3253
|
+
: '';
|
|
3254
|
+
const input = {
|
|
3255
|
+
kind: opts.kind,
|
|
3256
|
+
resourceGroup: opts.resourceGroup || String(azureCfg.default_resource_group || ''),
|
|
3257
|
+
name: opts.name || opts.server,
|
|
3258
|
+
namespace: opts.namespace,
|
|
3259
|
+
topicName: opts.topicName,
|
|
3260
|
+
subscriptionName: opts.subscriptionName,
|
|
3261
|
+
sourceResourceId: opts.sourceResourceId,
|
|
3262
|
+
endpoint: opts.endpoint,
|
|
3263
|
+
location: opts.location || preferredLocation,
|
|
3264
|
+
server: opts.server,
|
|
3265
|
+
database: opts.database,
|
|
3266
|
+
adminUser: opts.adminUser,
|
|
3267
|
+
adminPasswordEnv: opts.adminPasswordEnv || 'AZURE_SQL_ADMIN_PASSWORD',
|
|
3268
|
+
startIpAddress: opts.startIpAddress,
|
|
3269
|
+
endIpAddress: opts.endIpAddress,
|
|
3270
|
+
env: process.env,
|
|
3271
|
+
};
|
|
3272
|
+
|
|
3273
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
3274
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || 'modo legado'}${c ? reset : ''}`);
|
|
3275
|
+
|
|
3276
|
+
try {
|
|
3277
|
+
if (opts.scope === 'status') {
|
|
3278
|
+
const st = oxeAzure.statusAzure(opts.dir, config);
|
|
3279
|
+
const loginColor = st.loginActive ? green : red;
|
|
3280
|
+
const invColor = st.inventoryPresent && !st.inventoryStale ? green : yellow;
|
|
3281
|
+
console.log(` ${c ? (st.cliInstalled ? green : red) : ''}CLI:${c ? reset : ''} ${st.cliInstalled ? `Azure CLI ${st.cliVersion || 'detectada'}` : 'não instalada'}`);
|
|
3282
|
+
console.log(` ${c ? loginColor : ''}Login:${c ? reset : ''} ${st.loginActive ? 'ativo' : 'ausente'}`);
|
|
3283
|
+
console.log(` ${c ? green : ''}Sub:${c ? reset : ''} ${st.subscription || '—'}`);
|
|
3284
|
+
console.log(` ${c ? invColor : ''}Inventário:${c ? reset : ''} ${st.inventoryPresent ? `${st.inventoryAgeHours !== null ? `${st.inventoryAgeHours}h atrás` : 'presente'}${st.inventoryStale ? ' (stale)' : ''}` : 'ausente'}`);
|
|
3285
|
+
if (st.inventorySummary) {
|
|
3286
|
+
console.log(` ${c ? green : ''}Recursos:${c ? reset : ''} total=${st.inventorySummary.total} sb=${st.inventorySummary.servicebus} eg=${st.inventorySummary.eventgrid} sql=${st.inventorySummary.sql}`);
|
|
3287
|
+
}
|
|
3288
|
+
if (st.pendingOperations > 0) {
|
|
3289
|
+
console.log(` ${yellow}Ops pendentes:${reset} ${st.pendingOperations} (${st.pendingOperationIds.join(', ')})`);
|
|
3290
|
+
}
|
|
3291
|
+
if (st.vpnRequired) {
|
|
3292
|
+
console.log(` ${yellow}VPN:${reset} requerida pela configuração do projeto`);
|
|
3293
|
+
}
|
|
3294
|
+
return;
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
if (opts.scope === 'operations') {
|
|
3298
|
+
const ops = oxeAzure.listAzureOperations(opts.dir);
|
|
3299
|
+
if (!ops.length) {
|
|
3300
|
+
console.log(` ${c ? dim : ''}Nenhuma operação registrada.${c ? reset : ''}`);
|
|
3301
|
+
return;
|
|
3302
|
+
}
|
|
3303
|
+
for (const op of ops) {
|
|
3304
|
+
const phaseColor = op.phase === 'applied' ? green : op.phase === 'waiting_approval' ? yellow : op.phase === 'failed' ? red : dim;
|
|
3305
|
+
console.log(` ${c ? phaseColor : ''}${op.phase}${c ? reset : ''} · ${op.operation_id} · ${op.domain}/${op.kind} · ${op.summary || '—'}`);
|
|
3306
|
+
}
|
|
3307
|
+
return;
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
if (opts.scope === 'doctor') {
|
|
3311
|
+
const report = oxeAzure.azureDoctor(opts.dir, config, {
|
|
3312
|
+
autoInstall: Boolean(azureCfg.resource_graph_auto_install !== false),
|
|
3313
|
+
});
|
|
3314
|
+
console.log(` ${c ? green : ''}CLI:${c ? reset : ''} ${report.authStatus.installed ? `Azure CLI ${report.authStatus.version || 'detectada'}` : 'não instalada'}`);
|
|
3315
|
+
console.log(` ${c ? green : ''}Login:${c ? reset : ''} ${report.authStatus.login_active ? 'ativo' : 'ausente'}`);
|
|
3316
|
+
console.log(` ${c ? green : ''}Subscription:${c ? reset : ''} ${report.profile.subscription_name || report.profile.subscription_id || '—'}`);
|
|
3317
|
+
console.log(` ${c ? green : ''}Inventário:${c ? reset : ''} ${report.inventory && report.inventory.synced_at ? `sync em ${report.inventory.synced_at}` : 'ausente'}`);
|
|
3318
|
+
if (report.warnings.length) {
|
|
3319
|
+
for (const warning of report.warnings) {
|
|
3320
|
+
console.log(` ${yellow}AVISO${reset} ${warning}`);
|
|
3321
|
+
}
|
|
3322
|
+
} else {
|
|
3323
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Contexto Azure saudável.`);
|
|
3324
|
+
}
|
|
3325
|
+
if (!report.authStatus.installed || !report.authStatus.login_active || !report.profile.subscription_id) {
|
|
3326
|
+
process.exit(1);
|
|
3327
|
+
}
|
|
3328
|
+
return;
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
if (opts.scope === 'auth') {
|
|
3332
|
+
if (opts.action === 'login') {
|
|
3333
|
+
const context = oxeAzure.loginAzure(opts.dir, { inherit: true, tenant: opts.tenant || undefined });
|
|
3334
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Login Azure concluído.`);
|
|
3335
|
+
console.log(` ${c ? green : ''}Conta:${c ? reset : ''} ${context.authStatus.user || '—'}`);
|
|
3336
|
+
console.log(` ${c ? green : ''}Subscription:${c ? reset : ''} ${context.profile.subscription_name || context.profile.subscription_id || '—'}`);
|
|
3337
|
+
return;
|
|
3338
|
+
}
|
|
3339
|
+
if (opts.action === 'whoami' || !opts.action) {
|
|
3340
|
+
const context = oxeAzure.getAzureContext(opts.dir);
|
|
3341
|
+
console.log(` ${c ? green : ''}Cloud:${c ? reset : ''} ${context.profile.cloud || '—'}`);
|
|
3342
|
+
console.log(` ${c ? green : ''}Conta:${c ? reset : ''} ${context.authStatus.user || '—'}`);
|
|
3343
|
+
console.log(` ${c ? green : ''}Tipo:${c ? reset : ''} ${context.authStatus.user_type || context.profile.auth_mode || '—'}`);
|
|
3344
|
+
console.log(` ${c ? green : ''}Tenant:${c ? reset : ''} ${context.profile.tenant_id || '—'}`);
|
|
3345
|
+
console.log(` ${c ? green : ''}Subscription:${c ? reset : ''} ${context.profile.subscription_name || context.profile.subscription_id || '—'}`);
|
|
3346
|
+
console.log(` ${c ? green : ''}Resource Graph:${c ? reset : ''} ${context.authStatus.resource_graph_enabled ? 'habilitado' : 'ausente'}`);
|
|
3347
|
+
return;
|
|
3348
|
+
}
|
|
3349
|
+
if (opts.action === 'set-subscription') {
|
|
3350
|
+
if (!opts.subscription) {
|
|
3351
|
+
console.error(`${red}Informe --subscription <id|nome>.${reset}`);
|
|
3352
|
+
process.exit(1);
|
|
3353
|
+
}
|
|
3354
|
+
const context = oxeAzure.setAzureSubscription(opts.dir, opts.subscription);
|
|
3355
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Subscription selecionada.`);
|
|
3356
|
+
console.log(` ${c ? green : ''}Subscription:${c ? reset : ''} ${context.profile.subscription_name || context.profile.subscription_id || '—'}`);
|
|
3357
|
+
return;
|
|
3358
|
+
}
|
|
3359
|
+
throw new Error(`Subcomando Azure auth desconhecido: ${opts.action || '—'}`);
|
|
3360
|
+
}
|
|
3361
|
+
|
|
3362
|
+
if (opts.scope === 'sync') {
|
|
3363
|
+
const synced = oxeAzure.syncAzureInventory(opts.dir, {
|
|
3364
|
+
autoInstall: Boolean(azureCfg.resource_graph_auto_install !== false),
|
|
3365
|
+
diff: opts.diff,
|
|
3366
|
+
});
|
|
3367
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Inventário Azure sincronizado.`);
|
|
3368
|
+
console.log(` ${c ? green : ''}Subscription:${c ? reset : ''} ${synced.profile.subscription_name || synced.profile.subscription_id || '—'}`);
|
|
3369
|
+
console.log(` ${c ? green : ''}Resumo:${c ? reset : ''} total=${synced.inventory.summary.total} servicebus=${synced.inventory.summary.servicebus} eventgrid=${synced.inventory.summary.eventgrid} sql=${synced.inventory.summary.sql}`);
|
|
3370
|
+
console.log(` ${c ? green : ''}Artefato:${c ? reset : ''} ${synced.paths.inventory}`);
|
|
3371
|
+
if (synced.diff) {
|
|
3372
|
+
const d = synced.diff;
|
|
3373
|
+
console.log(` ${c ? green : ''}Diff:${c ? reset : ''} +${d.added.length} adicionados · -${d.removed.length} removidos · ${d.unchanged} inalterados`);
|
|
3374
|
+
for (const item of d.added) console.log(` ${c ? green : ''}+${c ? reset : ''} ${item.name} · ${item.type} · ${item.resourceGroup || '—'}`);
|
|
3375
|
+
for (const item of d.removed) console.log(` ${c ? red : ''}-${c ? reset : ''} ${item.name} · ${item.type} · ${item.resourceGroup || '—'}`);
|
|
3376
|
+
}
|
|
3377
|
+
return;
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
if (opts.scope === 'find') {
|
|
3381
|
+
const filters = {};
|
|
3382
|
+
if (opts.filterType) filters.type = opts.filterType;
|
|
3383
|
+
if (opts.filterRg) filters.resourceGroup = opts.filterRg;
|
|
3384
|
+
const matches = oxeAzure.searchAzureInventory(opts.dir, opts.query, filters);
|
|
3385
|
+
if (!matches.length) {
|
|
3386
|
+
console.log(` ${yellow}Nenhum recurso encontrado no inventário local.${reset}`);
|
|
3387
|
+
return;
|
|
3388
|
+
}
|
|
3389
|
+
for (const item of matches) {
|
|
3390
|
+
console.log(` ${c ? dim : ''}•${c ? reset : ''} ${item.name} · ${item.type} · ${item.resourceGroup || '—'} · ${item.location || '—'}`);
|
|
3391
|
+
}
|
|
3392
|
+
return;
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3395
|
+
if (!['servicebus', 'eventgrid', 'sql'].includes(opts.scope)) {
|
|
3396
|
+
throw new Error(`Escopo Azure desconhecido: ${opts.scope}. Use: doctor | status | auth | sync | find | operations | servicebus | eventgrid | sql`);
|
|
3397
|
+
}
|
|
3398
|
+
if (!opts.action) {
|
|
3399
|
+
throw new Error(`Informe a ação para ${opts.scope}: list | show | plan | apply`);
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
if (opts.action === 'list' || opts.action === 'show') {
|
|
3403
|
+
const result = oxeAzure.executeAzureRead(opts.dir, activeSession, opts.scope, opts.action, input);
|
|
3404
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3405
|
+
return;
|
|
3406
|
+
}
|
|
3407
|
+
if (opts.action === 'plan') {
|
|
3408
|
+
const planned = oxeAzure.planAzureOperation(opts.dir, activeSession, opts.scope, input);
|
|
3409
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Operação Azure planejada.`);
|
|
3410
|
+
console.log(` ${c ? green : ''}Operation ID:${c ? reset : ''} ${planned.operation.operation_id}`);
|
|
3411
|
+
console.log(` ${c ? green : ''}Checkpoint:${c ? reset : ''} ${planned.operation.checkpoint_id}`);
|
|
3412
|
+
console.log(` ${c ? green : ''}Comando:${c ? reset : ''} ${planned.operation.command_display_redacted}`);
|
|
3413
|
+
console.log(` ${c ? green : ''}Resumo:${c ? reset : ''} ${planned.operation.summary}`);
|
|
3414
|
+
console.log(` ${c ? green : ''}Artefatos:${c ? reset : ''} ${planned.files.jsonPath} · ${planned.files.mdPath}`);
|
|
3415
|
+
return;
|
|
3416
|
+
}
|
|
3417
|
+
if (opts.action === 'apply') {
|
|
3418
|
+
const applied = oxeAzure.applyAzureOperation(opts.dir, activeSession, opts.scope, input, {
|
|
3419
|
+
approve: opts.approve,
|
|
3420
|
+
overridePolicy: opts.overridePolicy,
|
|
3421
|
+
dryRun: opts.dryRun,
|
|
3422
|
+
vpnRequired: Boolean(azureCfg.vpn_required),
|
|
3423
|
+
vpnConfirmed: opts.vpnConfirmed,
|
|
3424
|
+
});
|
|
3425
|
+
if (applied.dryRun) {
|
|
3426
|
+
console.log(` ${yellow}[dry-run]${reset} Validação OK — nenhuma alteração foi feita.`);
|
|
3427
|
+
console.log(` ${c ? green : ''}Comando:${c ? reset : ''} ${applied.commandPreview}`);
|
|
3428
|
+
console.log(` ${c ? green : ''}Resumo:${c ? reset : ''} ${applied.operation.summary}`);
|
|
3429
|
+
return;
|
|
3430
|
+
}
|
|
3431
|
+
if (!applied.approved) {
|
|
3432
|
+
console.log(` ${yellow}Checkpoint aberto antes da mutação.${reset}`);
|
|
3433
|
+
console.log(` ${c ? green : ''}Operation ID:${c ? reset : ''} ${applied.operation.operation_id}`);
|
|
3434
|
+
console.log(` ${c ? green : ''}Checkpoint:${c ? reset : ''} ${applied.checkpoint_id}`);
|
|
3435
|
+
console.log(` ${c ? green : ''}Resumo:${c ? reset : ''} ${applied.operation.summary}`);
|
|
3436
|
+
console.log(` ${c ? green : ''}Reexecute:${c ? reset : ''} npx oxe-cc azure ${opts.scope} apply --approve --kind ${applied.operation.kind} --resource-group ${applied.operation.resource_group}`);
|
|
3437
|
+
return;
|
|
3438
|
+
}
|
|
3439
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Operação Azure aplicada.`);
|
|
3440
|
+
console.log(` ${c ? green : ''}Operation ID:${c ? reset : ''} ${applied.operation.operation_id}`);
|
|
3441
|
+
console.log(` ${c ? green : ''}Artefatos:${c ? reset : ''} ${applied.files.jsonPath} · ${applied.files.mdPath}`);
|
|
3442
|
+
return;
|
|
3443
|
+
}
|
|
3444
|
+
|
|
3445
|
+
throw new Error(`Ação Azure desconhecida: ${opts.action}`);
|
|
3446
|
+
} catch (err) {
|
|
3447
|
+
console.error(`${red}${err && err.message ? err.message : 'Falha no provider Azure.'}${reset}`);
|
|
3448
|
+
process.exit(1);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3452
|
+
function capabilityManifestPath(target, id) {
|
|
3453
|
+
return path.join(target, '.oxe', 'capabilities', id, 'CAPABILITY.md');
|
|
3454
|
+
}
|
|
3455
|
+
|
|
3456
|
+
function listLocalCapabilities(target) {
|
|
3457
|
+
const dir = path.join(target, '.oxe', 'capabilities');
|
|
3458
|
+
if (!fs.existsSync(dir)) return [];
|
|
3459
|
+
return fs
|
|
3460
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
3461
|
+
.filter((e) => e.isDirectory() && fs.existsSync(path.join(dir, e.name, 'CAPABILITY.md')))
|
|
3462
|
+
.map((e) => e.name)
|
|
3463
|
+
.sort();
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3466
|
+
function renderCapabilitiesIndex(target) {
|
|
3467
|
+
const template = ['# OXE — Capabilities Instaladas', '', '> Catálogo local de capabilities do projeto. Cada capability vive em `.oxe/capabilities/<id>/`.', '', '| ID | Tipo | Status | Escopo | Política | Side effects | Requer env | Evidência | Resumo |', '|----|------|--------|--------|-----------|--------------|------------|-----------|--------|'];
|
|
3468
|
+
const catalog = oxeOperational.readCapabilityCatalog(target);
|
|
3469
|
+
if (!catalog.length) {
|
|
3470
|
+
template.push('| (vazio) | — | — | — | — | — | — | — | Nenhuma capability instalada |');
|
|
3471
|
+
return template.join('\n') + '\n';
|
|
3472
|
+
}
|
|
3473
|
+
for (const cap of catalog) {
|
|
3474
|
+
template.push(`| ${cap.id} | ${cap.type} | ${cap.status} | ${cap.scope} | ${cap.approvalPolicy || '—'} | ${(cap.sideEffects || []).join(', ') || '—'} | ${(cap.requiresEnv || []).join(', ') || '—'} | ${(cap.evidenceOutputs || []).join(', ') || '—'} | ${cap.description} |`);
|
|
3475
|
+
}
|
|
3476
|
+
return template.join('\n') + '\n';
|
|
3477
|
+
}
|
|
3478
|
+
|
|
3479
|
+
function writeCapabilitiesIndex(target) {
|
|
3480
|
+
const dest = path.join(target, '.oxe', 'CAPABILITIES.md');
|
|
3481
|
+
fs.writeFileSync(dest, renderCapabilitiesIndex(target), 'utf8');
|
|
3482
|
+
}
|
|
3483
|
+
|
|
3484
|
+
/**
|
|
3485
|
+
* @param {CapabilityOpts} opts
|
|
3486
|
+
*/
|
|
3487
|
+
function runCapabilities(opts) {
|
|
3488
|
+
const c = useAnsiColors();
|
|
3489
|
+
printSection('OXE ▸ capabilities');
|
|
3490
|
+
if (!fs.existsSync(opts.dir)) {
|
|
3491
|
+
console.error(`${yellow}Diretório não encontrado: ${opts.dir}${reset}`);
|
|
3492
|
+
process.exit(1);
|
|
3493
|
+
}
|
|
3494
|
+
bootstrapOxe(opts.dir, { dryRun: false, force: false });
|
|
3495
|
+
const capsDir = path.join(opts.dir, '.oxe', 'capabilities');
|
|
3496
|
+
ensureDir(capsDir);
|
|
3497
|
+
if (opts.action === 'list') {
|
|
3498
|
+
const ids = listLocalCapabilities(opts.dir);
|
|
3499
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
3500
|
+
if (!ids.length) {
|
|
3501
|
+
console.log(` ${yellow}Nenhuma capability instalada.${reset}`);
|
|
3502
|
+
} else {
|
|
3503
|
+
for (const id of ids) console.log(` ${c ? dim : ''}•${c ? reset : ''} ${id}`);
|
|
3504
|
+
}
|
|
3505
|
+
return;
|
|
3506
|
+
}
|
|
3507
|
+
if (!opts.id && (opts.action === 'install' || opts.action === 'remove')) {
|
|
3508
|
+
console.error(`${red}Informe o ID da capability.${reset}`);
|
|
3509
|
+
process.exit(1);
|
|
3510
|
+
}
|
|
3511
|
+
if (opts.action === 'install') {
|
|
3512
|
+
const safeId = opts.id.trim().toLowerCase();
|
|
3513
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(safeId)) {
|
|
3514
|
+
console.error(`${red}ID inválido para capability:${reset} ${opts.id}`);
|
|
3515
|
+
process.exit(1);
|
|
3516
|
+
}
|
|
3517
|
+
const dir = path.join(capsDir, safeId);
|
|
3518
|
+
ensureDir(dir);
|
|
3519
|
+
const manifest = capabilityManifestPath(opts.dir, safeId);
|
|
3520
|
+
if (!fs.existsSync(manifest)) {
|
|
3521
|
+
const src = path.join(PKG_ROOT, 'oxe', 'templates', 'CAPABILITY.template.md');
|
|
3522
|
+
let raw = fs.readFileSync(src, 'utf8');
|
|
3523
|
+
raw = raw.replace(/^id:\s*sample-capability$/m, `id: ${safeId}`);
|
|
3524
|
+
fs.writeFileSync(manifest, raw, 'utf8');
|
|
3525
|
+
}
|
|
3526
|
+
writeCapabilitiesIndex(opts.dir);
|
|
3527
|
+
oxeOperational.appendEvent(opts.dir, null, { type: 'capability_installed', payload: { capability_id: safeId } });
|
|
3528
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Capability instalada: ${safeId}`);
|
|
3529
|
+
return;
|
|
3530
|
+
}
|
|
3531
|
+
if (opts.action === 'remove') {
|
|
3532
|
+
fs.rmSync(path.join(capsDir, opts.id), { recursive: true, force: true });
|
|
3533
|
+
writeCapabilitiesIndex(opts.dir);
|
|
3534
|
+
oxeOperational.appendEvent(opts.dir, null, { type: 'capability_removed', payload: { capability_id: opts.id } });
|
|
3535
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Capability removida: ${opts.id}`);
|
|
3536
|
+
return;
|
|
3537
|
+
}
|
|
3538
|
+
if (opts.action === 'update') {
|
|
3539
|
+
writeCapabilitiesIndex(opts.dir);
|
|
3540
|
+
oxeOperational.appendEvent(opts.dir, null, { type: 'capability_index_refreshed' });
|
|
3541
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} Índice de capabilities atualizado.`);
|
|
3542
|
+
return;
|
|
3543
|
+
}
|
|
3544
|
+
console.error(`${red}Ação desconhecida:${reset} ${opts.action}`);
|
|
3545
|
+
process.exit(1);
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
/**
|
|
3549
|
+
* @param {string[]} argv
|
|
3550
|
+
* @returns {{ help: boolean, dir: string, visual: boolean, activeSession: string|null, parseError: boolean, unknownFlag: string }}
|
|
3551
|
+
*/
|
|
3552
|
+
function parsePlanArgs(argv) {
|
|
3553
|
+
const out = { help: false, dir: process.cwd(), visual: false, activeSession: null, parseError: false, unknownFlag: '' };
|
|
3554
|
+
for (let i = 0; i < argv.length; i++) {
|
|
3555
|
+
const a = argv[i];
|
|
3556
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
3557
|
+
else if (a === '--visual') out.visual = true;
|
|
3558
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
3559
|
+
else if (a === '--session' && argv[i + 1]) out.activeSession = String(argv[++i]).replace(/\\/g, '/');
|
|
3560
|
+
else if (!a.startsWith('-') && i === 0) out.dir = path.resolve(a);
|
|
3561
|
+
else { out.parseError = true; out.unknownFlag = a; break; }
|
|
3562
|
+
}
|
|
3563
|
+
return out;
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
/**
|
|
3567
|
+
* @param {string[]} argv
|
|
3568
|
+
* @returns {{ help: boolean, dir: string, matrix: boolean, activeSession: string|null, parseError: boolean, unknownFlag: string }}
|
|
3569
|
+
*/
|
|
3570
|
+
function parseVerifyArgs(argv) {
|
|
3571
|
+
const out = { help: false, dir: process.cwd(), matrix: false, activeSession: null, parseError: false, unknownFlag: '' };
|
|
3572
|
+
for (let i = 0; i < argv.length; i++) {
|
|
3573
|
+
const a = argv[i];
|
|
3574
|
+
if (a === '-h' || a === '--help') out.help = true;
|
|
3575
|
+
else if (a === '--matrix') out.matrix = true;
|
|
3576
|
+
else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
|
|
3577
|
+
else if (a === '--session' && argv[i + 1]) out.activeSession = String(argv[++i]).replace(/\\/g, '/');
|
|
3578
|
+
else if (!a.startsWith('-') && i === 0) out.dir = path.resolve(a);
|
|
3579
|
+
else { out.parseError = true; out.unknownFlag = a; break; }
|
|
3580
|
+
}
|
|
3581
|
+
return out;
|
|
3582
|
+
}
|
|
3583
|
+
|
|
3584
|
+
/**
|
|
3585
|
+
* Imprime grafo ASCII de ondas e tarefas lido do PLAN.md.
|
|
3586
|
+
* @param {{ dir: string, activeSession: string|null }} opts
|
|
3587
|
+
*/
|
|
3588
|
+
function runPlanVisual(opts) {
|
|
3589
|
+
const c = useAnsiColors();
|
|
3590
|
+
printSection('OXE ▸ plan --visual');
|
|
3591
|
+
const statePath = oxeHealth.oxePaths(opts.dir).state;
|
|
3592
|
+
const stateText = fs.existsSync(statePath) ? fs.readFileSync(statePath, 'utf8') : '';
|
|
3593
|
+
const activeSession = opts.activeSession || oxeHealth.parseActiveSession(stateText) || null;
|
|
3594
|
+
const sp = oxeHealth.scopedOxePaths(opts.dir, activeSession);
|
|
3595
|
+
const planPath = sp.plan || oxeHealth.oxePaths(opts.dir).plan;
|
|
3596
|
+
|
|
3597
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
3598
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || 'modo legado'}${c ? reset : ''}`);
|
|
3599
|
+
|
|
3600
|
+
if (!fs.existsSync(planPath)) {
|
|
3601
|
+
console.log(`\n ${yellow}PLAN.md não encontrado em ${planPath}${reset}`);
|
|
3602
|
+
console.log(` ${dim}Rode /oxe-plan para criar o plano.${reset}\n`);
|
|
3603
|
+
return;
|
|
3604
|
+
}
|
|
3605
|
+
|
|
3606
|
+
const planMd = fs.readFileSync(planPath, 'utf8');
|
|
3607
|
+
const plan = oxeDashboard.parsePlan(planMd);
|
|
3608
|
+
|
|
3609
|
+
if (!plan.waves.length) {
|
|
3610
|
+
console.log(`\n ${yellow}Nenhuma onda encontrada no PLAN.md.${reset}\n`);
|
|
3611
|
+
return;
|
|
3612
|
+
}
|
|
3613
|
+
|
|
3614
|
+
console.log('');
|
|
3615
|
+
for (const wave of plan.waves) {
|
|
3616
|
+
const waveLabel = ` ${c ? yellow : ''}Onda ${wave.wave}${c ? reset : ''}`;
|
|
3617
|
+
for (let i = 0; i < wave.tasks.length; i++) {
|
|
3618
|
+
const task = wave.tasks[i];
|
|
3619
|
+
const deps = task.dependsOn.length ? ` ${c ? dim : ''}← ${task.dependsOn.join(', ')}${c ? reset : ''}` : '';
|
|
3620
|
+
const complexity = task.complexity ? ` ${c ? dim : ''}[${task.complexity}]${c ? reset : ''}` : '';
|
|
3621
|
+
if (i === 0) {
|
|
3622
|
+
console.log(`${waveLabel} ${c ? cyan : ''}${task.id}${c ? reset : ''} — ${task.title}${complexity}${deps}`);
|
|
3623
|
+
} else {
|
|
3624
|
+
console.log(`${''.padEnd(waveLabel.replace(/\x1b\[[0-9;]*m/g, '').length + 3)}${c ? cyan : ''}${task.id}${c ? reset : ''} — ${task.title}${complexity}${deps}`);
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
console.log('');
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3630
|
+
const total = plan.totalTasks;
|
|
3631
|
+
const waveCount = plan.waves.length;
|
|
3632
|
+
console.log(` ${c ? dim : ''}${total} tarefa${total !== 1 ? 's' : ''} · ${waveCount} onda${waveCount !== 1 ? 's' : ''}${c ? reset : ''}`);
|
|
3633
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} plan --visual concluído.\n`);
|
|
3634
|
+
}
|
|
3635
|
+
|
|
3636
|
+
/**
|
|
3637
|
+
* Imprime tabela ANSI spec → plan → verify (coverage matrix).
|
|
3638
|
+
* @param {{ dir: string, activeSession: string|null }} opts
|
|
3639
|
+
*/
|
|
3640
|
+
function runVerifyMatrix(opts) {
|
|
3641
|
+
const c = useAnsiColors();
|
|
3642
|
+
printSection('OXE ▸ verify --matrix');
|
|
3643
|
+
const statePath = oxeHealth.oxePaths(opts.dir).state;
|
|
3644
|
+
const stateText = fs.existsSync(statePath) ? fs.readFileSync(statePath, 'utf8') : '';
|
|
3645
|
+
const activeSession = opts.activeSession || oxeHealth.parseActiveSession(stateText) || null;
|
|
3646
|
+
const sp = oxeHealth.scopedOxePaths(opts.dir, activeSession);
|
|
3647
|
+
const specPath = sp.spec || oxeHealth.oxePaths(opts.dir).spec;
|
|
3648
|
+
const planPath = sp.plan || oxeHealth.oxePaths(opts.dir).plan;
|
|
3649
|
+
const verifyPath = sp.verify || oxeHealth.oxePaths(opts.dir).verify;
|
|
3650
|
+
|
|
3651
|
+
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
3652
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || 'modo legado'}${c ? reset : ''}`);
|
|
3653
|
+
|
|
3654
|
+
const specMd = fs.existsSync(specPath) ? fs.readFileSync(specPath, 'utf8') : '';
|
|
3655
|
+
const planMd = fs.existsSync(planPath) ? fs.readFileSync(planPath, 'utf8') : '';
|
|
3656
|
+
const verifyMd = fs.existsSync(verifyPath) ? fs.readFileSync(verifyPath, 'utf8') : '';
|
|
3657
|
+
|
|
3658
|
+
const spec = oxeDashboard.parseSpec(specMd);
|
|
3659
|
+
const plan = oxeDashboard.parsePlan(planMd);
|
|
3660
|
+
const verify = oxeDashboard.parseVerify(verifyMd);
|
|
3661
|
+
const matrix = oxeDashboard.buildCoverageMatrix(spec, plan, verify);
|
|
3662
|
+
|
|
3663
|
+
if (!matrix.length) {
|
|
3664
|
+
if (!spec.criteria.length) {
|
|
3665
|
+
console.log(`\n ${yellow}Nenhum critério A* encontrado no SPEC.md.${reset}`);
|
|
3666
|
+
console.log(` ${dim}Rode /oxe-spec para criar a especificação.${reset}\n`);
|
|
3667
|
+
} else {
|
|
3668
|
+
console.log(`\n ${yellow}Nenhum dado disponível para a matriz.${reset}\n`);
|
|
3669
|
+
}
|
|
3670
|
+
return;
|
|
3671
|
+
}
|
|
3672
|
+
|
|
3673
|
+
const statusSymbol = (s) => {
|
|
3674
|
+
if (s === 'passed') return c ? `${green}✓${reset}` : '✓';
|
|
3675
|
+
if (s === 'failed') return c ? `${red}✗${reset}` : '✗';
|
|
3676
|
+
return c ? `${dim}—${reset}` : '—';
|
|
3677
|
+
};
|
|
3678
|
+
const planSymbol = (covered) => covered ? (c ? `${green}✓${reset}` : '✓') : (c ? `${dim}✗${reset}` : '✗');
|
|
3679
|
+
|
|
3680
|
+
// Compute column widths
|
|
3681
|
+
const colId = Math.max(9, ...matrix.map((r) => r.id.length)) + 2;
|
|
3682
|
+
const colTasks = Math.max(12, ...matrix.map((r) => (r.tasks.join(', ') || '—').length)) + 2;
|
|
3683
|
+
|
|
3684
|
+
const sep = ` ${'─'.repeat(colId)}┼${'─'.repeat(colTasks)}┼──────────┼──────────`;
|
|
3685
|
+
const header = ` ${' Critério'.padEnd(colId)}│${' Tarefas'.padEnd(colTasks)}│ PLAN │ VERIFY`;
|
|
3686
|
+
|
|
3687
|
+
console.log('');
|
|
3688
|
+
console.log(c ? `${dim}${header}${reset}` : header);
|
|
3689
|
+
console.log(sep);
|
|
3690
|
+
|
|
3691
|
+
let covered = 0;
|
|
3692
|
+
let verified = 0;
|
|
3693
|
+
for (const row of matrix) {
|
|
3694
|
+
const tasks = row.tasks.length ? row.tasks.join(', ') : '—';
|
|
3695
|
+
const planCell = planSymbol(row.planCovered);
|
|
3696
|
+
const verifyCell = statusSymbol(row.verifyStatus);
|
|
3697
|
+
const label = ` ${row.id}`.padEnd(colId);
|
|
3698
|
+
const taskCell = ` ${tasks}`.padEnd(colTasks);
|
|
3699
|
+
console.log(`${label}│${taskCell}│ ${planCell} │ ${verifyCell}`);
|
|
3700
|
+
if (row.planCovered) covered++;
|
|
3701
|
+
if (row.verifyStatus === 'passed') verified++;
|
|
3702
|
+
}
|
|
3703
|
+
|
|
3704
|
+
console.log(sep);
|
|
3705
|
+
const total = matrix.length;
|
|
3706
|
+
console.log(`\n ${c ? dim : ''}${covered}/${total} critérios cobertos pelo plano · ${verified}/${total} aprovados no verify${c ? reset : ''}`);
|
|
3707
|
+
console.log(` ${c ? green : ''}✓${c ? reset : ''} verify --matrix concluído.\n`);
|
|
3708
|
+
}
|
|
3709
|
+
|
|
2437
3710
|
async function main() {
|
|
2438
3711
|
const argv = process.argv.slice(2);
|
|
2439
3712
|
let command = 'install';
|
|
@@ -2441,8 +3714,14 @@ async function main() {
|
|
|
2441
3714
|
argv[0] === 'doctor' ||
|
|
2442
3715
|
argv[0] === 'status' ||
|
|
2443
3716
|
argv[0] === 'init-oxe' ||
|
|
3717
|
+
argv[0] === 'dashboard' ||
|
|
3718
|
+
argv[0] === 'runtime' ||
|
|
3719
|
+
argv[0] === 'azure' ||
|
|
2444
3720
|
argv[0] === 'uninstall' ||
|
|
2445
3721
|
argv[0] === 'update' ||
|
|
3722
|
+
argv[0] === 'capabilities' ||
|
|
3723
|
+
argv[0] === 'plan' ||
|
|
3724
|
+
argv[0] === 'verify' ||
|
|
2446
3725
|
argv[0] === 'install'
|
|
2447
3726
|
) {
|
|
2448
3727
|
command = argv[0];
|
|
@@ -2509,6 +3788,108 @@ async function main() {
|
|
|
2509
3788
|
return;
|
|
2510
3789
|
}
|
|
2511
3790
|
|
|
3791
|
+
if (command === 'capabilities') {
|
|
3792
|
+
const cap = parseCapabilitiesArgs(argv);
|
|
3793
|
+
if (cap.help) {
|
|
3794
|
+
printBanner();
|
|
3795
|
+
usage();
|
|
3796
|
+
process.exit(0);
|
|
3797
|
+
}
|
|
3798
|
+
if (cap.parseError) {
|
|
3799
|
+
printBanner();
|
|
3800
|
+
console.error(`${red}Opção desconhecida:${reset} ${cap.unknownFlag}`);
|
|
3801
|
+
usage();
|
|
3802
|
+
process.exit(1);
|
|
3803
|
+
}
|
|
3804
|
+
printBanner();
|
|
3805
|
+
if (!fs.existsSync(cap.dir)) {
|
|
3806
|
+
console.error(`${yellow}Diretório não encontrado: ${cap.dir}${reset}`);
|
|
3807
|
+
process.exit(1);
|
|
3808
|
+
}
|
|
3809
|
+
runCapabilities(cap);
|
|
3810
|
+
return;
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
if (command === 'dashboard') {
|
|
3814
|
+
const d = parseDashboardArgs(argv);
|
|
3815
|
+
if (d.help) {
|
|
3816
|
+
printBanner();
|
|
3817
|
+
usage();
|
|
3818
|
+
process.exit(0);
|
|
3819
|
+
}
|
|
3820
|
+
if (d.parseError) {
|
|
3821
|
+
printBanner();
|
|
3822
|
+
console.error(`${red}Opção desconhecida:${reset} ${d.unknownFlag}`);
|
|
3823
|
+
usage();
|
|
3824
|
+
process.exit(1);
|
|
3825
|
+
}
|
|
3826
|
+
printBanner();
|
|
3827
|
+
await runDashboard(d);
|
|
3828
|
+
return;
|
|
3829
|
+
}
|
|
3830
|
+
|
|
3831
|
+
if (command === 'runtime') {
|
|
3832
|
+
const runtime = parseRuntimeArgs(argv);
|
|
3833
|
+
if (runtime.help) {
|
|
3834
|
+
printBanner();
|
|
3835
|
+
usage();
|
|
3836
|
+
process.exit(0);
|
|
3837
|
+
}
|
|
3838
|
+
if (runtime.parseError) {
|
|
3839
|
+
printBanner();
|
|
3840
|
+
console.error(`${red}Opção desconhecida:${reset} ${runtime.unknownFlag}`);
|
|
3841
|
+
usage();
|
|
3842
|
+
process.exit(1);
|
|
3843
|
+
}
|
|
3844
|
+
printBanner();
|
|
3845
|
+
if (!fs.existsSync(runtime.dir)) {
|
|
3846
|
+
console.error(`${yellow}Diretório não encontrado: ${runtime.dir}${reset}`);
|
|
3847
|
+
process.exit(1);
|
|
3848
|
+
}
|
|
3849
|
+
runRuntime(runtime);
|
|
3850
|
+
return;
|
|
3851
|
+
}
|
|
3852
|
+
|
|
3853
|
+
if (command === 'azure') {
|
|
3854
|
+
const azure = parseAzureArgs(argv);
|
|
3855
|
+
if (azure.help) {
|
|
3856
|
+
printBanner();
|
|
3857
|
+
usage();
|
|
3858
|
+
process.exit(0);
|
|
3859
|
+
}
|
|
3860
|
+
if (azure.parseError) {
|
|
3861
|
+
printBanner();
|
|
3862
|
+
console.error(`${red}Opção desconhecida:${reset} ${azure.unknownFlag}`);
|
|
3863
|
+
usage();
|
|
3864
|
+
process.exit(1);
|
|
3865
|
+
}
|
|
3866
|
+
printBanner();
|
|
3867
|
+
runAzure(azure);
|
|
3868
|
+
return;
|
|
3869
|
+
}
|
|
3870
|
+
|
|
3871
|
+
if (command === 'plan') {
|
|
3872
|
+
const planOpts = parsePlanArgs(argv);
|
|
3873
|
+
if (planOpts.help) { printBanner(); usage(); process.exit(0); }
|
|
3874
|
+
if (planOpts.parseError) { printBanner(); console.error(`${red}Opção desconhecida:${reset} ${planOpts.unknownFlag}`); usage(); process.exit(1); }
|
|
3875
|
+
if (!planOpts.visual) { printBanner(); console.error(`${yellow}Use oxe-cc plan --visual${reset}`); process.exit(1); }
|
|
3876
|
+
printBanner();
|
|
3877
|
+
if (!fs.existsSync(planOpts.dir)) { console.error(`${yellow}Diretório não encontrado: ${planOpts.dir}${reset}`); process.exit(1); }
|
|
3878
|
+
runPlanVisual(planOpts);
|
|
3879
|
+
return;
|
|
3880
|
+
}
|
|
3881
|
+
|
|
3882
|
+
if (command === 'verify') {
|
|
3883
|
+
const verifyOpts = parseVerifyArgs(argv);
|
|
3884
|
+
if (verifyOpts.help) { printBanner(); usage(); process.exit(0); }
|
|
3885
|
+
if (verifyOpts.parseError) { printBanner(); console.error(`${red}Opção desconhecida:${reset} ${verifyOpts.unknownFlag}`); usage(); process.exit(1); }
|
|
3886
|
+
if (!verifyOpts.matrix) { printBanner(); console.error(`${yellow}Use oxe-cc verify --matrix${reset}`); process.exit(1); }
|
|
3887
|
+
printBanner();
|
|
3888
|
+
if (!fs.existsSync(verifyOpts.dir)) { console.error(`${yellow}Diretório não encontrado: ${verifyOpts.dir}${reset}`); process.exit(1); }
|
|
3889
|
+
runVerifyMatrix(verifyOpts);
|
|
3890
|
+
return;
|
|
3891
|
+
}
|
|
3892
|
+
|
|
2512
3893
|
const opts = parseInstallArgs(argv);
|
|
2513
3894
|
|
|
2514
3895
|
if (opts.version) {
|
|
@@ -2555,7 +3936,11 @@ async function main() {
|
|
|
2555
3936
|
console.error(`${yellow}Diretório não encontrado: ${target}${reset}`);
|
|
2556
3937
|
process.exit(1);
|
|
2557
3938
|
}
|
|
2558
|
-
|
|
3939
|
+
if (opts.statusFull) {
|
|
3940
|
+
runStatusFull(target);
|
|
3941
|
+
} else {
|
|
3942
|
+
runStatus(target, { json: opts.jsonOutput, hints: opts.statusHints });
|
|
3943
|
+
}
|
|
2559
3944
|
return;
|
|
2560
3945
|
}
|
|
2561
3946
|
|
|
@@ -2571,8 +3956,8 @@ async function main() {
|
|
|
2571
3956
|
bootstrapOxe(target, { dryRun: opts.dryRun, force: opts.force });
|
|
2572
3957
|
printSummaryAndNextSteps(c0, {
|
|
2573
3958
|
bullets: opts.dryRun
|
|
2574
|
-
? ['[simulação] Seriam criados ou atualizados .oxe/STATE.md, .oxe/config.json e .oxe/
|
|
2575
|
-
: ['.oxe/STATE.md, .oxe/config.json
|
|
3959
|
+
? ['[simulação] Seriam criados ou atualizados .oxe/STATE.md, .oxe/config.json, .oxe/codebase/, .oxe/ACTIVE-RUN.json e .oxe/OXE-EVENTS.ndjson']
|
|
3960
|
+
: ['.oxe/STATE.md, .oxe/config.json, .oxe/codebase/, .oxe/ACTIVE-RUN.json e .oxe/OXE-EVENTS.ndjson (criados ou atualizados conforme --force)'],
|
|
2576
3961
|
nextSteps: [
|
|
2577
3962
|
{ desc: 'Validar o projeto:', cmd: 'npx oxe-cc doctor' },
|
|
2578
3963
|
{ desc: 'Instalar integrações IDE/CLI (se ainda não fez):', cmd: 'npx oxe-cc@latest' },
|
|
@@ -2590,6 +3975,8 @@ async function main() {
|
|
|
2590
3975
|
}
|
|
2591
3976
|
|
|
2592
3977
|
main().catch((err) => {
|
|
2593
|
-
|
|
3978
|
+
const msg = err && err.message ? err.message : String(err);
|
|
3979
|
+
console.error(`${red}Erro:${reset} ${msg}`);
|
|
3980
|
+
if (process.env.OXE_DEBUG === '1') console.error(err);
|
|
2594
3981
|
process.exit(1);
|
|
2595
3982
|
});
|