oxe-cc 0.6.4 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.cursor/commands/oxe-ask.md +11 -0
  2. package/.cursor/commands/oxe-execute.md +1 -1
  3. package/.cursor/commands/oxe-session.md +11 -0
  4. package/.github/prompts/oxe-ask.prompt.md +12 -0
  5. package/.github/prompts/oxe-execute.prompt.md +1 -1
  6. package/.github/prompts/oxe-session.prompt.md +12 -0
  7. package/README.md +363 -323
  8. package/bin/banner.txt +1 -1
  9. package/bin/lib/oxe-project-health.cjs +284 -91
  10. package/bin/oxe-cc.js +305 -123
  11. package/commands/oxe/ask.md +14 -0
  12. package/commands/oxe/session.md +16 -0
  13. package/oxe/templates/CONFIG.md +3 -2
  14. package/oxe/templates/PLAN.template.md +22 -7
  15. package/oxe/templates/SESSION.template.md +32 -0
  16. package/oxe/templates/STATE.md +10 -5
  17. package/oxe/templates/config.template.json +3 -2
  18. package/oxe/workflows/ask.md +62 -0
  19. package/oxe/workflows/checkpoint.md +10 -9
  20. package/oxe/workflows/debug.md +6 -5
  21. package/oxe/workflows/discuss.md +8 -7
  22. package/oxe/workflows/execute.md +37 -28
  23. package/oxe/workflows/forensics.md +6 -6
  24. package/oxe/workflows/help.md +39 -19
  25. package/oxe/workflows/milestone.md +12 -13
  26. package/oxe/workflows/next.md +16 -13
  27. package/oxe/workflows/obs.md +9 -8
  28. package/oxe/workflows/plan-agent.md +6 -4
  29. package/oxe/workflows/plan.md +73 -32
  30. package/oxe/workflows/project.md +1 -1
  31. package/oxe/workflows/quick.md +11 -7
  32. package/oxe/workflows/references/flow-robustness-contract.md +80 -0
  33. package/oxe/workflows/references/session-path-resolution.md +71 -0
  34. package/oxe/workflows/research.md +9 -8
  35. package/oxe/workflows/security.md +7 -6
  36. package/oxe/workflows/session.md +153 -0
  37. package/oxe/workflows/spec.md +41 -27
  38. package/oxe/workflows/ui-review.md +3 -3
  39. package/oxe/workflows/ui-spec.md +3 -3
  40. package/oxe/workflows/validate-gaps.md +5 -4
  41. package/oxe/workflows/verify.md +37 -21
  42. package/oxe/workflows/workstream.md +16 -15
  43. package/package.json +1 -1
package/bin/oxe-cc.js CHANGED
@@ -254,12 +254,13 @@ function buildUninstallFooter(u) {
254
254
  `${p}${rm} extensões multi-agente marcadas oxe-cc (OpenCode, Gemini TOML, Windsurf workflows, Codex prompts/skills, Antigravity), se existirem.`
255
255
  );
256
256
  }
