oxe-cc 1.12.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.github/dependabot.yml +31 -0
  2. package/.github/workflows/ci.yml +141 -56
  3. package/.github/workflows/release.yml +114 -89
  4. package/CHANGELOG.md +866 -754
  5. package/README.md +600 -736
  6. package/bin/lib/oxe-agent-install.cjs +299 -284
  7. package/bin/lib/oxe-artifact-catalog.cjs +376 -0
  8. package/bin/lib/oxe-command-registry.cjs +31 -0
  9. package/bin/lib/oxe-context-engine.cjs +11 -11
  10. package/bin/lib/oxe-core-command-handlers.cjs +82 -0
  11. package/bin/lib/oxe-dashboard.cjs +140 -140
  12. package/bin/lib/oxe-manifest.cjs +20 -20
  13. package/bin/lib/oxe-npm-version.cjs +6 -4
  14. package/bin/lib/oxe-plugin-cli.cjs +95 -0
  15. package/bin/lib/oxe-plugins.cjs +94 -3
  16. package/bin/lib/oxe-process.cjs +67 -0
  17. package/bin/lib/oxe-project-health.cjs +2846 -2781
  18. package/bin/lib/oxe-runtime-semantics.cjs +68 -69
  19. package/bin/oxe-cc.js +369 -353
  20. package/docs/INTEGRATION.md +182 -0
  21. package/docs/QUALITY-GATES.md +46 -0
  22. package/docs/RELEASE-READINESS.md +86 -61
  23. package/docs/RUNTIME-SMOKE-MATRIX.md +137 -135
  24. package/docs/oxe-artifact-map.html +1172 -0
  25. package/lib/sdk/index.cjs +20 -0
  26. package/lib/sdk/index.d.ts +971 -876
  27. package/lib/sdk/index.types.ts +933 -0
  28. package/oxe/templates/PLUGINS.md +8 -1
  29. package/oxe/templates/STATE-REFERENCE.md +125 -0
  30. package/oxe/templates/STATE.md +11 -121
  31. package/oxe/workflows/help.md +2 -0
  32. package/package.json +129 -108
  33. package/packages/runtime/package.json +18 -18
  34. package/packages/runtime/src/evidence/evidence-store.ts +2 -2
  35. package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +728 -728
  36. package/packages/runtime/src/workspace/strategies/git-worktree.ts +24 -24
  37. package/packages/runtime/tsconfig.json +8 -2
  38. package/vscode-extension/.vscodeignore +2 -0
  39. package/vscode-extension/package.json +193 -185
  40. package/vscode-extension/src/extension.js +11 -1
