oxe-cc 0.7.1 → 0.9.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 (156) hide show
  1. package/.cursor/commands/oxe-ask.md +34 -0
  2. package/.cursor/commands/oxe-capabilities.md +34 -0
  3. package/.cursor/commands/oxe-checkpoint.md +34 -0
  4. package/.cursor/commands/oxe-compact.md +33 -0
  5. package/.cursor/commands/oxe-dashboard.md +34 -0
  6. package/.cursor/commands/oxe-debug.md +34 -0
  7. package/.cursor/commands/oxe-discuss.md +34 -0
  8. package/.cursor/commands/oxe-execute.md +34 -0
  9. package/.cursor/commands/oxe-forensics.md +34 -0
  10. package/.cursor/commands/oxe-help.md +33 -0
  11. package/.cursor/commands/oxe-loop.md +34 -0
  12. package/.cursor/commands/oxe-milestone.md +34 -0
  13. package/.cursor/commands/oxe-next.md +33 -0
  14. package/.cursor/commands/oxe-obs.md +34 -0
  15. package/.cursor/commands/oxe-plan-agent.md +33 -0
  16. package/.cursor/commands/oxe-plan.md +34 -0
  17. package/.cursor/commands/oxe-project.md +34 -0
  18. package/.cursor/commands/oxe-quick.md +34 -0
  19. package/.cursor/commands/oxe-research.md +34 -0
  20. package/.cursor/commands/oxe-retro.md +34 -0
  21. package/.cursor/commands/oxe-review-pr.md +34 -0
  22. package/.cursor/commands/oxe-route.md +34 -0
  23. package/.cursor/commands/oxe-scan.md +34 -0
  24. package/.cursor/commands/oxe-security.md +34 -0
  25. package/.cursor/commands/oxe-session.md +34 -0
  26. package/.cursor/commands/oxe-skill.md +45 -0
  27. package/.cursor/commands/oxe-spec.md +34 -0
  28. package/.cursor/commands/oxe-ui-review.md +34 -0
  29. package/.cursor/commands/oxe-ui-spec.md +34 -0
  30. package/.cursor/commands/oxe-update.md +33 -0
  31. package/.cursor/commands/oxe-validate-gaps.md +34 -0
  32. package/.cursor/commands/oxe-verify.md +34 -0
  33. package/.cursor/commands/oxe-workstream.md +34 -0
  34. package/.cursor/commands/oxe.md +38 -2
  35. package/.github/copilot-instructions.md +8 -5
  36. package/.github/prompts/oxe-ask.prompt.md +33 -0
  37. package/.github/prompts/oxe-capabilities.prompt.md +33 -0
  38. package/.github/prompts/oxe-checkpoint.prompt.md +45 -12
  39. package/.github/prompts/oxe-compact.prompt.md +44 -11
  40. package/.github/prompts/oxe-dashboard.prompt.md +33 -0
  41. package/.github/prompts/oxe-debug.prompt.md +45 -12
  42. package/.github/prompts/oxe-discuss.prompt.md +33 -0
  43. package/.github/prompts/oxe-execute.prompt.md +45 -12
  44. package/.github/prompts/oxe-forensics.prompt.md +45 -12
  45. package/.github/prompts/oxe-help.prompt.md +42 -9
  46. package/.github/prompts/oxe-loop.prompt.md +45 -12
  47. package/.github/prompts/oxe-milestone.prompt.md +45 -12
  48. package/.github/prompts/oxe-next.prompt.md +42 -9
  49. package/.github/prompts/oxe-obs.prompt.md +45 -12
  50. package/.github/prompts/oxe-plan-agent.prompt.md +43 -10
  51. package/.github/prompts/oxe-plan.prompt.md +45 -12
  52. package/.github/prompts/oxe-project.prompt.md +45 -12
  53. package/.github/prompts/oxe-quick.prompt.md +45 -12
  54. package/.github/prompts/oxe-research.prompt.md +45 -12
  55. package/.github/prompts/oxe-retro.prompt.md +45 -12
  56. package/.github/prompts/oxe-review-pr.prompt.md +45 -12
  57. package/.github/prompts/oxe-route.prompt.md +45 -12
  58. package/.github/prompts/oxe-scan.prompt.md +45 -12
  59. package/.github/prompts/oxe-security.prompt.md +45 -12
  60. package/.github/prompts/oxe-session.prompt.md +33 -0
  61. package/.github/prompts/oxe-skill.prompt.md +45 -0
  62. package/.github/prompts/oxe-spec.prompt.md +45 -12
  63. package/.github/prompts/oxe-ui-review.prompt.md +45 -12
  64. package/.github/prompts/oxe-ui-spec.prompt.md +45 -12
  65. package/.github/prompts/oxe-update.prompt.md +44 -11
  66. package/.github/prompts/oxe-validate-gaps.prompt.md +45 -12
  67. package/.github/prompts/oxe-verify.prompt.md +45 -12
  68. package/.github/prompts/oxe-workstream.prompt.md +45 -12
  69. package/.github/prompts/oxe.prompt.md +45 -12
  70. package/AGENTS.md +6 -4
  71. package/CHANGELOG.md +45 -0
  72. package/README.md +38 -8
  73. package/bin/lib/oxe-agent-install.cjs +69 -55
  74. package/bin/lib/oxe-context-engine.cjs +866 -0
  75. package/bin/lib/oxe-dashboard.cjs +605 -588
  76. package/bin/lib/oxe-operational.cjs +105 -0
  77. package/bin/lib/oxe-plugins.cjs +115 -0
  78. package/bin/lib/oxe-project-health.cjs +1139 -666
  79. package/bin/lib/oxe-runtime-semantics.cjs +459 -0
  80. package/bin/lib/oxe-security.cjs +64 -0
  81. package/bin/oxe-cc.js +615 -46
  82. package/commands/oxe/ask.md +33 -0
  83. package/commands/oxe/capabilities.md +33 -0
  84. package/commands/oxe/checkpoint.md +49 -16
  85. package/commands/oxe/compact.md +43 -10
  86. package/commands/oxe/dashboard.md +33 -0
  87. package/commands/oxe/debug.md +49 -16
  88. package/commands/oxe/discuss.md +33 -0
  89. package/commands/oxe/execute.md +49 -16
  90. package/commands/oxe/forensics.md +49 -16
  91. package/commands/oxe/help.md +44 -11
  92. package/commands/oxe/loop.md +50 -17
  93. package/commands/oxe/milestone.md +49 -16
  94. package/commands/oxe/next.md +45 -12
  95. package/commands/oxe/obs.md +49 -16
  96. package/commands/oxe/oxe.md +49 -16
  97. package/commands/oxe/plan-agent.md +48 -15
  98. package/commands/oxe/plan.md +48 -15
  99. package/commands/oxe/project.md +49 -16
  100. package/commands/oxe/quick.md +49 -16
  101. package/commands/oxe/research.md +49 -16
  102. package/commands/oxe/retro.md +49 -16
  103. package/commands/oxe/review-pr.md +49 -16
  104. package/commands/oxe/route.md +44 -11
  105. package/commands/oxe/scan.md +49 -16
  106. package/commands/oxe/security.md +49 -16
  107. package/commands/oxe/session.md +33 -0
  108. package/commands/oxe/skill.md +49 -0
  109. package/commands/oxe/spec.md +47 -14
  110. package/commands/oxe/ui-review.md +49 -16
  111. package/commands/oxe/ui-spec.md +49 -16
  112. package/commands/oxe/update.md +49 -16
  113. package/commands/oxe/validate-gaps.md +49 -16
  114. package/commands/oxe/verify.md +48 -15
  115. package/commands/oxe/workstream.md +49 -16
  116. package/lib/sdk/index.cjs +140 -7
  117. package/lib/sdk/index.d.ts +266 -1
  118. package/oxe/templates/HYPOTHESES.template.md +33 -0
  119. package/oxe/templates/PLAN.template.md +53 -22
  120. package/oxe/templates/SESSION.template.md +2 -0
  121. package/oxe/templates/SKILL.template.md +26 -0
  122. package/oxe/templates/WORKFLOW_AUTHORING.md +18 -2
  123. package/oxe/templates/config.template.json +16 -14
  124. package/oxe/workflows/ask.md +28 -7
  125. package/oxe/workflows/capabilities.md +2 -0
  126. package/oxe/workflows/dashboard.md +12 -2
  127. package/oxe/workflows/debug.md +9 -4
  128. package/oxe/workflows/discuss.md +12 -6
  129. package/oxe/workflows/execute.md +34 -12
  130. package/oxe/workflows/forensics.md +14 -9
  131. package/oxe/workflows/help.md +20 -9
  132. package/oxe/workflows/loop.md +13 -7
  133. package/oxe/workflows/next.md +6 -4
  134. package/oxe/workflows/plan-agent.md +3 -2
  135. package/oxe/workflows/plan.md +26 -3
  136. package/oxe/workflows/quick.md +10 -3
  137. package/oxe/workflows/references/reasoning-discovery.md +28 -0
  138. package/oxe/workflows/references/reasoning-execution.md +29 -0
  139. package/oxe/workflows/references/reasoning-planning.md +32 -0
  140. package/oxe/workflows/references/reasoning-review.md +29 -0
  141. package/oxe/workflows/references/reasoning-status.md +24 -0
  142. package/oxe/workflows/references/workflow-runtime-contracts.json +879 -0
  143. package/oxe/workflows/research.md +8 -2
  144. package/oxe/workflows/retro.md +7 -2
  145. package/oxe/workflows/review-pr.md +12 -8
  146. package/oxe/workflows/route.md +16 -13
  147. package/oxe/workflows/security.md +3 -2
  148. package/oxe/workflows/session.md +44 -0
  149. package/oxe/workflows/skill.md +44 -0
  150. package/oxe/workflows/spec.md +21 -18
  151. package/oxe/workflows/ui-review.md +13 -7
  152. package/oxe/workflows/update.md +3 -1
  153. package/oxe/workflows/validate-gaps.md +12 -6
  154. package/oxe/workflows/verify-audit.md +73 -0
  155. package/oxe/workflows/verify.md +40 -16
  156. package/package.json +4 -3
