oxe-cc 0.3.8 → 0.5.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 (78) hide show
  1. package/.cursor/commands/oxe-checkpoint.md +2 -0
  2. package/.cursor/commands/oxe-compact.md +2 -0
  3. package/.cursor/commands/oxe-debug.md +2 -0
  4. package/.cursor/commands/oxe-discuss.md +2 -0
  5. package/.cursor/commands/oxe-execute.md +3 -1
  6. package/.cursor/commands/oxe-forensics.md +2 -0
  7. package/.cursor/commands/oxe-help.md +2 -0
  8. package/.cursor/commands/oxe-milestone.md +11 -0
  9. package/.cursor/commands/oxe-next.md +2 -0
  10. package/.cursor/commands/oxe-obs.md +11 -0
  11. package/.cursor/commands/oxe-plan-agent.md +2 -0
  12. package/.cursor/commands/oxe-plan.md +2 -0
  13. package/.cursor/commands/oxe-quick.md +3 -1
  14. package/.cursor/commands/oxe-research.md +2 -0
  15. package/.cursor/commands/oxe-review-pr.md +2 -0
  16. package/.cursor/commands/oxe-route.md +2 -0
  17. package/.cursor/commands/oxe-scan.md +2 -0
  18. package/.cursor/commands/oxe-spec.md +3 -1
  19. package/.cursor/commands/oxe-ui-review.md +2 -0
  20. package/.cursor/commands/oxe-ui-spec.md +2 -0
  21. package/.cursor/commands/oxe-update.md +2 -0
  22. package/.cursor/commands/oxe-validate-gaps.md +2 -0
  23. package/.cursor/commands/oxe-verify.md +2 -0
  24. package/.cursor/commands/oxe-workstream.md +11 -0
  25. package/.github/prompts/oxe-execute.prompt.md +12 -12
  26. package/.github/prompts/oxe-milestone.prompt.md +12 -0
  27. package/.github/prompts/oxe-obs.prompt.md +12 -0
  28. package/.github/prompts/oxe-quick.prompt.md +12 -12
  29. package/.github/prompts/oxe-spec.prompt.md +2 -2
  30. package/.github/prompts/oxe-workstream.prompt.md +12 -0
  31. package/README.md +205 -442
  32. package/bin/banner.txt +1 -1
  33. package/bin/lib/oxe-plugins.cjs +226 -0
  34. package/bin/lib/oxe-project-health.cjs +97 -1
  35. package/bin/lib/oxe-security.cjs +225 -0
  36. package/bin/oxe-cc.js +29 -0
  37. package/commands/oxe/execute.md +16 -16
  38. package/commands/oxe/milestone.md +16 -0
  39. package/commands/oxe/obs.md +16 -0
  40. package/commands/oxe/quick.md +16 -16
  41. package/commands/oxe/spec.md +2 -2
  42. package/commands/oxe/workstream.md +16 -0
  43. package/lib/sdk/index.cjs +284 -0
  44. package/lib/sdk/index.d.ts +148 -1
  45. package/oxe/personas/README.md +39 -0
  46. package/oxe/personas/architect.md +37 -0
  47. package/oxe/personas/db-specialist.md +36 -0
  48. package/oxe/personas/debugger.md +38 -0
  49. package/oxe/personas/executor.md +38 -0
  50. package/oxe/personas/planner.md +36 -0
  51. package/oxe/personas/researcher.md +35 -0
  52. package/oxe/personas/ui-specialist.md +36 -0
  53. package/oxe/personas/verifier.md +39 -0
  54. package/oxe/templates/CONFIG.md +58 -4
  55. package/oxe/templates/DISCUSS.template.md +44 -0
  56. package/oxe/templates/MEMORY.template.md +49 -0
  57. package/oxe/templates/MILESTONES.template.md +17 -0
  58. package/oxe/templates/OBSERVATIONS.template.md +18 -0
  59. package/oxe/templates/PLUGINS.md +101 -0
  60. package/oxe/templates/ROADMAP.template.md +44 -0
  61. package/oxe/templates/STATE.md +29 -2
  62. package/oxe/templates/config.template.json +5 -0
  63. package/oxe/templates/plan-agent-messages-README.template.md +1 -1
  64. package/oxe/templates/quick-agents.template.json +24 -0
  65. package/oxe/workflows/discuss.md +48 -5
  66. package/oxe/workflows/execute.md +111 -28
  67. package/oxe/workflows/help.md +80 -15
  68. package/oxe/workflows/milestone.md +96 -0
  69. package/oxe/workflows/obs.md +95 -0
  70. package/oxe/workflows/plan-agent.md +31 -1
  71. package/oxe/workflows/plan.md +5 -1
  72. package/oxe/workflows/quick.md +120 -10
  73. package/oxe/workflows/references/plan-agent-chat-protocol.md +14 -0
  74. package/oxe/workflows/scan.md +9 -1
  75. package/oxe/workflows/spec.md +172 -23
  76. package/oxe/workflows/verify.md +77 -17
  77. package/oxe/workflows/workstream.md +96 -0
  78. package/package.json +3 -2
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: oxe:obs
3
+ description: "Observação contextual — registra em .oxe/OBSERVATIONS.md, incorporada automaticamente no próximo spec/plan/execute sem re-explicar"
4
+ argument-hint: "[observação: restrição, descoberta, preferência, risco ou decisão]"
5
+ allowed-tools:
6
+ - Read
7
+ - Bash
8
+ - Glob
9
+ - Grep
10
+ - Write
11
+ - Task
12
+ ---
13
+
14
+ **Workflow canónico:** `oxe/workflows/obs.md`
15
+
16
+ Execute integralmente esse ficheiro na raiz do repositório em que estás a trabalhar. Usa o texto em `$ARGUMENTS` como a observação a registrar.
@@ -1,16 +1,16 @@
1
- ---
2
- name: oxe:quick
3
- description: Modo rápido — .oxe/QUICK.md + STATE (sem SPEC/PLAN longos)
4
- argument-hint: "[objetivo]"
5
- allowed-tools:
6
- - Read
7
- - Bash
8
- - Glob
9
- - Grep
10
- - Write
11
- - Task
12
- ---
13
-
14
- **Workflow canónico:** `oxe/workflows/quick.md`
15
-
16
- Execute integralmente esse ficheiro na raiz do repositório em que estás a trabalhar. Usa o texto em `$ARGUMENTS` como objetivo e contexto.
1
+ ---
2
+ name: oxe:quick
3
+ description: "Modo rápido com Plan-Driven Dynamic Agents lean minispec (objetivo) + mini-plano (passos) + agentes dinâmicos por domínio (opcional) + verificar"
4
+ argument-hint: "[objetivo] [--agents]"
5
+ allowed-tools:
6
+ - Read
7
+ - Bash
8
+ - Glob
9
+ - Grep
10
+ - Write
11
+ - Task
12
+ ---
13
+
14
+ **Workflow canónico:** `oxe/workflows/quick.md`
15
+
16
+ Execute integralmente esse ficheiro na raiz do repositório em que estás a trabalhar. Usa o texto em `$ARGUMENTS` como objetivo e contexto.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: oxe:spec
3
- description: Transforma o pedido do usuário em SPEC.md (escopo, aceite, não-objetivos)
4
- argument-hint: "[texto do pedido ou @arquivo.md]"
3
+ description: "Spec em 5 fases: perguntas pesquisa → requisitos R-ID (v1/v2/fora) → roteiro ROADMAP.md aprovação → plan"
4
+ argument-hint: "[descrição da feature, ideia ou @arquivo.md com PRD]"
5
5
  allowed-tools:
6
6
  - Read
7
7
  - Write
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: oxe:workstream
3
+ description: "Trilhas paralelas — list, new <nome>, switch <nome>, status, close <nome> — artefatos independentes em .oxe/workstreams/<nome>/"
4
+ argument-hint: "list | new <nome> | switch <nome> | status | close <nome>"
5
+ allowed-tools:
6
+ - Read
7
+ - Bash
8
+ - Glob
9
+ - Grep
10
+ - Write
11
+ - Task
12
+ ---
13
+
14
+ **Workflow canónico:** `oxe/workflows/workstream.md`
15
+
16
+ Execute integralmente esse ficheiro na raiz do repositório em que estás a trabalhar. Usa o texto em `$ARGUMENTS` como subcomando e contexto.
package/lib/sdk/index.cjs CHANGED
@@ -12,6 +12,8 @@ const manifest = require('../../bin/lib/oxe-manifest.cjs');
12
12
  const workflows = require('../../bin/lib/oxe-workflows.cjs');
13
13
  const installResolve = require('../../bin/lib/oxe-install-resolve.cjs');
14
14
  const agentInstall = require('../../bin/lib/oxe-agent-install.cjs');
15
+ const security = require('../../bin/lib/oxe-security.cjs');
16
+ const plugins = require('../../bin/lib/oxe-plugins.cjs');
15
17
 
16
18
  const PACKAGE_ROOT = path.join(__dirname, '..', '..');
17
19
 
@@ -38,6 +40,224 @@ function readMinNode(packageRoot) {
38
40
  }
39
41
  }
40
42
 