257
- if (u.ideLocal) {
258
- bullets.push(
259
- `${p}${rm} integrações OXE no repositório (.cursor, .github, .claude, .copilot, .opencode, … conforme flags).`
260
- );
261
- }
262
- if (!u.noProject) {
257
+ if (u.ideLocal) {
258
+ bullets.push(
259
+ `${p}${rm} integrações OXE no repositório (.cursor, .github, .claude, .copilot, .opencode, … conforme flags).`
260
+ );
261
+ }
262
+ if (u.globalCli) bullets.push(`${p}${rm} também o pacote npm global oxe-cc do PATH.`);
263
+ if (!u.noProject) {
263
264
  bullets.push(
264
265
  `${p}${u.dryRun ? 'Seriam removidas' : 'Removidas'} no repositório: .oxe/workflows, .oxe/templates, oxe/ e commands/oxe (o que existir).`
265
266
  );
@@ -1049,9 +1050,9 @@ function bootstrapOxe(target, opts) {
1049
1050
  }
1050
1051
 
1051
1052
  // Criar estruturas opcionais: plugins/, workstreams/, memory/
1052
- const pluginsDir = path.join(oxeDir, 'plugins');
1053
- if (!fs.existsSync(pluginsDir)) {
1054
- ensureDir(pluginsDir);
1053
+ const pluginsDir = path.join(oxeDir, 'plugins');
1054
+ if (!fs.existsSync(pluginsDir)) {
1055
+ ensureDir(pluginsDir);
1055
1056
  const pluginsReadme = path.join(PKG_ROOT, 'oxe', 'templates', 'PLUGINS.md');
1056
1057
  if (fs.existsSync(pluginsReadme)) {
1057
1058
  const destPluginsReadme = path.join(pluginsDir, 'README.md');
@@ -1062,15 +1063,44 @@ function bootstrapOxe(target, opts) {
1062
1063
  }
1063
1064
  }
1064
1065
 
1065
- const workstreamsDir = path.join(oxeDir, 'workstreams');
1066
- if (!fs.existsSync(workstreamsDir)) {
1067
- ensureDir(workstreamsDir);
1068
- }
1069
-
1070
- const memoryDir = path.join(oxeDir, 'memory');
1071
- if (!fs.existsSync(memoryDir)) {
1072
- ensureDir(memoryDir);
1073
- }
1066
+ const workstreamsDir = path.join(oxeDir, 'workstreams');
1067
+ if (!fs.existsSync(workstreamsDir)) {
1068
+ ensureDir(workstreamsDir);
1069
+ }
1070
+
1071
+ const sessionsDir = path.join(oxeDir, 'sessions');
1072
+ if (!fs.existsSync(sessionsDir)) {
1073
+ ensureDir(sessionsDir);
1074
+ }
1075
+
1076
+ const globalDir = path.join(oxeDir, 'global');
1077
+ if (!fs.existsSync(globalDir)) {
1078
+ ensureDir(globalDir);
1079
+ }
1080
+
1081
+ const globalMilestonesDir = path.join(globalDir, 'milestones');
1082
+ if (!fs.existsSync(globalMilestonesDir)) {
1083
+ ensureDir(globalMilestonesDir);
1084
+ }
1085
+
1086
+ const lessonsSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'LESSONS.template.md');
1087
+ const lessonsDest = path.join(globalDir, 'LESSONS.md');
1088
+ if (fs.existsSync(lessonsSrc) && !fs.existsSync(lessonsDest)) {
1089
+ copyFile(lessonsSrc, lessonsDest, { dryRun: false });
1090
+ console.log(`${green}init${reset} ${lessonsDest}`);
1091
+ }
1092
+
1093
+ const milestonesSrc = path.join(PKG_ROOT, 'oxe', 'templates', 'MILESTONES.template.md');
1094
+ const milestonesDest = path.join(globalDir, 'MILESTONES.md');
1095
+ if (fs.existsSync(milestonesSrc) && !fs.existsSync(milestonesDest)) {
1096
+ copyFile(milestonesSrc, milestonesDest, { dryRun: false });
1097
+ console.log(`${green}init${reset} ${milestonesDest}`);
1098
+ }
1099
+
1100
+ const memoryDir = path.join(oxeDir, 'memory');
1101
+ if (!fs.existsSync(memoryDir)) {
1102
+ ensureDir(memoryDir);
1103
+ }
1074
1104
 
1075
1105
  ensureGitignoreIgnoresOxeDir(target, { dryRun: false });
1076
1106
  }
@@ -1114,12 +1144,13 @@ function collectOxeRoutineHints(target, r, config) {
1114
1144
  * @param {boolean} c
1115
1145
  * @param {{ skipScanCompactAgeWarnings?: boolean }} [diagOpts]
1116
1146
  */
1117
- function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
1118
- const skipAge = Boolean(diagOpts.skipScanCompactAgeWarnings);
1119
- const r = oxeHealth.buildHealthReport(target);
1120
- const { config } = oxeHealth.loadOxeConfigMerged(target);
1121
-
1122
- console.log(`\n ${c ? cyan : ''}▸ Coerência .oxe/ e config${reset}`);
1147
+ function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
1148
+ const skipAge = Boolean(diagOpts.skipScanCompactAgeWarnings);
1149
+ const r = oxeHealth.buildHealthReport(target);
1150
+ const { config } = oxeHealth.loadOxeConfigMerged(target);
1151
+
1152
+ console.log(`\n ${c ? cyan : ''}▸ Coerência .oxe/ e config${reset}`);
1153
+ console.log(` ${c ? dim : ''}Saúde lógica:${c ? reset : ''} ${r.healthStatus}`);
1123
1154
 
1124
1155
  if (r.configParseError) {
1125
1156
  console.log(` ${red}FALHA${reset} config.json: ${r.configParseError}`);
@@ -1135,9 +1166,18 @@ function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
1135
1166
  );
1136
1167
  }
1137
1168
 
1138
- if (r.phase) {
1139
- console.log(` ${c ? dim : ''}Fase (STATE.md):${c ? reset : ''} ${r.phase}`);
1140
- }
1169
+ if (r.phase) {
1170
+ console.log(` ${c ? dim : ''}Fase (STATE.md):${c ? reset : ''} ${r.phase}`);
1171
+ }
1172
+ if (r.activeSession) {
1173
+ console.log(` ${c ? dim : ''}Sessão ativa:${c ? reset : ''} ${r.activeSession}`);
1174
+ }
1175
+ if (r.planSelfEvaluation && r.planSelfEvaluation.hasSection) {
1176
+ const best = r.planSelfEvaluation.bestPlan || '—';
1177
+ const conf =
1178
+ typeof r.planSelfEvaluation.confidence === 'number' ? `${r.planSelfEvaluation.confidence}%` : '—';
1179
+ console.log(` ${c ? dim : ''}Plano (autoavaliação):${c ? reset : ''} melhor=${best} | confiança=${conf}`);
1180
+ }
1141
1181
 
1142
1182
  if (!skipAge) {
1143
1183
  if (config.scan_max_age_days > 0 && r.scanDate && r.stale.stale) {
@@ -1172,12 +1212,18 @@ function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
1172
1212
  console.log(` ${c ? dim : ''}Scan (ignorar):${c ? reset : ''} ${config.scan_ignore_globs.join(', ')}`);
1173
1213
  }
1174
1214
 
1175
- for (const w of r.phaseWarn) {
1176
- console.log(` ${yellow}AVISO${reset} ${w}`);
1177
- }
1178
- if (r.summaryGapWarn) {
1179
- console.log(` ${yellow}AVISO${reset} ${r.summaryGapWarn}`);
1180
- }
1215
+ for (const w of r.phaseWarn) {
1216
+ console.log(` ${yellow}AVISO${reset} ${w}`);
1217
+ }
1218
+ for (const w of r.sessionWarn) {
1219
+ console.log(` ${yellow}AVISO${reset} ${w}`);
1220
+ }
1221
+ for (const w of r.installWarn) {
1222
+ console.log(` ${yellow}AVISO${reset} ${w}`);
1223
+ }
1224
+ if (r.summaryGapWarn) {
1225
+ console.log(` ${yellow}AVISO${reset} ${r.summaryGapWarn}`);
1226
+ }
1181
1227
  for (const w of r.specWarn) {
1182
1228
  console.log(` ${yellow}AVISO${reset} ${w}`);
1183
1229
  }
@@ -1204,20 +1250,25 @@ function runStatus(target, opts = {}) {
1204
1250
  nextStep: report.next.step,
1205
1251
  cursorCmd: report.next.cursorCmd,
1206
1252
  reason: report.next.reason,
1207
- artifacts: report.next.artifacts,
1208
- phase: report.phase,
1209
- scanDate: report.scanDate,
1210
- staleScan: report.stale,
1211
- compactDate: report.compactDate,
1212
- staleCompact: report.staleCompact,
1213
- diagnostics: {
1214
- configParseError: report.configParseError,
1215
- typeErrors: report.typeErrors,
1216
- unknownConfigKeys: report.unknownConfigKeys,
1217
- phaseWarnings: report.phaseWarn,
1218
- summaryGapWarning: report.summaryGapWarn,
1219
- specWarnings: report.specWarn,
1220
- planWarnings: report.planWarn,
1253
+ artifacts: report.next.artifacts,
1254
+ phase: report.phase,
1255
+ healthStatus: report.healthStatus,
1256
+ activeSession: report.activeSession,
1257
+ scanDate: report.scanDate,
1258
+ staleScan: report.stale,
1259
+ compactDate: report.compactDate,
1260
+ staleCompact: report.staleCompact,
1261
+ planSelfEvaluation: report.planSelfEvaluation,
1262
+ diagnostics: {
1263
+ configParseError: report.configParseError,
1264
+ typeErrors: report.typeErrors,
1265
+ unknownConfigKeys: report.unknownConfigKeys,
1266
+ phaseWarnings: report.phaseWarn,
1267
+ sessionWarnings: report.sessionWarn,
1268
+ installWarnings: report.installWarn,
1269
+ summaryGapWarning: report.summaryGapWarn,
1270
+ specWarnings: report.specWarn,
1271
+ planWarnings: report.planWarn,
1221
1272
  },
1222
1273
  };
1223
1274
  if (opts.hints) {
@@ -1254,9 +1305,12 @@ function runStatus(target, opts = {}) {
1254
1305
  console.log(` ${c ? dim : ''}No Cursor (referência):${c ? reset : ''} ${c ? cyan : ''}${next.cursorCmd}${reset}`);
1255
1306
  console.log(` ${c ? dim : ''}Motivo:${c ? reset : ''} ${next.reason}`);
1256
1307
 
1257
- printSummaryAndNextSteps(c, {
1258
- bullets: [`Artefatos em jogo: ${next.artifacts.join(', ')}`],
1259
- nextSteps: [
1308
+ printSummaryAndNextSteps(c, {
1309
+ bullets: [
1310
+ `Saúde lógica: ${report.healthStatus}`,
1311
+ `Artefatos em jogo: ${next.artifacts.join(', ')}`,
1312
+ ],
1313
+ nextSteps: [
1260
1314
  { desc: 'Diagnóstico completo (inclui pacote de workflows):', cmd: 'npx oxe-cc doctor' },
1261
1315
  { desc: 'Ação sugerida no agente:', cmd: next.cursorCmd },
1262
1316
  ],
@@ -1355,7 +1409,7 @@ function runDoctor(target) {
1355
1409
  'CONVENTIONS.md',
1356
1410
  'CONCERNS.md',
1357
1411
  ];
1358
- if (fs.existsSync(cbDir)) {
1412
+ if (fs.existsSync(cbDir)) {
1359
1413
  const missingMaps = expectedMaps.filter((f) => !fs.existsSync(path.join(cbDir, f)));
1360
1414
  if (missingMaps.length) {
1361
1415
  console.log(
@@ -1364,17 +1418,21 @@ function runDoctor(target) {
1364
1418
  } else {
1365
1419
  console.log(`${green}OK${reset} .oxe/codebase/ com os ${expectedMaps.length} mapas esperados`);
1366
1420
  }
1367
- }
1368
-
1369
- printOxeHealthDiagnostics(target, c);
1370
-
1371
- console.log(`\n ${green}Diagnóstico OK nenhum bloqueio crítico encontrado.${reset}`);
1372
- printSummaryAndNextSteps(c, {
1373
- bullets: [
1374
- `Projeto em ${target}`,
1375
- `Workflows conferidos em ${wfLabel}`,
1376
- 'Node.js e (quando existir) config.json validados',
1377
- ],
1421
+ }
1422
+
1423
+ printOxeHealthDiagnostics(target, c);
1424
+ const report = oxeHealth.buildHealthReport(target);
1425
+ const statusColor = report.healthStatus === 'healthy' ? green : report.healthStatus === 'warning' ? yellow : red;
1426
+ console.log(`\n ${statusColor}Diagnóstico ${report.healthStatus}${reset}`);
1427
+ if (report.healthStatus === 'broken') {
1428
+ process.exitCode = 1;
1429
+ }
1430
+ printSummaryAndNextSteps(c, {
1431
+ bullets: [
1432
+ `Projeto em ${target}`,
1433
+ `Workflows conferidos em ${wfLabel}`,
1434
+ `Saúde lógica: ${report.healthStatus}`,
1435
+ ],
1378
1436
  nextSteps: [
1379
1437
  { desc: 'Mapear ou atualizar o codebase no agente:', cmd: '/oxe-scan' },
1380
1438
  { desc: 'Ver ajuda e ordem dos passos OXE:', cmd: '/oxe-help' },
@@ -1388,7 +1446,7 @@ function runDoctor(target) {
1388
1446
  * npm install -g oxe-cc@version (same version as this running CLI).
1389
1447
  * @returns {boolean}
1390
1448
  */
1391
- function installGlobalCliPackage() {
1449
+ function installGlobalCliPackage() {
1392
1450
  const name = readPkgName();
1393
1451
  const ver = readPkgVersion();
1394
1452
  const spec = `${name}@${ver}`;
@@ -1411,7 +1469,99 @@ function installGlobalCliPackage() {
1411
1469
  `\n ${c ? yellow : ''}⚠${c ? reset : ''} npm install -g falhou. Tente manualmente: ${c ? cyan : ''}npm install -g ${spec}${c ? reset : ''}\n`
1412
1470
  );
1413
1471
  return false;
1414
- }
1472
+ }
1473
+
1474
+ /**
1475
+ * `npm uninstall -g oxe-cc` com a mesma semântica cross-platform do instalador.
1476
+ * @returns {boolean}
1477
+ */
1478
+ function uninstallGlobalCliPackage() {
1479
+ const name = readPkgName();
1480
+ const c = useAnsiColors();
1481
+ const dimOrEmpty = c ? dim : '';
1482
+ const resetOrEmpty = c ? reset : '';
1483
+ console.log(`\n ${dimOrEmpty}npm uninstall -g ${name}${resetOrEmpty}\n`);
1484
+ const r = spawnSync('npm', ['uninstall', '-g', name], {
1485
+ stdio: 'inherit',
1486
+ shell: true,
1487
+ env: process.env,
1488
+ });
1489
+ if (r.status === 0) {
1490
+ console.log(
1491
+ `\n ${c ? green : ''}✓${c ? reset : ''} pacote global ${c ? cyan : ''}${name}${c ? reset : ''} removido do npm global.\n`
1492
+ );
1493
+ return true;
1494
+ }
1495
+ console.log(
1496
+ `\n ${c ? yellow : ''}⚠${c ? reset : ''} npm uninstall -g falhou. Remova manualmente: ${c ? cyan : ''}npm uninstall -g ${name}${c ? reset : ''}\n`
1497
+ );
1498
+ return false;
1499
+ }
1500
+
1501
+ /**
1502
+ * Best-effort: detecta se esta execução vem de uma instalação global do npm.
1503
+ * Usa `npm root -g` para evitar confundir execução local do repositório com pacote global.
1504
+ * @returns {boolean}
1505
+ */
1506
+ function isRunningFromGlobalNpmInstall() {
1507
+ try {
1508
+ const r = spawnSync('npm', ['root', '-g'], {
1509
+ encoding: 'utf8',
1510
+ shell: true,
1511
+ env: process.env,
1512
+ });
1513
+ if (r.status !== 0) return false;
1514
+ const root = String(r.stdout || '').trim();
1515
+ if (!root) return false;
1516
+ const rel = path.relative(path.resolve(root), PKG_ROOT);
1517
+ return rel && !rel.startsWith('..') && !path.isAbsolute(rel);
1518
+ } catch {
1519
+ return false;
1520
+ }
1521
+ }
1522
+
1523
+ /** @returns {string[]} */
1524
+ function updateForwardedInstallFlags() {
1525
+ return [
1526
+ '--cursor',
1527
+ '--copilot',
1528
+ '--copilot-cli',
1529
+ '--all-agents',
1530
+ '--opencode',
1531
+ '--gemini',
1532
+ '--codex',
1533
+ '--windsurf',
1534
+ '--antigravity',
1535
+ '--vscode',
1536
+ '--no-commands',
1537
+ '--no-agents',
1538
+ '--no-init-oxe',
1539
+ '--oxe-only',
1540
+ '--global',
1541
+ '--local',
1542
+ '--ide-global',
1543
+ '--ide-local',
1544
+ '--global-cli',
1545
+ '-g',
1546
+ '--no-global-cli',
1547
+ '-l',
1548
+ '--no-install-config',
1549
+ '--force',
1550
+ '-f',
1551
+ '--all',
1552
+ '-a',
1553
+ '--config-dir',
1554
+ '-c',
1555
+ ];
1556
+ }
1557
+
1558
+ /**
1559
+ * @param {string[]} rest
1560
+ * @returns {boolean}
1561
+ */
1562
+ function updateArgsExplicitlyControlGlobalCli(rest) {
1563
+ return rest.includes('--global-cli') || rest.includes('-g') || rest.includes('--no-global-cli') || rest.includes('-l');
1564
+ }
1415
1565
 
1416
1566
  /**
1417
1567
  * After copying OXE into the project: optionally install the CLI globally (pergunta interativa ou flags).
@@ -1499,21 +1649,22 @@ ${green}Uso:${reset}
1499
1649
  npx oxe-cc uninstall [opções] [pasta-do-projeto]
1500
1650
  npx oxe-cc update [opções] [argumentos extras…]
1501
1651
 
1502
- ${green}uninstall${reset} (remove OXE da pasta do usuário + pastas de workflows no repo)
1503
- --cursor / --copilot / --copilot-cli só essa integração (omissão = todas)
1504
- --all-agents também remove ficheiros multi-plataforma (com --copilot-cli implícito)
1505
- --ide-local remove integrações IDE neste repositório (.cursor, .github, .claude, .copilot, …)
1506
- --ide-only não apagar .oxe/workflows, oxe/, etc. no projeto
1507
- --config-dir <caminho> com exatamente uma flag IDE acima (não combina com --ide-local)
1508
- --dry-run
1509
- --dir <pasta> raiz do projeto (padrão: diretório atual)
1652
+ ${green}uninstall${reset} (remove OXE da pasta do usuário + pastas de workflows no repo)
1653
+ --cursor / --copilot / --copilot-cli só essa integração (omissão = todas)
1654
+ --all-agents também remove ficheiros multi-plataforma (com --copilot-cli implícito)
1655
+ --ide-local remove integrações IDE neste repositório (.cursor, .github, .claude, .copilot, …)
1656
+ --ide-only não apagar .oxe/workflows, oxe/, etc. no projeto
1657
+ --global-cli, -g também executa npm uninstall -g oxe-cc
1658
+ --config-dir <caminho> com exatamente uma flag IDE acima (não combina com --ide-local)
1659
+ --dry-run
1660
+ --dir <pasta> raiz do projeto (padrão: diretório atual)
1510
1661
 
1511
1662
  ${green}update${reset} (executa npx oxe-cc@latest --force na pasta do projeto)
1512
1663
  --check só consulta npm: compara versão em execução com latest (saída 0=ok, 1=há mais nova, 2=erro; incompatível com --dry-run)
1513
1664
  --if-newer só executa o npx se existir versão mais nova no npm (falha de rede/registry: saída 2, sem npx)
1514
1665
  --dir <pasta> pasta em que o npx roda (padrão: atual; ignorada com --check)
1515
1666
  --dry-run mostra o comando sem executar
1516
- [argumentos extras…] repassados ao oxe-cc (ex.: --cursor --global)
1667
+ [argumentos extras…] repassados ao oxe-cc (ex.: --cursor --global, --ide-local, --global-cli)
1517
1668
  ${dim}CI / sem rede:${reset} OXE_UPDATE_SKIP_REGISTRY=1 desativa consultas (--check sai 2; --if-newer sai 2 sem npx)
1518
1669
 
1519
1670
  ${green}Opções da instalação:${reset}
@@ -1856,7 +2007,7 @@ function runInstall(opts) {
1856
2007
  console.log(` ${c ? green : ''}✓${c ? reset : ''} Instalação concluída com sucesso.\n`);
1857
2008
  }
1858
2009
 
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 */
2010
+ /** @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
2011
 
1861
2012
  /**
1862
2013
  * @param {UninstallOpts} u
@@ -1953,10 +2104,10 @@ function uninstallLocalIdeFromProject(u, removedPaths) {
1953
2104
  }
1954
2105
 
1955
2106
  /**
1956
- * @param {string[]} argv
1957
- * @returns {UninstallOpts}
1958
- */
1959
- function parseUninstallArgs(argv) {
2107
+ * @param {string[]} argv
2108
+ * @returns {UninstallOpts}
2109
+ */
2110
+ function parseUninstallArgs(argv) {
1960
2111
  /** @type {UninstallOpts} */
1961
2112
  const out = {
1962
2113
  help: false,
@@ -1964,10 +2115,11 @@ function parseUninstallArgs(argv) {
1964
2115
  cursor: false,
1965
2116
  copilot: false,
1966
2117
  copilotCli: false,
1967
- allAgents: false,
1968
- ideLocal: false,
1969
- ideExplicit: false,
1970
- noProject: false,
2118
+ allAgents: false,
2119
+ globalCli: false,
2120
+ ideLocal: false,
2121
+ ideExplicit: false,
2122
+ noProject: false,
1971
2123
  dir: process.cwd(),
1972
2124
  explicitConfigDir: null,
1973
2125
  parseError: false,
@@ -1990,13 +2142,15 @@ function parseUninstallArgs(argv) {
1990
2142
  } else if (a === '--copilot-cli') {
1991
2143
  out.copilotCli = true;
1992
2144
  out.ideExplicit = true;
1993
- } else if (a === '--all-agents') {
1994
- out.allAgents = true;
1995
- out.copilotCli = true;
1996
- out.ideExplicit = true;
1997
- } else if (a === '--ide-local') out.ideLocal = true;
1998
- else if (a === '--ide-only') out.noProject = true;
1999
- else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
2145
+ } else if (a === '--all-agents') {
2146
+ out.allAgents = true;
2147
+ out.copilotCli = true;
2148
+ out.ideExplicit = true;
2149
+ } else if (a === '--global-cli' || a === '-g') {
2150
+ out.globalCli = true;
2151
+ } else if (a === '--ide-local') out.ideLocal = true;
2152
+ else if (a === '--ide-only') out.noProject = true;
2153
+ else if (a === '--dir' && argv[i + 1]) out.dir = path.resolve(argv[++i]);
2000
2154
  else if (!a.startsWith('-')) rest.push(path.resolve(a));
2001
2155
  else {
2002
2156
  out.parseError = true;
@@ -2076,7 +2230,7 @@ function rmDirIfEmpty(dirPath, opts) {
2076
2230
  /**
2077
2231
  * @param {UninstallOpts} u
2078
2232
  */
2079
- function runUninstall(u) {
2233
+ function runUninstall(u) {
2080
2234
  assertNotWslWindowsNode();
2081
2235
  const c = useAnsiColors();
2082
2236
  const home = os.homedir();
@@ -2233,7 +2387,7 @@ function runUninstall(u) {
2233
2387
  }
2234
2388
  }
2235
2389
 
2236
- if (!u.dryRun && (u.cursor || u.copilot || u.copilotCli)) {
2390
+ if (!u.dryRun && (u.cursor || u.copilot || u.copilotCli)) {
2237
2391
  const prev = oxeManifest.loadFileManifest(home);
2238
2392
  const next = { ...prev };
2239
2393
  for (const p of removedPaths) delete next[p];
@@ -2249,20 +2403,31 @@ function runUninstall(u) {
2249
2403
  delete next[instPath];
2250
2404
  }
2251
2405
  }
2252
- oxeManifest.writeFileManifest(home, next, readPkgVersion());
2253
- }
2254
-
2255
- printSummaryAndNextSteps(c, buildUninstallFooter(u));
2256
- console.log(` ${c ? green : ''}✓${c ? reset : ''} Desinstalação concluída com sucesso.\n`);
2257
- }
2258
-
2259
- /** @typedef {{ help: boolean, dryRun: boolean, check: boolean, ifNewer: boolean, dir: string, rest: string[], parseError: boolean, unknownFlag: string, conflictFlags: string|null }} UpdateOpts */
2406
+ oxeManifest.writeFileManifest(home, next, readPkgVersion());
2407
+ }
2408
+
2409
+ if (u.globalCli && !u.dryRun) {
2410
+ uninstallGlobalCliPackage();
2411
+ } else if (u.globalCli && u.dryRun) {
2412
+ console.log(`${dim}npm${reset} npm uninstall -g ${readPkgName()}`);
2413
+ }
2414
+
2415
+ printSummaryAndNextSteps(c, buildUninstallFooter(u));
2416
+ if (!u.globalCli) {
2417
+ console.log(
2418
+ ` ${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`
2419
+ );
2420
+ }
2421
+ console.log(` ${c ? green : ''}✓${c ? reset : ''} Desinstalação concluída com sucesso.\n`);
2422
+ }
2423
+
2424
+ /** @typedef {{ help: boolean, dryRun: boolean, check: boolean, ifNewer: boolean, dir: string, rest: string[], parseError: boolean, unknownFlag: string, conflictFlags: string|null }} UpdateOpts */
2260
2425
 
2261
2426
  /**
2262
2427
  * @param {string[]} argv
2263
2428
  * @returns {UpdateOpts}
2264
2429
  */
2265
- function parseUpdateArgs(argv) {
2430
+ function parseUpdateArgs(argv) {
2266
2431
  /** @type {UpdateOpts} */
2267
2432
  const out = {
2268
2433
  help: false,
@@ -2275,25 +2440,31 @@ function parseUpdateArgs(argv) {
2275
2440
  unknownFlag: '',
2276
2441
  conflictFlags: null,
2277
2442
  };
2278
- let dirExplicit = false;
2279
- let firstPositionalConsumed = false;
2280
- for (let i = 0; i < argv.length; i++) {
2281
- const a = argv[i];
2282
- if (a === '-h' || a === '--help') out.help = true;
2443
+ let dirExplicit = false;
2444
+ let firstPositionalConsumed = false;
2445
+ const passthroughFlags = new Set(updateForwardedInstallFlags());
2446
+ for (let i = 0; i < argv.length; i++) {
2447
+ const a = argv[i];
2448
+ if (a === '-h' || a === '--help') out.help = true;
2283
2449
  else if (a === '--dry-run') out.dryRun = true;
2284
2450
  else if (a === '--check') out.check = true;
2285
2451
  else if (a === '--if-newer') out.ifNewer = true;
2286
2452
  else if (a === '--dir' && argv[i + 1]) {
2287
2453
  out.dir = path.resolve(argv[++i]);
2288
2454
  dirExplicit = true;
2289
- } else if (!a.startsWith('-')) {
2290
- if (!dirExplicit && !firstPositionalConsumed) {
2291
- out.dir = path.resolve(a);
2292
- firstPositionalConsumed = true;
2293
- } else out.rest.push(a);
2294
- } else {
2295
- out.parseError = true;
2296
- out.unknownFlag = a;
2455
+ } else if (!a.startsWith('-')) {
2456
+ if (!dirExplicit && !firstPositionalConsumed) {
2457
+ out.dir = path.resolve(a);
2458
+ firstPositionalConsumed = true;
2459
+ } else out.rest.push(a);
2460
+ } else if (passthroughFlags.has(a)) {
2461
+ out.rest.push(a);
2462
+ if ((a === '--config-dir' || a === '-c') && argv[i + 1]) {
2463
+ out.rest.push(argv[++i]);
2464
+ }
2465
+ } else {
2466
+ out.parseError = true;
2467
+ out.unknownFlag = a;
2297
2468
  break;
2298
2469
  }
2299
2470
  }
@@ -2348,7 +2519,7 @@ function runUpdateVersionCheck(u) {
2348
2519
  /**
2349
2520
  * @param {UpdateOpts} u
2350
2521
  */
2351
- function runUpdate(u) {
2522
+ function runUpdate(u) {
2352
2523
  assertNotWslWindowsNode();
2353
2524
  const c = useAnsiColors();
2354
2525
  if (u.dryRun) {
@@ -2359,8 +2530,12 @@ function runUpdate(u) {
2359
2530
  );
2360
2531
  }
2361
2532
  console.log(` ${dim}Comando que seria executado (instalação):${reset}`);
2362
- console.log(` ${cyan}npx -y oxe-cc@latest --force --no-global-cli -l${reset} ${u.rest.join(' ')}`);
2363
- console.log(` ${dim}Diretório:${reset} ${u.dir}`);
2533
+ const dryRunArgs = ['-y', 'oxe-cc@latest', '--force'];
2534
+ if (updateArgsExplicitlyControlGlobalCli(u.rest)) dryRunArgs.push(...u.rest);
2535
+ else if (isRunningFromGlobalNpmInstall()) dryRunArgs.push('--global-cli');
2536
+ else dryRunArgs.push('--no-global-cli', '-l');
2537
+ console.log(` ${cyan}npx ${dryRunArgs.join(' ')}${reset}`);
2538
+ console.log(` ${dim}Diretório:${reset} ${u.dir}`);
2364
2539
  printSummaryAndNextSteps(c, {
2365
2540
  bullets: [
2366
2541
  '[simulação] O npx baixaria o pacote oxe-cc@latest e rodaria a instalação com --force.',
@@ -2405,13 +2580,20 @@ function runUpdate(u) {
2405
2580
  console.log(
2406
2581
  ` ${dim}Há versão mais nova no npm (${res.version} > ${current}); a executar npx…${reset}\n`
2407
2582
  );
2408
- }
2409
-
2410
- printSection('OXE ▸ update');
2411
- const args = ['-y', 'oxe-cc@latest', '--force', '--no-global-cli', '-l', ...u.rest];
2412
- const r = spawnSync('npx', args, {
2413
- cwd: u.dir,
2414
- stdio: 'inherit',
2583
+ }
2584
+
2585
+ printSection('OXE ▸ update');
2586
+ const args = ['-y', 'oxe-cc@latest', '--force'];
2587
+ if (updateArgsExplicitlyControlGlobalCli(u.rest)) {
2588
+ args.push(...u.rest);
2589
+ } else if (isRunningFromGlobalNpmInstall()) {
2590
+ args.push('--global-cli', ...u.rest);
2591
+ } else {
2592
+ args.push('--no-global-cli', '-l', ...u.rest);
2593
+ }
2594
+ const r = spawnSync('npx', args, {
2595
+ cwd: u.dir,
2596
+ stdio: 'inherit',
2415
2597
  env: { ...process.env },
2416
2598
  shell: process.platform === 'win32',
2417
2599
  });
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: oxe:ask
3
+ description: "Perguntar ao OXE sobre a situação atual com leitura robusta de STATE, sessão ativa e artefatos da trilha"
4
+ argument-hint: "[pergunta em texto livre]"
5
+ allowed-tools:
6
+ - Read
7
+ - Bash
8
+ - Glob
9
+ - Grep
10
+ ---
11
+
12
+ **Workflow canónico:** `oxe/workflows/ask.md`
13
+
14
+ Execute integralmente esse ficheiro na raiz do repositório em que estás a trabalhar. Usa o texto em `$ARGUMENTS` como pergunta e foco.
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: oxe:session
3
+ description: "Gerir sessões OXE: new, list, switch, resume, status, close, migrate"
4
+ argument-hint: "[new <nome> | list | switch <id> | resume <id> | status | close | migrate <nome>]"
5
+ allowed-tools:
6
+ - Read
7
+ - Bash
8
+ - Glob
9
+ - Grep
10
+ - Write
11
+ - Task
12
+ ---
13
+
14
+ **Workflow canónico:** `oxe/workflows/session.md`
15
+
16
+ Executa integralmente esse ficheiro na raiz do repositório em que estás a trabalhar. Usa `$ARGUMENTS` como subcomando e foco da operação da sessão ativa.
@@ -8,8 +8,9 @@ Copie `oxe/templates/config.template.json` para **`.oxe/config.json`** no seu pr
8
8
  |-------|------|-------------|
9
9
  | `profile` | string | Profile de execução: `balanced` (padrão) \| `strict` \| `fast` \| `legacy`. Expande automaticamente outras keys — keys explícitas prevalecem. Ver tabela abaixo. |
10
10
  | `discuss_before_plan` | boolean | Se `true`, o fluxo recomenda **`oxe:discuss`** entre spec e plan. |
11
- | `verification_depth` | string | Profundidade da verificação: `standard` (padrão) \| `thorough` (ativa Camada 5 — validate-gaps automático) \| `quick` (skip camadas 3–4 e UAT). |
12
- | `security_in_verify` | boolean | Se `true`, executa auditoria OWASP automaticamente no **verify** como **Camada 6** (produz `.oxe/SECURITY.md`). Achados P0 bloqueiam `verify_complete`. Padrão: `false`. |
11
+ | `verification_depth` | string | Profundidade da verificação: `standard` (padrão) \| `thorough` (ativa Camada 5 — validate-gaps automático) \| `quick` (skip camadas 3–4 e UAT). |
12
+ | `plan_confidence_threshold` | number | Limiar mínimo de confiança para o `execute` aceitar um `PLAN.md`. Padrão: `70`. Abaixo disso, o fluxo deve reduzir incerteza antes de implementar. |
13
+ | `security_in_verify` | boolean | Se `true`, executa auditoria OWASP automaticamente no **verify** como **Camada 6** (produz `.oxe/SECURITY.md`). Achados P0 bloqueiam `verify_complete`. Padrão: `false`. |
13
14
  | `after_verify_suggest_pr` | boolean | Se `true`, o workflow **verify** inclui checklist de PR no fim. |
14
15
  | `after_verify_draft_commit` | boolean | Se `true`, o **verify** propõe rascunho de mensagem de commit alinhado aos critérios de aceite. |
15
16
  | `after_verify_suggest_uat` | boolean | Se `true`, o **verify** gera checklist UAT (Camada 4). Ativo automaticamente com `profile: strict`. |