@@ -0,0 +1,459 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const crypto = require('crypto');
6
+
7
+ const CONTRACTS_PATH = path.join(
8
+ __dirname,
9
+ '..',
10
+ '..',
11
+ 'oxe',
12
+ 'workflows',
13
+ 'references',
14
+ 'workflow-runtime-contracts.json'
15
+ );
16
+
17
+ const RUNTIME_METADATA_KEYS = [
18
+ 'oxe_workflow_slug',
19
+ 'oxe_reasoning_mode',
20
+ 'oxe_question_policy',
21
+ 'oxe_output_contract',
22
+ 'oxe_tool_profile',
23
+ 'oxe_confidence_policy',
24
+ 'oxe_context_tier',
25
+ 'oxe_contract_version',
26
+ 'oxe_semantics_hash',
27
+ ];
28
+
29
+ const REQUIRED_CONTRACT_FIELDS = [
30
+ 'reasoning_mode',
31
+ 'question_policy',
32
+ 'output_contract',
33
+ 'tool_profile',
34
+ 'confidence_policy',
35
+ 'required_artifacts',
36
+ 'optional_artifacts',
37
+ 'context_tiers',
38
+ 'freshness_policy',
39
+ 'fallback_policy',
40
+ 'blocking_conditions',
41
+ 'output_sections',
42
+ ];
43
+
44
+ function readContractsRegistry() {
45
+ try {
46
+ const raw = fs.readFileSync(CONTRACTS_PATH, 'utf8');
47
+ const parsed = JSON.parse(raw);
48
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
49
+ } catch {
50
+ return {};
51
+ }
52
+ }
53
+
54
+ const CONTRACTS_REGISTRY = readContractsRegistry();
55
+ if (!CONTRACTS_REGISTRY.contract_version) {
56
+ process.stderr.write('[oxe] WARN: workflow-runtime-contracts.json ausente ou inválido — usando defaults.\n');
57
+ }
58
+ const CONTRACT_VERSION = String(CONTRACTS_REGISTRY.contract_version || '0.0.0');
59
+ const MODE_REFERENCES = CONTRACTS_REGISTRY.mode_references || {};
60
+ const MODE_GUIDANCE = CONTRACTS_REGISTRY.mode_guidance || {};
61
+ const MODE_DEFAULT_SPECS = CONTRACTS_REGISTRY.mode_defaults || {};
62
+ const MODE_DEFAULTS = Object.fromEntries(
63
+ Object.entries(MODE_DEFAULT_SPECS).map(([mode, spec]) => [
64
+ mode,
65
+ {
66
+ oxe_reasoning_mode: mode,
67
+ oxe_question_policy: spec.question_policy,
68
+ oxe_output_contract: spec.output_contract,
69
+ oxe_tool_profile: spec.tool_profile,
70
+ oxe_confidence_policy: spec.confidence_policy,
71
+ },
72
+ ])
73
+ );
74
+
75
+ function stableJson(value) {
76
+ if (Array.isArray(value)) return `[${value.map((item) => stableJson(item)).join(',')}]`;
77
+ if (value && typeof value === 'object') {
78
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(',')}}`;
79
+ }
80
+ return JSON.stringify(value);
81
+ }
82
+
83
+ function uniqueStrings(items) {
84
+ return Array.from(new Set((items || []).map((item) => String(item || '').trim()).filter(Boolean)));
85
+ }
86
+
87
+ function buildContextTiers(requiredArtifacts, optionalArtifacts) {
88
+ const required = uniqueStrings(requiredArtifacts);
89
+ const optional = uniqueStrings(optionalArtifacts);
90
+ const minimalTail = optional.filter((item) => /(phase_summary|project_summary|session_summary)$/.test(item)).slice(0, 2);
91
+ const standardTail = optional.filter((item) => !minimalTail.includes(item)).slice(0, 4);
92
+ return {
93
+ minimal: uniqueStrings([...required, ...minimalTail]),
94
+ standard: uniqueStrings([...required, ...minimalTail, ...standardTail]),
95
+ full: uniqueStrings([...required, ...optional]),
96
+ };
97
+ }
98
+
99
+ function getWorkflowContract(slug) {
100
+ const workflow = (CONTRACTS_REGISTRY.workflows || {})[slug];
101
+ if (!workflow) return null;
102
+ const mode = String(workflow.reasoning_mode || 'status');
103
+ const modeDefaults = MODE_DEFAULT_SPECS[mode] || MODE_DEFAULT_SPECS.status || {};
104
+ const requiredArtifacts = uniqueStrings(workflow.required_artifacts);
105
+ const optionalArtifacts = uniqueStrings(workflow.optional_artifacts);
106
+ return {
107
+ workflow_slug: slug,
108
+ contract_version: CONTRACT_VERSION,
109
+ reasoning_mode: mode,
110
+ question_policy: String(workflow.question_policy || modeDefaults.question_policy || 'none'),
111
+ output_contract: String(workflow.output_contract || modeDefaults.output_contract || 'routing'),
112
+ tool_profile: String(workflow.tool_profile || modeDefaults.tool_profile || 'read_heavy'),
113
+ confidence_policy: String(workflow.confidence_policy || modeDefaults.confidence_policy || 'explicit'),
114
+ required_artifacts: requiredArtifacts,
115
+ optional_artifacts: optionalArtifacts,
116
+ context_tiers: workflow.context_tiers || buildContextTiers(requiredArtifacts, optionalArtifacts),
117
+ freshness_policy: workflow.freshness_policy || modeDefaults.freshness_policy || {},
118
+ fallback_policy: String(workflow.fallback_policy || modeDefaults.fallback_policy || 'read_direct_with_warning'),
119
+ blocking_conditions: uniqueStrings(workflow.blocking_conditions || modeDefaults.blocking_conditions || []),
120
+ output_sections: uniqueStrings(workflow.output_sections || modeDefaults.output_sections || []),
121
+ extraction_intent: String(workflow.extraction_intent || 'status_read'),
122
+ auditor_artifacts: Array.isArray(workflow.auditor_artifacts) ? workflow.auditor_artifacts.slice() : [],
123
+ auditor_excluded: Array.isArray(workflow.auditor_excluded) ? workflow.auditor_excluded.slice() : [],
124
+ reference: MODE_REFERENCES[mode] || null,
125
+ guidance: Array.isArray(MODE_GUIDANCE[mode]) ? MODE_GUIDANCE[mode].slice() : [],
126
+ };
127
+ }
128
+
129
+ function getAllWorkflowContracts() {
130
+ return Object.keys(CONTRACTS_REGISTRY.workflows || {})
131
+ .sort()
132
+ .map((slug) => getWorkflowContract(slug))
133
+ .filter(Boolean);
134
+ }
135
+
136
+ function validateWorkflowContractsRegistry(registry = CONTRACTS_REGISTRY) {
137
+ const issues = [];
138
+ if (!registry || typeof registry !== 'object' || Array.isArray(registry)) {
139
+ return ['workflow-runtime-contracts.json deve ser um objeto'];
140
+ }
141
+ if (!registry.contract_version || typeof registry.contract_version !== 'string') {
142
+ issues.push('workflow-runtime-contracts.json: contract_version ausente ou inválido');
143
+ }
144
+ if (!registry.workflows || typeof registry.workflows !== 'object' || Array.isArray(registry.workflows)) {
145
+ issues.push('workflow-runtime-contracts.json: workflows ausente ou inválido');
146
+ return issues;
147
+ }
148
+ for (const slug of Object.keys(registry.workflows)) {
149
+ const contract = getWorkflowContract(slug);
150
+ if (!contract) {
151
+ issues.push(`workflow-runtime-contracts.json: workflow "${slug}" inválido`);
152
+ continue;
153
+ }
154
+ for (const field of REQUIRED_CONTRACT_FIELDS) {
155
+ const value = contract[field];
156
+ if (value == null) {
157
+ issues.push(`workflow-runtime-contracts.json: ${slug} sem "${field}"`);
158
+ }
159
+ }
160
+ for (const tier of ['minimal', 'standard', 'full']) {
161
+ if (!Array.isArray(contract.context_tiers[tier])) {
162
+ issues.push(`workflow-runtime-contracts.json: ${slug} context_tiers.${tier} deve ser array`);
163
+ }
164
+ }
165
+ }
166
+ return issues;
167
+ }
168
+
169
+ function computeSemanticsHash(slug) {
170
+ const contract = getWorkflowContract(slug);
171
+ if (!contract) return null;
172
+ return crypto.createHash('sha256').update(stableJson(contract)).digest('hex').slice(0, 16);
173
+ }
174
+
175
+ function slugFromPromptFilename(name) {
176
+ return name.replace(/^oxe-/, '').replace(/\.prompt\.md$/i, '');
177
+ }
178
+
179
+ function slugFromCommandFilename(name) {
180
+ return name.replace(/^oxe-/i, '').replace(/\.md$/i, '');
181
+ }
182
+
183
+ function getRuntimeMetadataForSlug(slug, options = {}) {
184
+ const tier = String(options.tier || 'standard');
185
+ const contract = getWorkflowContract(slug);
186
+ if (!contract) {
187
+ const fallbackHash = crypto.createHash('sha256').update(`fallback:${slug}`).digest('hex').slice(0, 16);
188
+ return {
189
+ oxe_workflow_slug: slug,
190
+ ...(MODE_DEFAULTS.status || {}),
191
+ oxe_context_tier: tier,
192
+ oxe_contract_version: CONTRACT_VERSION,
193
+ oxe_semantics_hash: fallbackHash,
194
+ };
195
+ }
196
+ return {
197
+ oxe_workflow_slug: slug,
198
+ oxe_reasoning_mode: contract.reasoning_mode,
199
+ oxe_question_policy: contract.question_policy,
200
+ oxe_output_contract: contract.output_contract,
201
+ oxe_tool_profile: contract.tool_profile,
202
+ oxe_confidence_policy: contract.confidence_policy,
203
+ oxe_context_tier: tier,
204
+ oxe_contract_version: CONTRACT_VERSION,
205
+ oxe_semantics_hash: computeSemanticsHash(slug),
206
+ };
207
+ }
208
+
209
+ function pickRuntimeMetadata(frontmatter) {
210
+ const out = {};
211
+ for (const key of RUNTIME_METADATA_KEYS) {
212
+ if (frontmatter && typeof frontmatter[key] === 'string' && frontmatter[key].trim()) {
213
+ out[key] = frontmatter[key].trim();
214
+ }
215
+ }
216
+ return out;
217
+ }
218
+
219
+ function renderRuntimeMetadataLines(meta) {
220
+ return RUNTIME_METADATA_KEYS.map((key) => `${key}: ${meta[key] || ''}`);
221
+ }
222
+
223
+ function humanizeValue(value) {
224
+ const key = String(value || '');
225
+ const labels = {
226
+ discovery: 'descoberta',
227
+ planning: 'planejamento',
228
+ execution: 'execução',
229
+ review: 'revisão',
230
+ status: 'estado / roteamento',
231
+ explore_first: 'explorar primeiro',
232
+ ask_high_impact_only: 'perguntar só alto impacto',
233
+ none: 'nenhuma',
234
+ situational: 'situacional',
235
+ plan: 'plano',
236
+ findings: 'achados',
237
+ routing: 'roteamento',
238
+ execution: 'execução',
239
+ read_heavy: 'leitura intensa',
240
+ write_bounded: 'mutação limitada',
241
+ review_heavy: 'revisão intensa',
242
+ mixed: 'misto',
243
+ explicit: 'explícita',
244
+ rubric: 'rubrica',
245
+ optional: 'opcional',
246
+ minimal: 'mínimo',
247
+ standard: 'padrão',
248
+ full: 'completo',
249
+ };
250
+ return labels[key] || key.replace(/_/g, ' ');
251
+ }
252
+
253
+ function buildContextPackPaths(slug) {
254
+ return {
255
+ markdown: `.oxe/context/packs/${slug}.md`,
256
+ json: `.oxe/context/packs/${slug}.json`,
257
+ inspectCommand: `oxe-cc context inspect --workflow ${slug} --json`,
258
+ };
259
+ }
260
+
261
+ function buildReasoningContractBlock(meta, options = {}) {
262
+ const includeReference = options.includeReference !== false;
263
+ const slug = String(meta.oxe_workflow_slug || options.slug || '');
264
+ const contract = getWorkflowContract(slug) || {};
265
+ const mode = meta.oxe_reasoning_mode || contract.reasoning_mode || 'status';
266
+ const guidance = contract.guidance || MODE_GUIDANCE[mode] || [];
267
+ const outputSections = contract.output_sections || [];
268
+ const blocking = contract.blocking_conditions || [];
269
+ const contextPack = buildContextPackPaths(slug);
270
+ const lines = [
271
+ '<!-- oxe-reasoning-contract:start -->',
272
+ '',
273
+ '**Contrato de raciocínio OXE deste comando**',
274
+ `- **Workflow:** ${slug || '—'}`,
275
+ `- **Modo:** ${humanizeValue(meta.oxe_reasoning_mode)}`,
276
+ `- **Perguntas:** ${humanizeValue(meta.oxe_question_policy)}`,
277
+ `- **Saída esperada:** ${humanizeValue(meta.oxe_output_contract)}`,
278
+ `- **Perfil de ferramentas:** ${humanizeValue(meta.oxe_tool_profile)}`,
279
+ `- **Política de confiança:** ${humanizeValue(meta.oxe_confidence_policy)}`,
280
+ `- **Tier de contexto padrão:** ${humanizeValue(meta.oxe_context_tier || 'standard')}`,
281
+ `- **Versão do contrato:** ${meta.oxe_contract_version || CONTRACT_VERSION}`,
282
+ `- **Checksum semântico:** \`${meta.oxe_semantics_hash || computeSemanticsHash(slug) || '—'}\``,
283
+ `- **Entrada de contexto prioritária:** \`${contextPack.markdown}\` e \`${contextPack.json}\``,
284
+ `- **Regra pack-first:** ler o context pack primeiro; se estiver stale, incompleto ou ausente, cair para leitura direta com fallback explícito.`,
285
+ `- **Inspeção estruturada:** \`${contextPack.inspectCommand}\``,
286
+ ...guidance.map((line) => `- ${line}`),
287
+ ];
288
+ if (outputSections.length) {
289
+ lines.push(`- **Seções esperadas:** ${outputSections.join(' · ')}`);
290
+ }
291
+ if (blocking.length) {
292
+ lines.push(`- **Bloqueios formais:** ${blocking.join(' · ')}`);
293
+ }
294
+ if (includeReference && MODE_REFERENCES[mode]) {
295
+ lines.push(`- **Referência canónica:** \`${MODE_REFERENCES[mode]}\``);
296
+ }
297
+ lines.push('', '<!-- oxe-reasoning-contract:end -->');
298
+ return lines.join('\n');
299
+ }
300
+
301
+ function splitFrontmatter(raw) {
302
+ const normalized = String(raw || '').replace(/^\uFEFF/, '').replace(/\r\n/g, '\n');
303
+ if (!normalized.startsWith('---\n')) {
304
+ return { frontmatter: '', body: normalized.trimStart() };
305
+ }
306
+ const end = normalized.indexOf('\n---\n', 4);
307
+ if (end === -1) {
308
+ return { frontmatter: '', body: normalized.trimStart() };
309
+ }
310
+ return {
311
+ frontmatter: normalized.slice(4, end),
312
+ body: normalized.slice(end + 5).trimStart(),
313
+ };
314
+ }
315
+
316
+ function parseFrontmatterMap(raw) {
317
+ const { frontmatter } = splitFrontmatter(raw);
318
+ const out = {};
319
+ if (!frontmatter) return out;
320
+ for (const line of frontmatter.split('\n')) {
321
+ const match = line.match(/^([A-Za-z0-9_-]+):\s*(.+)$/);
322
+ if (match) out[match[1]] = match[2].trim().replace(/^["']|["']$/g, '');
323
+ }
324
+ return out;
325
+ }
326
+
327
+ function auditWrapperText(slug, raw) {
328
+ const frontmatter = parseFrontmatterMap(raw);
329
+ const expected = getRuntimeMetadataForSlug(slug);
330
+ const expectedPack = buildContextPackPaths(slug);
331
+ const issues = [];
332
+ for (const key of RUNTIME_METADATA_KEYS) {
333
+ if ((frontmatter[key] || '') !== (expected[key] || '')) {
334
+ issues.push({ key, expected: expected[key] || '', actual: frontmatter[key] || '' });
335
+ }
336
+ }
337
+ if (!String(raw || '').includes('<!-- oxe-reasoning-contract:start -->')) {
338
+ issues.push({ key: 'oxe-reasoning-contract', expected: 'present', actual: 'missing' });
339
+ }
340
+ if (!String(raw || '').includes(`Checksum semântico:** \`${expected.oxe_semantics_hash}\``)) {
341
+ issues.push({ key: 'oxe_semantics_hash_block', expected: expected.oxe_semantics_hash, actual: 'mismatch' });
342
+ }
343
+ if (!String(raw || '').includes(expectedPack.markdown) || !String(raw || '').includes(expectedPack.json)) {
344
+ issues.push({ key: 'oxe_context_pack_entry', expected: `${expectedPack.markdown} + ${expectedPack.json}`, actual: 'missing' });
345
+ }
346
+ if (!String(raw || '').includes('Regra pack-first')) {
347
+ issues.push({ key: 'oxe_pack_first_rule', expected: 'present', actual: 'missing' });
348
+ }
349
+ return {
350
+ slug,
351
+ frontmatter,
352
+ expected,
353
+ issues,
354
+ ok: issues.length === 0,
355
+ };
356
+ }
357
+
358
+ function auditRuntimeTargets(projectRoot) {
359
+ const targets = [
360
+ {
361
+ name: 'copilot-prompts',
362
+ dir: path.join(projectRoot, '.github', 'prompts'),
363
+ filter: (name) => (name === 'oxe.prompt.md' || name.startsWith('oxe-')) && name.endsWith('.prompt.md'),
364
+ slug: slugFromPromptFilename,
365
+ },
366
+ {
367
+ name: 'commands',
368
+ dir: path.join(projectRoot, 'commands', 'oxe'),
369
+ filter: (name) => name.endsWith('.md'),
370
+ slug: slugFromCommandFilename,
371
+ },
372
+ {
373
+ name: 'cursor',
374
+ dir: path.join(projectRoot, '.cursor', 'commands'),
375
+ filter: (name) => (name === 'oxe.md' || name.startsWith('oxe-')) && name.endsWith('.md'),
376
+ slug: slugFromCommandFilename,
377
+ },
378
+ ];
379
+ const result = {
380
+ ok: true,
381
+ contractVersion: CONTRACT_VERSION,
382
+ registryPath: CONTRACTS_PATH,
383
+ registryIssues: validateWorkflowContractsRegistry(),
384
+ warnings: [],
385
+ mismatches: [],
386
+ targets: {},
387
+ };
388
+ for (const target of targets) {
389
+ const info = { path: target.dir, files: [], checked: 0, missing: false };
390
+ if (!fs.existsSync(target.dir)) {
391
+ info.missing = true;
392
+ result.targets[target.name] = info;
393
+ continue;
394
+ }
395
+ let names;
396
+ try {
397
+ names = fs.readdirSync(target.dir);
398
+ } catch (err) {
399
+ info.missing = true;
400
+ result.warnings.push(`${target.name}: falha ao listar diretório — ${err instanceof Error ? err.message : String(err)}`);
401
+ result.targets[target.name] = info;
402
+ continue;
403
+ }
404
+ for (const name of names) {
405
+ if (!target.filter(name)) continue;
406
+ const filePath = path.join(target.dir, name);
407
+ let raw;
408
+ try {
409
+ raw = fs.readFileSync(filePath, 'utf8');
410
+ } catch (err) {
411
+ result.warnings.push(`${target.name}/${name}: falha ao ler arquivo — ${err instanceof Error ? err.message : String(err)}`);
412
+ continue;
413
+ }
414
+ const audit = auditWrapperText(target.slug(name), raw);
415
+ info.checked += 1;
416
+ info.files.push({ file: filePath, slug: audit.slug, ok: audit.ok, issues: audit.issues });
417
+ if (!audit.ok) {
418
+ result.ok = false;
419
+ result.mismatches.push({ target: target.name, file: filePath, slug: audit.slug, issues: audit.issues });
420
+ }
421
+ }
422
+ result.targets[target.name] = info;
423
+ }
424
+ if (result.registryIssues.length) {
425
+ result.ok = false;
426
+ result.warnings.push(...result.registryIssues);
427
+ }
428
+ if (result.mismatches.length) {
429
+ result.warnings.push(`${result.mismatches.length} wrapper(s) com drift semântico detectado`);
430
+ }
431
+ return result;
432
+ }
433
+
434
+ module.exports = {
435
+ CONTRACT_VERSION,
436
+ CONTRACTS_PATH,
437
+ CONTRACTS_REGISTRY,
438
+ MODE_DEFAULTS,
439
+ MODE_GUIDANCE,
440
+ MODE_REFERENCES,
441
+ REQUIRED_CONTRACT_FIELDS,
442
+ RUNTIME_METADATA_KEYS,
443
+ auditRuntimeTargets,
444
+ auditWrapperText,
445
+ buildContextPackPaths,
446
+ buildContextTiers,
447
+ buildReasoningContractBlock,
448
+ computeSemanticsHash,
449
+ getAllWorkflowContracts,
450
+ getRuntimeMetadataForSlug,
451
+ getWorkflowContract,
452
+ parseFrontmatterMap,
453
+ pickRuntimeMetadata,
454
+ renderRuntimeMetadataLines,
455
+ slugFromCommandFilename,
456
+ slugFromPromptFilename,
457
+ splitFrontmatter,
458
+ validateWorkflowContractsRegistry,
459
+ };
@@ -214,11 +214,75 @@ function validatePlanPaths(filePaths, projectRoot) {
214
214
  return { ok: issues.length === 0, issues };
215
215
  }
216
216
 
217
+ /**
218
+ * Converte glob simples em RegExp.
219
+ * Suporta: `*` (qualquer segmento sem separador), `**` (qualquer profundidade), `?` (um char).
220
+ * @param {string} glob
221
+ * @returns {RegExp}
222
+ */
223
+ function globToRegex(glob) {
224
+ const re = glob
225
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&') // escape specials (exceto * e ?)
226
+ .replace(/\*\*/g, '\u0000') // placeholder para **
227
+ .replace(/\*/g, '[^/\\\\]*') // * = qualquer coisa exceto separador
228
+ .replace(/\?/g, '[^/\\\\]') // ? = um char exceto separador
229
+ .replace(/\u0000\//g, '(.+\\/)?') // **/ = prefixo de diretório opcional (zero ou mais níveis)
230
+ .replace(/\u0000/g, '.*'); // ** isolado = qualquer profundidade
231
+ return new RegExp(`^${re}$`, 'i');
232
+ }
233
+
234
+ /**
235
+ * Verifica um filepath contra uma lista de regras de permissão (first-match wins).
236
+ * @param {string} filePath — caminho relativo ao projeto (ex.: `src/app.ts`, `.env`)
237
+ * @param {Array<{ pattern: string, action: string, scope?: string }>} permissions
238
+ * @param {string} [currentScope='execute']
239
+ * @returns {{ action: string, rule: object | null }}
240
+ */
241
+ function checkFilePermission(filePath, permissions, currentScope = 'execute') {
242
+ if (!Array.isArray(permissions) || permissions.length === 0) {
243
+ return { action: 'allow', rule: null };
244
+ }
245
+ const normalized = filePath.replace(/\\/g, '/');
246
+ const basename = normalized.split('/').pop() || '';
247
+ for (const rule of permissions) {
248
+ const ruleScope = rule.scope || 'all';
249
+ if (ruleScope !== 'all' && ruleScope !== currentScope) continue;
250
+ const regex = globToRegex(rule.pattern);
251
+ if (regex.test(normalized) || regex.test(basename)) {
252
+ return { action: String(rule.action || 'allow'), rule };
253
+ }
254
+ }
255
+ return { action: 'allow', rule: null };
256
+ }
257
+
258
+ /**
259
+ * Verifica uma lista de filepaths contra regras de permissão.
260
+ * @param {string[]} fileList
261
+ * @param {Array<{ pattern: string, action: string, scope?: string }>} permissions
262
+ * @param {string} [scope='execute']
263
+ * @returns {{ denied: string[], needsApproval: string[], allowed: string[] }}
264
+ */
265
+ function checkPermissions(fileList, permissions, scope = 'execute') {
266
+ const denied = [];
267
+ const needsApproval = [];
268
+ const allowed = [];
269
+ for (const f of fileList) {
270
+ const result = checkFilePermission(f, permissions, scope);
271
+ if (result.action === 'deny') denied.push(f);
272
+ else if (result.action === 'ask') needsApproval.push(f);
273
+ else allowed.push(f);
274
+ }
275
+ return { denied, needsApproval, allowed };
276
+ }
277
+
217
278
  module.exports = {
218
279
  checkPathSafety,
219
280
  scanFileForSecrets,
220
281
  scanDirForSecretFiles,
221
282
  validatePlanPaths,
283
+ checkFilePermission,
284
+ checkPermissions,
285
+ globToRegex,
222
286
  DEFAULT_SECRET_PATTERNS,
223
287
  DEFAULT_SECRET_CONTENT_PATTERNS,
224
288
  DEFAULT_DENIED_PATH_PATTERNS,