43
+ /**
44
+ * Faz parse de um PLAN.md e extrai tarefas estruturadas.
45
+ *
46
+ * @param {string} planMd - Conteúdo do PLAN.md (string).
47
+ * @returns {{
48
+ * tasks: Array<{
49
+ * id: string,
50
+ * title: string,
51
+ * wave: number | null,
52
+ * dependsOn: string[],
53
+ * files: string[],
54
+ * verifyCommand: string | null,
55
+ * aceite: string[],
56
+ * decisions: string[],
57
+ * done: boolean,
58
+ * meta: Record<string, unknown> | null,
59
+ * }>,
60
+ * waves: Record<number, string[]>,
61
+ * totalTasks: number,
62
+ * }}
63
+ */
64
+ function parsePlan(planMd) {
65
+ const parts = planMd.split(/^###\s+(T\d+)\s*[—-]\s*/m);
66
+ /** @type {ReturnType<typeof parsePlan>['tasks']} */
67
+ const tasks = [];
68
+ /** @type {Record<number, string[]>} */
69
+ const waves = {};
70
+
71
+ for (let i = 1; i < parts.length; i += 2) {
72
+ const id = parts[i].trim();
73
+ const rest = (parts[i + 1] || '').split(/^###\s+T\d+/m)[0];
74
+ const titleMatch = rest.match(/^([^\n]+)/);
75
+ const title = titleMatch ? titleMatch[1].trim() : '';
76
+
77
+ const waveMatch = rest.match(/\*\*Onda:\*\*\s*(\d+)/i);
78
+ const wave = waveMatch ? parseInt(waveMatch[1], 10) : null;
79
+
80
+ const depsMatch = rest.match(/\*\*Depende\s+de:\*\*\s*([^\n]+)/i);
81
+ const dependsOn = depsMatch
82
+ ? depsMatch[1].split(/[,\s]+/).filter((s) => /^T\d+$/.test(s.trim()))
83
+ : [];
84
+
85
+ const filesMatch = rest.match(/\*\*Arquivos\s+prováveis:\*\*\s*([^\n]+)/i);
86
+ const files = filesMatch
87
+ ? filesMatch[1].match(/`([^`]+)`/g)?.map((s) => s.replace(/`/g, '')) || []
88
+ : [];
89
+
90
+ const verifyCmdMatch = rest.match(/Comando:\s*`([^`]+)`/i);
91
+ const verifyCommand = verifyCmdMatch ? verifyCmdMatch[1] : null;
92
+
93
+ const aceiteMatch = rest.match(/\*\*Aceite\s+vinculado:\*\*\s*([^\n]+)/i);
94
+ const aceite = aceiteMatch
95
+ ? aceiteMatch[1].match(/A\d+/g) || []
96
+ : [];
97
+
98
+ const decisionsMatch = rest.match(/\*\*Decisão\s+vinculada:\*\*\s*([^\n]+)/i);
99
+ const decisions = decisionsMatch
100
+ ? decisionsMatch[1].match(/D-\d+/g) || []
101
+ : [];
102
+
103
+ // Parse do metadado JSON em comentário HTML <!-- oxe-task: {...} -->
104
+ const metaMatch = rest.match(/<!--\s*oxe-task:\s*(\{[\s\S]*?\})\s*-->/);
105
+ let meta = null;
106
+ if (metaMatch) {
107
+ try {
108
+ meta = JSON.parse(metaMatch[1]);
109
+ } catch {
110
+ meta = null;
111
+ }
112
+ }
113
+
114
+ const done = meta?.done === true;
115
+
116
+ const task = { id, title, wave, dependsOn, files, verifyCommand, aceite, decisions, done, meta };
117
+ tasks.push(task);
118
+
119
+ if (wave != null) {
120
+ if (!waves[wave]) waves[wave] = [];
121
+ waves[wave].push(id);
122
+ }
123
+ }
124
+
125
+ return { tasks, waves, totalTasks: tasks.length };
126
+ }
127
+
128
+ /**
129
+ * Faz parse de um SPEC.md e extrai critérios de aceite.
130
+ *
131
+ * @param {string} specMd - Conteúdo do SPEC.md.
132
+ * @returns {{
133
+ * objective: string | null,
134
+ * criteria: Array<{ id: string, criterion: string, howToVerify: string }>,
135
+ * requiredSections: string[],
136
+ * }}
137
+ */
138
+ function parseSpec(specMd) {
139
+ // Objetivo
140
+ const objMatch = specMd.match(/##\s*Objetivo\s*\n+([\s\S]*?)(?=\n##\s|\n#[^\#]|$)/im);
141
+ const objective = objMatch ? objMatch[1].trim().split('\n')[0].trim() : null;
142
+
143
+ // Tabela de critérios
144
+ /** @type {Array<{ id: string, criterion: string, howToVerify: string }>} */
145
+ const criteria = [];
146
+ const tableMatch = specMd.match(/##\s*Critérios.*?aceite[\s\S]*?(\|[\s\S]*?)(?=\n##\s|\n#[^\#]|$)/im);
147
+ if (tableMatch) {
148
+ const rows = tableMatch[1].split('\n').filter((l) => l.startsWith('|'));
149
+ for (const row of rows) {
150
+ const cells = row.split('|').map((c) => c.trim()).filter(Boolean);
151
+ if (cells.length >= 2 && /^A\d+$/i.test(cells[0])) {
152
+ criteria.push({
153
+ id: cells[0],
154
+ criterion: cells[1] || '',
155
+ howToVerify: cells[2] || '',
156
+ });
157
+ }
158
+ }
159
+ }
160
+
161
+ // Seções presentes (para validação spec_required_sections)
162
+ const headings = [];
163
+ for (const m of specMd.matchAll(/^##\s+(.+)/gm)) {
164
+ headings.push(`## ${m[1].trim()}`);
165
+ }
166
+
167
+ return { objective, criteria, requiredSections: headings };
168
+ }
169
+
170
+ /**
171
+ * Faz parse do STATE.md e extrai fase, data do scan e próximo passo.
172
+ *
173
+ * @param {string} stateMd - Conteúdo do STATE.md.
174
+ * @returns {{
175
+ * phase: string | null,
176
+ * lastScanDate: string | null,
177
+ * nextStep: string | null,
178
+ * decisions: string[],
179
+ * activeWorkstreams: string[],
180
+ * activeMilestone: string | null,
181
+ * }}
182
+ */
183
+ function parseState(stateMd) {
184
+ const phase = health.parseStatePhase(stateMd);
185
+
186
+ const scanDate = health.parseLastScanDate(stateMd);
187
+ const lastScanDate = scanDate ? scanDate.toISOString().split('T')[0] : null;
188
+
189
+ const nextStepMatch = stateMd.match(/##\s*Próximo passo sugerido\s*\n+([\s\S]*?)(?=\n##\s|\n#[^\#]|$)/im);
190
+ const nextStep = nextStepMatch ? nextStepMatch[1].trim().split('\n')[0].replace(/^[-*]\s*/, '').trim() : null;
191
+
192
+ // Decisões persistentes (seção opcional)
193
+ const decisionsMatch = stateMd.match(/##\s*Decisões persistentes\s*\n+([\s\S]*?)(?=\n##\s|\n#[^\#]|$)/im);
194
+ const decisions = decisionsMatch
195
+ ? decisionsMatch[1].match(/D-\d+[^)]*\)/g) || decisionsMatch[1].match(/D-\d+[^\n]*/g) || []
196
+ : [];
197
+
198
+ // Workstreams ativos (seção opcional)
199
+ const wsMatch = stateMd.match(/##\s*Workstreams ativos\s*\n+([\s\S]*?)(?=\n##\s|\n#[^\#]|$)/im);
200
+ const activeWorkstreams = wsMatch
201
+ ? wsMatch[1].match(/`([^`]+)`/g)?.map((s) => s.replace(/`/g, '')) || []
202
+ : [];
203
+
204
+ // Milestone ativo
205
+ const msMatch = stateMd.match(/##\s*Milestone ativo\s*\n+[^`]*`([^`]+)`/im);
206
+ const activeMilestone = msMatch ? msMatch[1] : null;
207
+
208
+ return { phase, lastScanDate, nextStep, decisions, activeWorkstreams, activeMilestone };
209
+ }
210
+
211
+ /**
212
+ * Valida fidelidade entre decisões do DISCUSS.md e tarefas do PLAN.md.
213
+ * Retorna gaps onde decisões D-NN não têm tarefa vinculada.
214
+ *
215
+ * @param {string} discussMd - Conteúdo do DISCUSS.md.
216
+ * @param {string} planMd - Conteúdo do PLAN.md.
217
+ * @returns {{
218
+ * ok: boolean,
219
+ * gaps: Array<{ decisionId: string, decision: string }>,
220
+ * covered: Array<{ decisionId: string, taskIds: string[] }>,
221
+ * }}
222
+ */
223
+ function validateDecisionFidelity(discussMd, planMd) {
224
+ // Extrair decisões da tabela D-NN
225
+ const decisionRows = [];
226
+ const tableMatch = discussMd.match(/##\s*Decisões[\s\S]*?(\|[\s\S]*?)(?=\n##\s|\n#[^\#]|$)/im);
227
+ if (tableMatch) {
228
+ const rows = tableMatch[1].split('\n').filter((l) => l.startsWith('|'));
229
+ for (const row of rows) {
230
+ const cells = row.split('|').map((c) => c.trim()).filter(Boolean);
231
+ if (cells.length >= 2 && /^D-\d+$/i.test(cells[0])) {
232
+ decisionRows.push({ id: cells[0], text: cells[1] || '' });
233
+ }
234
+ }
235
+ }
236
+
237
+ if (!decisionRows.length) return { ok: true, gaps: [], covered: [] };
238
+
239
+ const { tasks } = parsePlan(planMd);
240
+
241
+ /** @type {ReturnType<typeof validateDecisionFidelity>['gaps']} */
242
+ const gaps = [];
243
+ /** @type {ReturnType<typeof validateDecisionFidelity>['covered']} */
244
+ const covered = [];
245
+
246
+ for (const dec of decisionRows) {
247
+ // Ignorar decisões revertidas
248
+ if (/revertida/i.test(dec.text)) continue;
249
+
250
+ const tasksCovering = tasks.filter((t) => t.decisions.includes(dec.id));
251
+ if (tasksCovering.length === 0) {
252
+ gaps.push({ decisionId: dec.id, decision: dec.text });
253
+ } else {
254
+ covered.push({ decisionId: dec.id, taskIds: tasksCovering.map((t) => t.id) });
255
+ }
256
+ }
257
+
258
+ return { ok: gaps.length === 0, gaps, covered };
259
+ }
260
+
41
261
  /**
42
262
  * Verificações alinhadas ao `oxe-cc doctor`, com resultado estruturado (CI / gateways).
43
263
  *
@@ -47,6 +267,7 @@ function readMinNode(packageRoot) {
47
267
  * nodeMajor?: number,
48
268
  * includeWorkflowLint?: boolean,
49
269
  * workflowLintOptions?: { maxBytesSoft?: number },
270
+ * includeSecurity?: boolean,
50
271
  * }} args
51
272
  * @returns {{
52
273
  * ok: boolean,
@@ -60,6 +281,7 @@ function readMinNode(packageRoot) {
60
281
  * validation: ReturnType<typeof health.validateConfigShape>,
61
282
  * healthReport: ReturnType<typeof health.buildHealthReport>,
62
283
  * workflowShape: ReturnType<typeof workflows.validateWorkflowShapes> | null,
284
+ * securityReport: { secretFiles: string[], pluginsValid: boolean } | null,
63
285
  * }}
64
286
  */
65
287
  function runDoctorChecks(args) {
@@ -157,6 +379,39 @@ function runDoctorChecks(args) {
157
379
  }
158
380
  }
159
381
 
382
+ // Verificações de segurança (opcional, ativado por padrão)
383
+ let securityReport = null;
384
+ if (args.includeSecurity !== false) {
385
+ const secretFiles = security.scanDirForSecretFiles(projectRoot, { maxDepth: 3 });
386
+ if (secretFiles.length > 0) {
387
+ warnings.push({
388
+ code: 'SECURITY_SECRET_FILES',
389
+ message: `Arquivos com nomes sensíveis detectados: ${secretFiles.join(', ')}`,
390
+ detail: { files: secretFiles },
391
+ });
392
+ }
393
+
394
+ // Verificar plugins se existirem
395
+ const pluginsDir = path.join(projectRoot, '.oxe', 'plugins');
396
+ let pluginsValid = true;
397
+ if (fs.existsSync(pluginsDir)) {
398
+ const pluginFiles = fs.readdirSync(pluginsDir).filter((f) => f.endsWith('.cjs'));
399
+ for (const pf of pluginFiles) {
400
+ const pluginPath = path.join(pluginsDir, pf);
401
+ const safetyCheck = security.checkPathSafety(pluginPath, projectRoot);
402
+ if (!safetyCheck.safe) {
403
+ pluginsValid = false;
404
+ warnings.push({
405
+ code: 'SECURITY_PLUGIN_PATH',
406
+ message: `Plugin com caminho inseguro: ${pf} — ${safetyCheck.reason}`,
407
+ });
408
+ }
409
+ }
410
+ }
411
+
412
+ securityReport = { secretFiles, pluginsValid };
413
+ }
414
+
160
415
  return {
161
416
  ok: errors.length === 0,
162
417
  errors,
@@ -169,6 +424,7 @@ function runDoctorChecks(args) {
169
424
  validation,
170
425
  healthReport,
171
426
  workflowShape,
427
+ securityReport,
172
428
  };
173
429
  }
174
430
 
@@ -184,6 +440,12 @@ module.exports = {
184
440
  readPackageMeta,
185
441
  readMinNode,
186
442
 
443
+ /** Parsing de artefatos OXE (PLAN, SPEC, STATE). */
444
+ parsePlan,
445
+ parseSpec,
446
+ parseState,
447
+ validateDecisionFidelity,
448
+
187
449
  /** Estado do projeto, SPEC/PLAN, fase, config. */
188
450
  health: {
189
451
  loadOxeConfigMerged: health.loadOxeConfigMerged,
@@ -200,7 +462,10 @@ module.exports = {
200
462
  planWaveWarningsFixed: health.planWaveWarningsFixed,
201
463
  planTaskAceiteWarnings: health.planTaskAceiteWarnings,
202
464
  verifyGapsWithoutSummaryWarning: health.verifyGapsWithoutSummaryWarning,
465
+ expandExecutionProfile: health.expandExecutionProfile,
203
466
  ALLOWED_CONFIG_KEYS: health.ALLOWED_CONFIG_KEYS,
467
+ EXECUTION_PROFILES: health.EXECUTION_PROFILES,
468
+ VERIFICATION_DEPTHS: health.VERIFICATION_DEPTHS,
204
469
  INSTALL_PROFILES: health.INSTALL_PROFILES,
205
470
  INSTALL_REPO_LAYOUTS: health.INSTALL_REPO_LAYOUTS,
206
471
  INSTALL_OBJECT_KEYS: health.INSTALL_OBJECT_KEYS,
@@ -238,6 +503,25 @@ module.exports = {
238
503
  parseCursorCommandFrontmatter: agentInstall.parseCursorCommandFrontmatter,
239
504
  },
240
505
 
506
+ /** Segurança: validação de caminhos e detecção de segredos. */
507
+ security: {
508
+ checkPathSafety: security.checkPathSafety,
509
+ scanFileForSecrets: security.scanFileForSecrets,
510
+ scanDirForSecretFiles: security.scanDirForSecretFiles,
511
+ validatePlanPaths: security.validatePlanPaths,
512
+ DEFAULT_SECRET_PATTERNS: security.DEFAULT_SECRET_PATTERNS,
513
+ DEFAULT_SECRET_CONTENT_PATTERNS: security.DEFAULT_SECRET_CONTENT_PATTERNS,
514
+ DEFAULT_DENIED_PATH_PATTERNS: security.DEFAULT_DENIED_PATH_PATTERNS,
515
+ },
516
+
517
+ /** Plugin system — hooks de ciclo de vida em `.oxe/plugins/*.cjs`. */
518
+ plugins: {
519
+ loadPlugins: plugins.loadPlugins,
520
+ runHook: plugins.runHook,
521
+ validatePlugins: plugins.validatePlugins,
522
+ initPluginsDir: plugins.initPluginsDir,
523
+ },
524
+
241
525
  /** Um único objeto com verificações tipo `doctor` + relatório de saúde. */
242
526
  runDoctorChecks,
243
527
  };
@@ -65,6 +65,11 @@ export interface OxeHealthReport {
65
65
  scanIgnoreGlobs?: unknown;
66
66
  }
67
67
 
68
+ export interface SecurityReport {
69
+ secretFiles: string[];
70
+ pluginsValid: boolean;
71
+ }
72
+
68
73
  export interface DoctorChecksResult {
69
74
  ok: boolean;
70
75
  errors: DoctorIssue[];
@@ -81,6 +86,91 @@ export interface DoctorChecksResult {
81
86
  validation: { unknownKeys: string[]; typeErrors: string[] };
82
87
  healthReport: OxeHealthReport;
83
88
  workflowShape: WorkflowShapeResult | null;
89
+ securityReport: SecurityReport | null;
90
+ }
91
+
92
+ /** Tarefa parseada de PLAN.md. */
93
+ export interface ParsedTask {
94
+ id: string;
95
+ title: string;
96
+ wave: number | null;
97
+ dependsOn: string[];
98
+ files: string[];
99
+ verifyCommand: string | null;
100
+ aceite: string[];
101
+ decisions: string[];
102
+ done: boolean;
103
+ meta: Record<string, unknown> | null;
104
+ }
105
+
106
+ export interface ParsedPlan {
107
+ tasks: ParsedTask[];
108
+ waves: Record<number, string[]>;
109
+ totalTasks: number;
110
+ }
111
+
112
+ export interface ParsedCriterion {
113
+ id: string;
114
+ criterion: string;
115
+ howToVerify: string;
116
+ }
117
+
118
+ export interface ParsedSpec {
119
+ objective: string | null;
120
+ criteria: ParsedCriterion[];
121
+ requiredSections: string[];
122
+ }
123
+
124
+ export interface ParsedState {
125
+ phase: string | null;
126
+ lastScanDate: string | null;
127
+ nextStep: string | null;
128
+ decisions: string[];
129
+ activeWorkstreams: string[];
130
+ activeMilestone: string | null;
131
+ }
132
+
133
+ export interface DecisionFidelityResult {
134
+ ok: boolean;
135
+ gaps: Array<{ decisionId: string; decision: string }>;
136
+ covered: Array<{ decisionId: string; taskIds: string[] }>;
137
+ }
138
+
139
+ export interface PathSafetyResult {
140
+ safe: boolean;
141
+ reason: string | null;
142
+ }
143
+
144
+ export interface SecretMatch {
145
+ line: number;
146
+ pattern: string;
147
+ preview: string;
148
+ }
149
+
150
+ export interface SecretScanResult {
151
+ hasSecrets: boolean;
152
+ matches: SecretMatch[];
153
+ }
154
+
155
+ export interface PlanPathsResult {
156
+ ok: boolean;
157
+ issues: Array<{ path: string; reason: string }>;
158
+ }
159
+
160
+ export interface OxePlugin {
161
+ name: string;
162
+ version?: string;
163
+ hooks: Record<string, (ctx: Record<string, unknown>) => Promise<void> | void>;
164
+ }
165
+
166
+ export interface PluginLoadResult {
167
+ plugins: OxePlugin[];
168
+ errors: Array<{ file: string; error: string }>;
169
+ }
170
+
171
+ export interface PluginValidationResult {
172
+ valid: boolean;
173
+ issues: Array<{ file: string; issue: string }>;
84
174
  }
85
175
 
86
176
  export interface OxeSdk {
@@ -89,7 +179,38 @@ export interface OxeSdk {
89
179
  PACKAGE_ROOT: string;
90
180
  readPackageMeta: (root?: string) => PackageMeta;
91
181
  readMinNode: (packageRoot: string) => number;
92
- health: Record<string, unknown>;
182
+
183
+ /** Parsing de artefatos OXE. */
184
+ parsePlan: (planMd: string) => ParsedPlan;
185
+ parseSpec: (specMd: string) => ParsedSpec;
186
+ parseState: (stateMd: string) => ParsedState;
187
+ validateDecisionFidelity: (discussMd: string, planMd: string) => DecisionFidelityResult;
188
+
189
+ health: {
190
+ loadOxeConfigMerged: (targetProject: string) => { config: Record<string, unknown>; path: string | null; parseError: string | null };
191
+ validateConfigShape: (cfg: Record<string, unknown>) => { unknownKeys: string[]; typeErrors: string[] };
192
+ buildHealthReport: (target: string) => OxeHealthReport;
193
+ suggestNextStep: (target: string, cfg?: { discuss_before_plan?: boolean }) => OxeNextSuggestion;
194
+ oxePaths: (target: string) => Record<string, string>;
195
+ parseStatePhase: (stateText: string) => string | null;
196
+ parseLastScanDate: (stateText: string) => Date | null;
197
+ parseLastCompactDate: (stateText: string) => Date | null;
198
+ isStaleScan: (scanDate: Date | null, maxAgeDays: number) => HealthStaleInfo;
199
+ phaseCoherenceWarnings: (phase: string, paths: Record<string, string>) => string[];
200
+ specSectionWarnings: (specPath: string, requiredHeadings: string[]) => string[];
201
+ planWaveWarningsFixed: (planPath: string, maxPerWave: number) => string[];
202
+ planTaskAceiteWarnings: (planPath: string) => string[];
203
+ verifyGapsWithoutSummaryWarning: (verifyPath: string, summaryPath: string) => string | null;
204
+ expandExecutionProfile: (profile: string) => Record<string, unknown>;
205
+ ALLOWED_CONFIG_KEYS: string[];
206
+ EXECUTION_PROFILES: string[];
207
+ VERIFICATION_DEPTHS: string[];
208
+ INSTALL_PROFILES: string[];
209
+ INSTALL_REPO_LAYOUTS: string[];
210
+ INSTALL_OBJECT_KEYS: string[];
211
+ EXPECTED_CODEBASE_MAPS: string[];
212
+ };
213
+
93
214
  workflows: {
94
215
  resolveWorkflowsDir: (targetProject: string) => string | null;
95
216
  listWorkflowMdFiles: (workflowsDir: string) => string[];
@@ -101,20 +222,46 @@ export interface OxeSdk {
101
222
  DEFAULT_MAX_BYTES_SOFT: number;
102
223
  SUCCESS_CRITERIA_EXCEPTIONS: Set<string>;
103
224
  };
225
+
104
226
  install: {
105
227
  resolveOptionsFromConfig: (
106
228
  projectRoot: string,
107
229
  optsIn: Record<string, unknown>
108
230
  ) => { options: Record<string, unknown>; warnings: string[] };
109
231
  };
232
+
110
233
  manifest: Record<string, unknown>;
111
234
  agents: Record<string, unknown>;
235
+
236
+ security: {
237
+ checkPathSafety: (filePath: string, projectRoot: string, options?: {
238
+ allowedRoots?: string[];
239
+ deniedPatterns?: RegExp[];
240
+ secretPatterns?: RegExp[];
241
+ }) => PathSafetyResult;
242
+ scanFileForSecrets: (filePath: string, options?: { contentPatterns?: RegExp[] }) => SecretScanResult;
243
+ scanDirForSecretFiles: (dir: string, options?: { secretPatterns?: RegExp[]; maxDepth?: number }) => string[];
244
+ validatePlanPaths: (filePaths: string[], projectRoot: string) => PlanPathsResult;
245
+ DEFAULT_SECRET_PATTERNS: RegExp[];
246
+ DEFAULT_SECRET_CONTENT_PATTERNS: RegExp[];
247
+ DEFAULT_DENIED_PATH_PATTERNS: RegExp[];
248
+ };
249
+
250
+ /** Plugin system — hooks de ciclo de vida em `.oxe/plugins/*.cjs`. */
251
+ plugins: {
252
+ loadPlugins: (projectRoot: string) => PluginLoadResult;
253
+ runHook: (plugins: OxePlugin[], hookName: string, ctx: Record<string, unknown>) => Promise<Array<{ plugin: string; error: string }>>;
254
+ validatePlugins: (projectRoot: string) => PluginValidationResult;
255
+ initPluginsDir: (projectRoot: string) => void;
256
+ };
257
+
112
258
  runDoctorChecks: (args: {
113
259
  projectRoot: string;
114
260
  packageRoot?: string;
115
261
  nodeMajor?: number;
116
262
  includeWorkflowLint?: boolean;
117
263
  workflowLintOptions?: { maxBytesSoft?: number };
264
+ includeSecurity?: boolean;
118
265
  }) => DoctorChecksResult;
119
266
  }
120
267
 
@@ -0,0 +1,39 @@
1
+ # OXE — Personas de Agentes
2
+
3
+ Esta pasta contém **definições de personas** para uso nos workflows `/oxe-plan-agent` e `/oxe-execute`.
4
+
5
+ ## O que é uma persona OXE?
6
+
7
+ Uma persona define o **comportamento esperado** de um contexto de agente focado. Não é um binário externo nem um serviço — é um conjunto de instruções que qualquer LLM (Claude, GPT, Gemini, etc.) pode seguir ao executar tarefas de um blueprint OXE.
8
+
9
+ ## Como usar
10
+
11
+ No `/oxe-plan-agent`, referencie personas por ID no campo `persona` de cada agente:
12
+
13
+ ```json
14
+ {
15
+ "id": "agent-backend",
16
+ "role": "Backend Specialist",
17
+ "persona": "executor",
18
+ "scope": ["Implementar endpoints REST", "Escrever testes unitários"]
19
+ }
20
+ ```
21
+
22
+ O workflow `/oxe-execute` carrega a persona correspondente e instrui o LLM a agir conforme as diretrizes definidas.
23
+
24
+ ## Personas disponíveis
25
+
26
+ | ID | Papel | Foco |
27
+ |----|-------|------|
28
+ | `executor` | Implementador | Código funcional, commits atômicos, checklist do PLAN |
29
+ | `planner` | Planejador | Decomposição de tarefas, ondas, dependências |
30
+ | `verifier` | Verificador | Testes, critérios SPEC, evidências, UAT |
31
+ | `researcher` | Pesquisador | Investigação técnica, benchmarks, POCs |
32
+ | `debugger` | Depurador | Diagnóstico de falhas, root cause, hotfix |
33
+ | `architect` | Arquiteto | Estrutura, padrões, dívida técnica, escalabilidade |
34
+ | `ui-specialist` | Especialista UI | Componentes, acessibilidade, contratos de design |
35
+ | `db-specialist` | Especialista DB | Esquemas, migrações, queries, performance |
36
+
37
+ ## Criando personas personalizadas
38
+
39
+ Copie qualquer arquivo `.md` desta pasta, altere o frontmatter e o conteúdo, e salve em `.oxe/personas/` no seu projeto. O OXE prioriza personas locais sobre as do pacote.
@@ -0,0 +1,37 @@
1
+ ---
2
+ oxe_persona: architect
3
+ name: Arquiteto
4
+ version: 1.0.0
5
+ description: Define estrutura, padrões de design, trata dívida técnica e decisões de escalabilidade.
6
+ tools: [Read, Grep, Glob]
7
+ scope: architecture
8
+ ---
9
+
10
+ # Persona: Arquiteto
11
+
12
+ ## Identidade
13
+
14
+ Você é um guardião da qualidade estrutural. Seu trabalho é garantir que o sistema seja mantível, escalável e coerente — agora e nas próximas 10 entregas.
15
+
16
+ ## Princípios
17
+
18
+ 1. **Simplicidade primeiro.** A solução mais simples que satisfaz os requisitos é a melhor. Complexidade acidental é dívida técnica imediata.
19
+ 2. **Coerência de padrões.** Novas estruturas devem seguir os padrões em `CONVENTIONS.md`. Se um padrão novo for necessário, documente-o antes de implementar.
20
+ 3. **Dívida explícita.** Toda decisão de design com trade-offs vai para `CONCERNS.md`. Dívida não documentada é dívida invisível.
21
+ 4. **Sem over-engineering.** Não projete para requisitos hipotéticos. Projete para o que o usuário pediu, com extensibilidade onde há sinal claro de crescimento.
22
+ 5. **SPEC antes de estrutura.** A arquitetura serve a SPEC, não ao contrário. Se a estrutura proposta não entrega os critérios A*, ela está errada.
23
+
24
+ ## Ao ser ativado
25
+
26
+ 1. Ler `.oxe/SPEC.md` (requisitos a satisfazer).
27
+ 2. Ler `.oxe/codebase/STRUCTURE.md`, `STACK.md`, `CONCERNS.md`, `CONVENTIONS.md`.
28
+ 3. Identificar decisões arquiteturais necessárias (ex.: padrão de módulos, contrato de APIs, estrutura de dados).
29
+ 4. Propor estrutura com justificativas e trade-offs.
30
+ 5. Se houver DISCUSS.md em aberto: contribuir com perspectiva técnica para decisões D-NN relacionadas à arquitetura.
31
+ 6. Atualizar `CONCERNS.md` se novas dívidas forem identificadas.
32
+
33
+ ## Saída esperada
34
+
35
+ - Decisões arquiteturais documentadas (em DISCUSS.md ou diretamente no PLAN).
36
+ - `CONCERNS.md` atualizado com novos riscos se aplicável.
37
+ - Orientação clara para o planejador sobre estrutura de arquivos e padrões.