@@ -0,0 +1,376 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * OXE artifact catalog — single source of truth for what lives under `.oxe/`.
5
+ *
6
+ * Every entry here drives THREE outputs from ONE definition:
7
+ * - the generated `.oxe/README.md` legend (`renderLegend`)
8
+ * - the live `oxe-cc map` tree (`buildMapModel` + `renderMap`)
9
+ * - the host-integration model (`oxe-cc map --json`, SDK `artifacts.*`)
10
+ *
11
+ * Design intent: the `.oxe/` directory is *lean at install* (only STATE.md,
12
+ * config.json and this README). Everything else is created lazily, on first
13
+ * use, by the workflow named in `createdBy`. The catalog documents the FULL
14
+ * structure so users understand the flow without the empty folders cluttering
15
+ * their editor file tree.
16
+ *
17
+ * Entry shape:
18
+ * {
19
+ * path: 'codebase/', // relative to `.oxe/`; trailing `/` ⇒ directory
20
+ * kind: 'file' | 'dir',
21
+ * purpose: 'one-line what-it-is',
22
+ * createdBy: 'install' | 'scan' | 'spec' | 'plan' | 'execute' | 'verify'
23
+ * | 'runtime' | 'conduct' | 'session' | 'workstream' | 'milestone'
24
+ * | 'capabilities' | 'plugins' | 'azure' | 'memory' | 'distill',
25
+ * group: 'core' | 'discovery' | 'spec' | 'plan' | 'execute' | 'verify'
26
+ * | 'autonomous' | 'memory' | 'project' | 'platform',
27
+ * }
28
+ */
29
+
30
+ const fs = require('fs');
31
+ const path = require('path');
32
+
33
+ /** @type {{key:string,label:string,order:number}[]} */
34
+ const GROUPS = [
35
+ { key: 'core', label: 'Núcleo (sempre presente após install)', order: 0 },
36
+ { key: 'discovery', label: 'Descoberta — scan / compact', order: 1 },
37
+ { key: 'spec', label: 'Especificação — spec / discuss / research', order: 2 },
38
+ { key: 'plan', label: 'Planejamento — plan / plan-agents / quick', order: 3 },
39
+ { key: 'execute', label: 'Execução — runtime operacional', order: 4 },
40
+ { key: 'verify', label: 'Verificação — verify / gaps / security / ui', order: 5 },
41
+ { key: 'autonomous', label: 'Modo autônomo — conduct / agent / swarm', order: 6 },
42
+ { key: 'memory', label: 'Memória & aprendizado', order: 7 },
43
+ { key: 'project', label: 'Gestão de projeto — sessões / workstreams / milestones', order: 8 },
44
+ { key: 'platform', label: 'Plataforma — capabilities / plugins / cloud / install', order: 9 },
45
+ { key: 'layout', label: 'Layout instalado (árvore canônica copiada no install)', order: 10 },
46
+ ];
47
+
48
+ /** @type {Array<{path:string,kind:'file'|'dir',purpose:string,createdBy:string,group:string}>} */
49
+ const ARTIFACT_CATALOG = [
50
+ // ── core ────────────────────────────────────────────────────────────────
51
+ { path: 'STATE.md', kind: 'file', purpose: 'Estado curto: fase, sessão ativa, próximo passo, bloqueios', createdBy: 'install', group: 'core' },
52
+ { path: 'config.json', kind: 'file', purpose: 'Configuração do projeto (profile, verificação, permissões, azure)', createdBy: 'install', group: 'core' },
53
+ { path: 'README.md', kind: 'file', purpose: 'Esta legenda — o que cada artefato é e quando aparece', createdBy: 'install', group: 'core' },
54
+
55
+ // ── discovery ───────────────────────────────────────────────────────────
56
+ { path: 'codebase/', kind: 'dir', purpose: 'Mapas do repositório (OVERVIEW, STACK, STRUCTURE, TESTING, INTEGRATIONS, CONVENTIONS, CONCERNS)', createdBy: 'scan', group: 'discovery' },
57
+ { path: 'CODEBASE-DELTA.md', kind: 'file', purpose: 'Diff incremental dos mapas de codebase', createdBy: 'scan', group: 'discovery' },
58
+ { path: 'RESUME.md', kind: 'file', purpose: 'Resumo de retomada gerado pelo compact', createdBy: 'scan', group: 'discovery' },
59
+
60
+ // ── spec ────────────────────────────────────────────────────────────────
61
+ { path: 'SPEC.md', kind: 'file', purpose: 'Especificação da feature com critérios de aceite', createdBy: 'spec', group: 'spec' },
62
+ { path: 'ROADMAP.md', kind: 'file', purpose: 'Roteiro de requisitos (R-ID v1/v2/fora)', createdBy: 'spec', group: 'spec' },
63
+ { path: 'DISCUSS.md', kind: 'file', purpose: 'Perguntas e decisões de esclarecimento antes do plano', createdBy: 'spec', group: 'spec' },
64
+ { path: 'NOTES.md', kind: 'file', purpose: 'Notas não resolvidas da discussão', createdBy: 'spec', group: 'spec' },
65
+ { path: 'RESEARCH.md', kind: 'file', purpose: 'Índice de pesquisa exploratória', createdBy: 'spec', group: 'spec' },
66
+ { path: 'research/', kind: 'dir', purpose: 'Dossiês de pesquisa exploratória', createdBy: 'spec', group: 'spec' },
67
+ { path: 'INVESTIGATIONS.md', kind: 'file', purpose: 'Índice de investigações estruturadas', createdBy: 'spec', group: 'spec' },
68
+ { path: 'investigations/', kind: 'dir', purpose: 'Investigações estruturadas (inclui visual/VISUAL-INPUTS)', createdBy: 'spec', group: 'spec' },
69
+ { path: 'UI-SPEC.md', kind: 'file', purpose: 'Contrato de UI derivado da SPEC', createdBy: 'spec', group: 'spec' },
70
+ { path: 'SECURITY.md', kind: 'file', purpose: 'Auditoria de segurança OWASP (P0/P1/P2)', createdBy: 'verify', group: 'spec' },
71
+
72
+ // ── plan ────────────────────────────────────────────────────────────────
73
+ { path: 'PLAN.md', kind: 'file', purpose: 'Plano por ondas com verificação por tarefa', createdBy: 'plan', group: 'plan' },
74
+ { path: 'plan-agents.json', kind: 'file', purpose: 'Blueprint multi-agente (schema 3) — Plan-Driven Dynamic Agents', createdBy: 'plan', group: 'plan' },
75
+ { path: 'plan-agent-messages/', kind: 'dir', purpose: 'Protocolo de mensagens agente→agente', createdBy: 'plan', group: 'plan' },
76
+ { path: 'IMPLEMENTATION-PACK.json', kind: 'file', purpose: 'Pack racional: paths exatos, símbolos, contratos, write-set', createdBy: 'plan', group: 'plan' },
77
+ { path: 'REFERENCE-ANCHORS.md', kind: 'file', purpose: 'Âncoras de referência resolvidas para o plano', createdBy: 'plan', group: 'plan' },
78
+ { path: 'FIXTURE-PACK.json', kind: 'file', purpose: 'Fixtures obrigatórias por tipo de tarefa', createdBy: 'plan', group: 'plan' },
79
+ { path: 'QUICK.md', kind: 'file', purpose: 'Passos do modo rápido (sem cerimônia)', createdBy: 'plan', group: 'plan' },
80
+ { path: 'quick-agents.json', kind: 'file', purpose: 'Agentes lean (PDDA) do modo quick', createdBy: 'plan', group: 'plan' },
81
+ { path: 'PLAN-REVIEW.md', kind: 'file', purpose: 'Revisão de plano para aprovação (dashboard)', createdBy: 'plan', group: 'plan' },
82
+
83
+ // ── execute (runtime operacional) ─────────────────────────────────────────
84
+ { path: 'EXECUTION-RUNTIME.md', kind: 'file', purpose: 'Runtime tático da onda: agentes ativos, handoffs, retries', createdBy: 'execute', group: 'execute' },
85
+ { path: 'ACTIVE-RUN.json', kind: 'file', purpose: 'Cursor e estado do run atual', createdBy: 'runtime', group: 'execute' },
86
+ { path: 'OXE-EVENTS.ndjson', kind: 'file', purpose: 'Trace append-only de eventos do run', createdBy: 'runtime', group: 'execute' },
87
+ { path: 'runs/', kind: 'dir', purpose: 'Artefatos por run do runtime enterprise (manifest, evidence, risk)', createdBy: 'runtime', group: 'execute' },
88
+ { path: 'CHECKPOINTS.md', kind: 'file', purpose: 'Gates humanos de aprovação', createdBy: 'execute', group: 'execute' },
89
+ { path: 'OBSERVATIONS.md', kind: 'file', purpose: 'Observações contextuais pendentes de incorporação', createdBy: 'execute', group: 'execute' },
90
+ { path: 'DEBUG.md', kind: 'file', purpose: 'Diagnóstico técnico durante execução', createdBy: 'execute', group: 'execute' },
91
+ { path: 'context/', kind: 'dir', purpose: 'Context packs e summaries por workflow', createdBy: 'execute', group: 'execute' },
92
+
93
+ // ── verify ──────────────────────────────────────────────────────────────
94
+ { path: 'VERIFY.md', kind: 'file', purpose: 'Validação dos critérios da SPEC', createdBy: 'verify', group: 'verify' },
95
+ { path: 'SUMMARY.md', kind: 'file', purpose: 'Sumário de entrega', createdBy: 'verify', group: 'verify' },
96
+ { path: 'VALIDATION-GAPS.md', kind: 'file', purpose: 'Gaps de cobertura pós-verify', createdBy: 'verify', group: 'verify' },
97
+ { path: 'UI-REVIEW.md', kind: 'file', purpose: 'Auditoria de UI face ao UI-SPEC', createdBy: 'verify', group: 'verify' },
98
+
99
+ // ── autonomous (conduct / agent / swarm) ──────────────────────────────────
100
+ { path: 'agent/', kind: 'dir', purpose: 'Artefatos de Agent Mode (AGENT-SESSION, RECONCILIATION, SKILLS-LOADED)', createdBy: 'conduct', group: 'autonomous' },
101
+ { path: 'swarm/', kind: 'dir', purpose: 'Artefatos de Swarm Mode (SWARM-RUN, TASK-GRAPH, FILE-OWNERSHIP, BOARD, reviews)', createdBy: 'conduct', group: 'autonomous' },
102
+
103
+ // ── memory & learning ─────────────────────────────────────────────────────
104
+ { path: 'memory/', kind: 'dir', purpose: 'Memory Kernel: REPO-MEMORY + índice + sidecars injetados', createdBy: 'memory', group: 'memory' },
105
+ { path: 'learning/', kind: 'dir', purpose: 'Learning Kernel: candidates, promotion queue, learning events', createdBy: 'distill', group: 'memory' },
106
+ { path: 'global/', kind: 'dir', purpose: 'Cross-session: LESSONS.md, MILESTONES.md, milestones/', createdBy: 'verify', group: 'memory' },
107
+
108
+ // ── project mgmt ──────────────────────────────────────────────────────────
109
+ { path: 'sessions/', kind: 'dir', purpose: 'Sessões isoladas (sNNN-slug com spec/plan/execution/verification)', createdBy: 'session', group: 'project' },
110
+ { path: 'SESSIONS.md', kind: 'file', purpose: 'Índice de sessões', createdBy: 'session', group: 'project' },
111
+ { path: 'workstreams/', kind: 'dir', purpose: 'Trilhas paralelas com artefatos independentes', createdBy: 'workstream', group: 'project' },
112
+
113
+ // ── platform ──────────────────────────────────────────────────────────────
114
+ { path: 'CAPABILITIES.md', kind: 'file', purpose: 'Índice de capabilities nativas do projeto', createdBy: 'capabilities', group: 'platform' },
115
+ { path: 'capabilities/', kind: 'dir', purpose: 'Manifestos de capabilities', createdBy: 'capabilities', group: 'platform' },
116
+ { path: 'plugins/', kind: 'dir', purpose: 'Plugins opcionais do runtime', createdBy: 'plugins', group: 'platform' },
117
+ { path: 'cloud/', kind: 'dir', purpose: 'Provider Azure local-first (inventário e operações)', createdBy: 'azure', group: 'platform' },
118
+ { path: 'dashboard/', kind: 'dir', purpose: 'Estado do dashboard web (opt-in)', createdBy: 'execute', group: 'platform' },
119
+ { path: 'install/', kind: 'dir', purpose: 'Auditoria de instalação (runtime-semantics, manifests de IDE)', createdBy: 'install', group: 'platform' },
120
+
121
+ // ── layout (nested install copia a árvore canônica para `.oxe/`) ───────────
122
+ { path: 'workflows/', kind: 'dir', purpose: 'Workflows canônicos OXE (fonte da verdade dos passos)', createdBy: 'install', group: 'layout' },
123
+ { path: 'templates/', kind: 'dir', purpose: 'Templates de artefatos usados pelos workflows', createdBy: 'install', group: 'layout' },
124
+ { path: 'personas/', kind: 'dir', purpose: 'Personas builtin dos agentes', createdBy: 'install', group: 'layout' },
125
+ { path: 'agents/', kind: 'dir', purpose: 'Definições de agentes especializados', createdBy: 'install', group: 'layout' },
126
+ { path: 'schemas/', kind: 'dir', purpose: 'JSON schemas (plan-agents, swarm-run)', createdBy: 'install', group: 'layout' },
127
+ ];
128
+
129
+ /**
130
+ * Friendly "source" label per `createdBy` value. Not every artifact comes from
131
+ * a `/oxe-<x>` slash command — some are produced by CLI subcommands or kernels.
132
+ * @type {Record<string,string>}
133
+ */
134
+ const SOURCE_LABELS = {
135
+ install: 'install',
136
+ scan: '/oxe-scan',
137
+ spec: '/oxe-spec',
138
+ plan: '/oxe-plan',
139
+ execute: '/oxe-execute',
140
+ verify: '/oxe-verify',
141
+ runtime: 'oxe-cc runtime',
142
+ conduct: '/oxe (autônomo)',
143
+ session: '/oxe-session',
144
+ workstream: '/oxe-workstream',
145
+ milestone: '/oxe-milestone',
146
+ capabilities: 'oxe-cc capabilities',
147
+ plugins: 'oxe-cc plugins',
148
+ azure: 'oxe-cc azure',
149
+ memory: 'Memory Kernel',
150
+ distill: 'Learning Kernel',
151
+ };
152
+
153
+ function sourceLabel(createdBy) {
154
+ return SOURCE_LABELS[createdBy] || createdBy;
155
+ }
156
+
157
+ const CORE_INSTALL_PATHS = ARTIFACT_CATALOG.filter((e) => e.createdBy === 'install' && e.group === 'core').map((e) => e.path);
158
+
159
+ function groupLabel(key) {
160
+ const g = GROUPS.find((x) => x.key === key);
161
+ return g ? g.label : key;
162
+ }
163
+
164
+ /**
165
+ * Render the `.oxe/README.md` legend from the catalog. Pure string output so it
166
+ * is trivially testable and identical across install/doctor regeneration.
167
+ * @returns {string}
168
+ */
169
+ function renderLegend() {
170
+ const lines = [];
171
+ lines.push('# OXE — mapa de artefatos do `.oxe/`');
172
+ lines.push('');
173
+ lines.push('> Este diretório é **enxuto no install** — só `STATE.md`, `config.json` e este `README.md`.');
174
+ lines.push('> Todo o resto **nasce sob demanda**, criado pelo workflow correspondente quando você o usa.');
175
+ lines.push('> Rode **`oxe-cc map`** para ver, a qualquer momento, o que já existe no seu projeto e o que está disponível sob demanda.');
176
+ lines.push('');
177
+ lines.push('Legenda de estado no `oxe-cc map`: `✓` ativo (com conteúdo) · `◦` vazio/placeholder · `·` disponível sob demanda (ainda não criado) · `⚠` desatualizado.');
178
+ lines.push('');
179
+ for (const g of GROUPS) {
180
+ const entries = ARTIFACT_CATALOG.filter((e) => e.group === g.key);
181
+ if (!entries.length) continue;
182
+ lines.push(`## ${g.label}`);
183
+ lines.push('');
184
+ lines.push('| Artefato | O que é | Criado por |');
185
+ lines.push('|----------|---------|------------|');
186
+ for (const e of entries) {
187
+ const name = `\`${e.path}\``;
188
+ const by = e.createdBy === 'install' ? 'install' : `\`${sourceLabel(e.createdBy)}\``;
189
+ lines.push(`| ${name} | ${e.purpose} | ${by} |`);
190
+ }
191
+ lines.push('');
192
+ }
193
+ lines.push('---');
194
+ lines.push('_Gerado pelo OXE a partir do catálogo único de artefatos. Re-renderizado por `oxe-cc doctor`._');
195
+ lines.push('');
196
+ return lines.join('\n');
197
+ }
198
+
199
+ /**
200
+ * Classify the on-disk state of a single catalog entry.
201
+ * @param {string} oxeDir absolute path to the project's `.oxe/` directory
202
+ * @param {{path:string,kind:string}} entry
203
+ * @returns {'absent'|'empty'|'active'}
204
+ */
205
+ function classifyEntry(oxeDir, entry) {
206
+ const abs = path.join(oxeDir, entry.path.replace(/\/$/, ''));
207
+ let stat;
208
+ try {
209
+ stat = fs.statSync(abs);
210
+ } catch {
211
+ return 'absent';
212
+ }
213
+ if (entry.kind === 'dir') {
214
+ if (!stat.isDirectory()) return 'active';
215
+ let children;
216
+ try {
217
+ children = fs.readdirSync(abs);
218
+ } catch {
219
+ return 'empty';
220
+ }
221
+ const meaningful = children.filter((c) => c !== '.gitkeep' && c !== 'README.md');
222
+ return meaningful.length > 0 ? 'active' : 'empty';
223
+ }
224
+ // file
225
+ if (stat.isDirectory()) return 'active';
226
+ return stat.size > 0 ? 'active' : 'empty';
227
+ }
228
+
229
+ /**
230
+ * Build the live map model by cross-referencing the catalog with the real
231
+ * `.oxe/` directory. Absence of a lazy artifact is a NORMAL state, never an
232
+ * error. Optionally flags the codebase scan as stale via injected helpers.
233
+ *
234
+ * @param {string} target absolute path to the project root (NOT `.oxe`)
235
+ * @param {{ staleScan?: boolean }} [opts]
236
+ * @returns {{ projectRoot:string, oxeExists:boolean, groups:Array, present:Array, available:Array, extras:string[], counts:object }}
237
+ */
238
+ function buildMapModel(target, opts = {}) {
239
+ const root = path.resolve(target);
240
+ const oxeDir = path.join(root, '.oxe');
241
+ const oxeExists = fs.existsSync(oxeDir);
242
+
243
+ const nodes = ARTIFACT_CATALOG.map((e) => {
244
+ const state = oxeExists ? classifyEntry(oxeDir, e) : 'absent';
245
+ let flagged = state;
246
+ if (state === 'active' && opts.staleScan && e.path === 'codebase/') flagged = 'stale';
247
+ return {
248
+ path: e.path,
249
+ kind: e.kind,
250
+ purpose: e.purpose,
251
+ createdBy: e.createdBy,
252
+ group: e.group,
253
+ state: flagged,
254
+ };
255
+ });
256
+
257
+ const present = nodes.filter((n) => n.state !== 'absent');
258
+ const available = nodes.filter((n) => n.state === 'absent');
259
+
260
+ // extras: top-level entries on disk not described by the catalog
261
+ const known = new Set(ARTIFACT_CATALOG.map((e) => e.path.replace(/\/$/, '')));
262
+ let extras = [];
263
+ if (oxeExists) {
264
+ try {
265
+ extras = fs
266
+ .readdirSync(oxeDir, { withFileTypes: true })
267
+ .map((d) => (d.isDirectory() ? `${d.name}/` : d.name))
268
+ .filter((name) => !known.has(name.replace(/\/$/, '')))
269
+ .sort();
270
+ } catch {
271
+ extras = [];
272
+ }
273
+ }
274
+
275
+ const byGroup = GROUPS.map((g) => ({
276
+ key: g.key,
277
+ label: g.label,
278
+ present: present.filter((n) => n.group === g.key),
279
+ available: available.filter((n) => n.group === g.key),
280
+ })).filter((g) => g.present.length || g.available.length);
281
+
282
+ return {
283
+ projectRoot: root,
284
+ oxeExists,
285
+ groups: byGroup,
286
+ present,
287
+ available,
288
+ extras,
289
+ counts: {
290
+ total: nodes.length,
291
+ present: present.length,
292
+ active: nodes.filter((n) => n.state === 'active').length,
293
+ empty: nodes.filter((n) => n.state === 'empty').length,
294
+ stale: nodes.filter((n) => n.state === 'stale').length,
295
+ available: available.length,
296
+ extras: extras.length,
297
+ },
298
+ };
299
+ }
300
+
301
+ const STATE_GLYPH = {
302
+ active: '✓',
303
+ empty: '◦',
304
+ stale: '⚠',
305
+ absent: '·',
306
+ };
307
+
308
+ /**
309
+ * Render the map model as an annotated terminal tree. Colorizing is left to the
310
+ * caller via the `paint` hook so this stays dependency-free and testable.
311
+ *
312
+ * @param {ReturnType<buildMapModel>} model
313
+ * @param {{ paint?: (s:string, kind:string)=>string }} [opts]
314
+ * @returns {string}
315
+ */
316
+ function renderMap(model, opts = {}) {
317
+ const paint = opts.paint || ((s) => s);
318
+ const lines = [];
319
+ lines.push(paint('.oxe/', 'root'));
320
+ if (!model.oxeExists) {
321
+ lines.push(' (não inicializado — rode `oxe-cc init-oxe`)');
322
+ return lines.join('\n');
323
+ }
324
+
325
+ for (const g of model.groups) {
326
+ if (!g.present.length) continue;
327
+ lines.push('');
328
+ lines.push(paint(` ${g.label}`, 'group'));
329
+ for (const n of g.present) {
330
+ const glyph = STATE_GLYPH[n.state] || '·';
331
+ const name = n.path.padEnd(26);
332
+ lines.push(` ${paint(glyph, n.state)} ${name} ${paint(n.purpose, 'dim')}`);
333
+ }
334
+ }
335
+
336
+ if (model.extras.length) {
337
+ lines.push('');
338
+ lines.push(paint(' Outros (fora do catálogo)', 'group'));
339
+ for (const name of model.extras) lines.push(` ${paint('?', 'dim')} ${name}`);
340
+ }
341
+
342
+ // Available-on-demand: compact, grouped — NOT shown as folders.
343
+ if (model.available.length) {
344
+ lines.push('');
345
+ lines.push(paint(' Disponível sob demanda (ainda não criado)', 'group'));
346
+ const byCmd = new Map();
347
+ for (const n of model.available) {
348
+ const key = sourceLabel(n.createdBy);
349
+ if (!byCmd.has(key)) byCmd.set(key, []);
350
+ byCmd.get(key).push(n.path);
351
+ }
352
+ for (const [cmd, paths] of byCmd) {
353
+ lines.push(` ${paint('·', 'absent')} ${paint(cmd, 'dim')} → ${paths.join(', ')}`);
354
+ }
355
+ }
356
+
357
+ lines.push('');
358
+ const c = model.counts;
359
+ lines.push(paint(` ${c.active} ativo · ${c.empty} vazio · ${c.stale} desatualizado · ${c.available} sob demanda`, 'dim'));
360
+ lines.push(paint(' Legenda: ✓ ativo · ◦ vazio · ⚠ desatualizado · · sob demanda', 'dim'));
361
+ return lines.join('\n');
362
+ }
363
+
364
+ module.exports = {
365
+ ARTIFACT_CATALOG,
366
+ GROUPS,
367
+ CORE_INSTALL_PATHS,
368
+ SOURCE_LABELS,
369
+ sourceLabel,
370
+ groupLabel,
371
+ renderLegend,
372
+ classifyEntry,
373
+ buildMapModel,
374
+ renderMap,
375
+ STATE_GLYPH,
376
+ };
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ const COMMAND_NAME = /^[a-z][a-z0-9-]*$/;
4
+
5
+ function createCommandRegistry(definitions = []) {
6
+ const commands = new Map();
7
+ for (const definition of definitions) {
8
+ if (!definition || !COMMAND_NAME.test(definition.name || '')) {
9
+ throw new TypeError('nome de comando inválido');
10
+ }
11
+ if (typeof definition.handler !== 'function') {
12
+ throw new TypeError(`handler obrigatório para ${definition.name}`);
13
+ }
14
+ if (commands.has(definition.name)) throw new Error(`comando duplicado: ${definition.name}`);
15
+ commands.set(definition.name, Object.freeze({ ...definition }));
16
+ }
17
+ return Object.freeze({
18
+ has(name) { return commands.has(name); },
19
+ names() { return [...commands.keys()]; },
20
+ get(name) { return commands.get(name) || null; },
21
+ async dispatch(name, argv = [], context = {}) {
22
+ const definition = commands.get(name);
23
+ if (!definition) return { handled: false, result: undefined };
24
+ if (!Array.isArray(argv)) throw new TypeError('argv deve ser um array');
25
+ const result = await definition.handler([...argv], context);
26
+ return { handled: true, result };
27
+ },
28
+ });
29
+ }
30
+
31
+ module.exports = { createCommandRegistry };
@@ -223,17 +223,17 @@ function resolveArtifactCandidates(projectRoot, activeSession) {
223
223
  session_manifest: withFallback('session_manifest', 'session', scoped.sessionManifest || null, null, 'session'),
224
224
  sessions_index: withFallback('sessions_index', 'session_index', base.sessionsIndex, null, 'project'),
225
225
  execution_state: withFallback('execution_state', 'state', scoped.executionState || null, null, 'session'),
226
- spec: withFallback('spec', 'spec', scoped.spec, base.spec),
227
- discuss: withFallback('discuss', 'discuss', scoped.discuss, base.discuss),
228
- plan: withFallback('plan', 'plan', scoped.plan, base.plan),
229
- implementation_pack_md: withFallback('implementation_pack_md', 'execution_contract', scoped.implementationPackMd, base.implementationPackMd),
230
- implementation_pack_json: withFallback('implementation_pack_json', 'execution_contract', scoped.implementationPackJson, base.implementationPackJson),
231
- reference_anchors: withFallback('reference_anchors', 'reference_anchors', scoped.referenceAnchors, base.referenceAnchors),
232
- fixture_pack_md: withFallback('fixture_pack_md', 'fixtures', scoped.fixturePackMd, base.fixturePackMd),
233
- fixture_pack_json: withFallback('fixture_pack_json', 'fixtures', scoped.fixturePackJson, base.fixturePackJson),
234
- visual_inputs_md: withFallback('visual_inputs_md', 'visual_inputs', scoped.visualInputsMd, base.visualInputsMd),
235
- visual_inputs_json: withFallback('visual_inputs_json', 'visual_inputs', scoped.visualInputsJson, base.visualInputsJson),
236
- plan_agents: withFallback('plan_agents', 'plan_agents', scoped.planAgents, base.planAgents),
226
+ spec: withFallback('spec', 'spec', scoped.spec, base.spec),
227
+ discuss: withFallback('discuss', 'discuss', scoped.discuss, base.discuss),
228
+ plan: withFallback('plan', 'plan', scoped.plan, base.plan),
229
+ implementation_pack_md: withFallback('implementation_pack_md', 'execution_contract', scoped.implementationPackMd, base.implementationPackMd),
230
+ implementation_pack_json: withFallback('implementation_pack_json', 'execution_contract', scoped.implementationPackJson, base.implementationPackJson),
231
+ reference_anchors: withFallback('reference_anchors', 'reference_anchors', scoped.referenceAnchors, base.referenceAnchors),
232
+ fixture_pack_md: withFallback('fixture_pack_md', 'fixtures', scoped.fixturePackMd, base.fixturePackMd),
233
+ fixture_pack_json: withFallback('fixture_pack_json', 'fixtures', scoped.fixturePackJson, base.fixturePackJson),
234
+ visual_inputs_md: withFallback('visual_inputs_md', 'visual_inputs', scoped.visualInputsMd, base.visualInputsMd),
235
+ visual_inputs_json: withFallback('visual_inputs_json', 'visual_inputs', scoped.visualInputsJson, base.visualInputsJson),
236
+ plan_agents: withFallback('plan_agents', 'plan_agents', scoped.planAgents, base.planAgents),
237
237
  quick: withFallback('quick', 'plan', scoped.quick, base.quick),
238
238
  runtime: withFallback('runtime', 'runtime', scoped.runtime, base.runtime),
239
239
  checkpoints: withFallback('checkpoints', 'checkpoints', scoped.checkpoints, base.checkpoints),
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ const { createCommandRegistry } = require('./oxe-command-registry.cjs');
4
+
5
+ /** Build the high-level CLI handlers from explicit parser/domain dependencies. */
6
+ function createCoreCommandRegistry(deps) {
7
+ const {
8
+ existsSync, printBanner, usage, log, error, exit, colors,
9
+ readPkgVersion, parseUninstallArgs, parseUpdateArgs, parseCapabilitiesArgs,
10
+ parseRuntimeArgs, parseAzureArgs, parseInstallArgs, runUninstall,
11
+ runUpdateVersionCheck, runUpdate, runCapabilities, runRuntime, runAzure,
12
+ runDoctor, runStatus, runStatusFull,
13
+ } = deps;
14
+
15
+ function showParseFailure(opts) {
16
+ if (opts.conflictFlags) {
17
+ printBanner(); error(`${colors.red}${opts.conflictFlags}${colors.reset}`); usage(); exit(1);
18
+ }
19
+ if (opts.parseError) {
20
+ printBanner(); error(`${colors.red}Opção desconhecida:${colors.reset} ${opts.unknownFlag}`); usage(); exit(1);
21
+ }
22
+ }
23
+ function showHelp(opts) {
24
+ if (opts.version) { log(`oxe-cc v${readPkgVersion()}`); exit(0); }
25
+ if (!opts.help) return;
26
+ printBanner(); usage(); exit(0);
27
+ }
28
+ function requireDirectory(dir, allowMissing) {
29
+ if (allowMissing || existsSync(dir)) return;
30
+ error(`${colors.yellow}Diretório não encontrado: ${dir}${colors.reset}`); exit(1);
31
+ }
32
+
33
+ return createCommandRegistry([
34
+ { name: 'uninstall', handler(argv) {
35
+ const opts = parseUninstallArgs(argv);
36
+ showHelp(opts); showParseFailure(opts); printBanner(); requireDirectory(opts.dir, opts.dryRun);
37
+ return runUninstall(opts);
38
+ } },
39
+ { name: 'update', handler(argv) {
40
+ const opts = parseUpdateArgs(argv);
41
+ showHelp(opts); showParseFailure(opts); printBanner();
42
+ if (opts.check) return runUpdateVersionCheck(opts);
43
+ requireDirectory(opts.dir, opts.dryRun);
44
+ return runUpdate(opts);
45
+ } },
46
+ { name: 'capabilities', handler(argv) {
47
+ const opts = parseCapabilitiesArgs(argv);
48
+ showHelp(opts); showParseFailure(opts); printBanner(); requireDirectory(opts.dir, false);
49
+ return runCapabilities(opts);
50
+ } },
51
+ { name: 'runtime', async handler(argv) {
52
+ const opts = parseRuntimeArgs(argv);
53
+ showHelp(opts); showParseFailure(opts); printBanner(); requireDirectory(opts.dir, false);
54
+ return runRuntime(opts);
55
+ } },
56
+ { name: 'azure', handler(argv) {
57
+ const opts = parseAzureArgs(argv);
58
+ showHelp(opts); showParseFailure(opts); printBanner();
59
+ return runAzure(opts);
60
+ } },
61
+ { name: 'doctor', handler(argv) {
62
+ const opts = parseInstallArgs(argv);
63
+ showHelp(opts); showParseFailure(opts);
64
+ if (!opts.jsonOutput) printBanner();
65
+ requireDirectory(opts.dir, false);
66
+ return runDoctor(opts.dir, {
67
+ release: opts.releaseDoctor, json: opts.jsonOutput, writeManifest: opts.writeManifest,
68
+ });
69
+ } },
70
+ { name: 'status', handler(argv) {
71
+ const opts = parseInstallArgs(argv);
72
+ showHelp(opts); showParseFailure(opts);
73
+ if (!opts.jsonOutput) printBanner();
74
+ requireDirectory(opts.dir, false);
75
+ return opts.statusFull
76
+ ? runStatusFull(opts.dir)
77
+ : runStatus(opts.dir, { json: opts.jsonOutput, hints: opts.statusHints, summary: opts.statusSummary });
78
+ } },
79
+ ]);
80
+ }
81
+
82
+ module.exports = { createCoreCommandRegistry };