oxe-cc 0.7.0 → 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.
- package/.cursor/commands/oxe-ask.md +34 -0
- package/.cursor/commands/oxe-capabilities.md +34 -0
- package/.cursor/commands/oxe-checkpoint.md +34 -0
- package/.cursor/commands/oxe-compact.md +33 -0
- package/.cursor/commands/oxe-dashboard.md +34 -0
- package/.cursor/commands/oxe-debug.md +34 -0
- package/.cursor/commands/oxe-discuss.md +34 -0
- package/.cursor/commands/oxe-execute.md +34 -0
- package/.cursor/commands/oxe-forensics.md +34 -0
- package/.cursor/commands/oxe-help.md +33 -0
- package/.cursor/commands/oxe-loop.md +34 -0
- package/.cursor/commands/oxe-milestone.md +34 -0
- package/.cursor/commands/oxe-next.md +33 -0
- package/.cursor/commands/oxe-obs.md +34 -0
- package/.cursor/commands/oxe-plan-agent.md +33 -0
- package/.cursor/commands/oxe-plan.md +34 -0
- package/.cursor/commands/oxe-project.md +34 -0
- package/.cursor/commands/oxe-quick.md +34 -0
- package/.cursor/commands/oxe-research.md +34 -0
- package/.cursor/commands/oxe-retro.md +34 -0
- package/.cursor/commands/oxe-review-pr.md +34 -0
- package/.cursor/commands/oxe-route.md +34 -0
- package/.cursor/commands/oxe-scan.md +34 -0
- package/.cursor/commands/oxe-security.md +34 -0
- package/.cursor/commands/oxe-session.md +34 -0
- package/.cursor/commands/oxe-skill.md +45 -0
- package/.cursor/commands/oxe-spec.md +34 -0
- package/.cursor/commands/oxe-ui-review.md +34 -0
- package/.cursor/commands/oxe-ui-spec.md +34 -0
- package/.cursor/commands/oxe-update.md +33 -0
- package/.cursor/commands/oxe-validate-gaps.md +34 -0
- package/.cursor/commands/oxe-verify.md +34 -0
- package/.cursor/commands/oxe-workstream.md +34 -0
- package/.cursor/commands/oxe.md +38 -2
- package/.github/copilot-instructions.md +20 -5
- package/.github/prompts/oxe-ask.prompt.md +33 -0
- package/.github/prompts/oxe-capabilities.prompt.md +33 -0
- package/.github/prompts/oxe-checkpoint.prompt.md +45 -12
- package/.github/prompts/oxe-compact.prompt.md +44 -11
- package/.github/prompts/oxe-dashboard.prompt.md +33 -0
- package/.github/prompts/oxe-debug.prompt.md +45 -12
- package/.github/prompts/oxe-discuss.prompt.md +33 -0
- package/.github/prompts/oxe-execute.prompt.md +45 -12
- package/.github/prompts/oxe-forensics.prompt.md +45 -12
- package/.github/prompts/oxe-help.prompt.md +42 -9
- package/.github/prompts/oxe-loop.prompt.md +45 -12
- package/.github/prompts/oxe-milestone.prompt.md +45 -12
- package/.github/prompts/oxe-next.prompt.md +42 -9
- package/.github/prompts/oxe-obs.prompt.md +45 -12
- package/.github/prompts/oxe-plan-agent.prompt.md +43 -10
- package/.github/prompts/oxe-plan.prompt.md +45 -12
- package/.github/prompts/oxe-project.prompt.md +45 -12
- package/.github/prompts/oxe-quick.prompt.md +45 -12
- package/.github/prompts/oxe-research.prompt.md +45 -12
- package/.github/prompts/oxe-retro.prompt.md +45 -12
- package/.github/prompts/oxe-review-pr.prompt.md +45 -12
- package/.github/prompts/oxe-route.prompt.md +45 -12
- package/.github/prompts/oxe-scan.prompt.md +45 -12
- package/.github/prompts/oxe-security.prompt.md +45 -12
- package/.github/prompts/oxe-session.prompt.md +33 -0
- package/.github/prompts/oxe-skill.prompt.md +45 -0
- package/.github/prompts/oxe-spec.prompt.md +45 -12
- package/.github/prompts/oxe-ui-review.prompt.md +45 -12
- package/.github/prompts/oxe-ui-spec.prompt.md +45 -12
- package/.github/prompts/oxe-update.prompt.md +44 -11
- package/.github/prompts/oxe-validate-gaps.prompt.md +45 -12
- package/.github/prompts/oxe-verify.prompt.md +45 -12
- package/.github/prompts/oxe-workstream.prompt.md +45 -12
- package/.github/prompts/oxe.prompt.md +45 -12
- package/AGENTS.md +6 -4
- package/CHANGELOG.md +45 -0
- package/README.md +38 -8
- package/bin/banner.txt +1 -1
- package/bin/lib/oxe-agent-install.cjs +69 -55
- package/bin/lib/oxe-context-engine.cjs +866 -0
- package/bin/lib/oxe-dashboard.cjs +605 -588
- package/bin/lib/oxe-operational.cjs +105 -0
- package/bin/lib/oxe-plugins.cjs +115 -0
- package/bin/lib/oxe-project-health.cjs +1139 -666
- package/bin/lib/oxe-runtime-semantics.cjs +459 -0
- package/bin/lib/oxe-security.cjs +64 -0
- package/bin/oxe-cc.js +615 -46
- package/commands/oxe/ask.md +33 -0
- package/commands/oxe/capabilities.md +33 -0
- package/commands/oxe/checkpoint.md +49 -16
- package/commands/oxe/compact.md +43 -10
- package/commands/oxe/dashboard.md +33 -0
- package/commands/oxe/debug.md +49 -16
- package/commands/oxe/discuss.md +33 -0
- package/commands/oxe/execute.md +49 -16
- package/commands/oxe/forensics.md +49 -16
- package/commands/oxe/help.md +44 -11
- package/commands/oxe/loop.md +50 -17
- package/commands/oxe/milestone.md +49 -16
- package/commands/oxe/next.md +45 -12
- package/commands/oxe/obs.md +49 -16
- package/commands/oxe/oxe.md +49 -16
- package/commands/oxe/plan-agent.md +48 -15
- package/commands/oxe/plan.md +48 -15
- package/commands/oxe/project.md +49 -16
- package/commands/oxe/quick.md +49 -16
- package/commands/oxe/research.md +49 -16
- package/commands/oxe/retro.md +49 -16
- package/commands/oxe/review-pr.md +49 -16
- package/commands/oxe/route.md +44 -11
- package/commands/oxe/scan.md +49 -16
- package/commands/oxe/security.md +49 -16
- package/commands/oxe/session.md +33 -0
- package/commands/oxe/skill.md +49 -0
- package/commands/oxe/spec.md +47 -14
- package/commands/oxe/ui-review.md +49 -16
- package/commands/oxe/ui-spec.md +49 -16
- package/commands/oxe/update.md +49 -16
- package/commands/oxe/validate-gaps.md +49 -16
- package/commands/oxe/verify.md +48 -15
- package/commands/oxe/workstream.md +49 -16
- package/lib/sdk/index.cjs +140 -7
- package/lib/sdk/index.d.ts +266 -1
- package/oxe/templates/HYPOTHESES.template.md +33 -0
- package/oxe/templates/PLAN.template.md +53 -22
- package/oxe/templates/SESSION.template.md +2 -0
- package/oxe/templates/SKILL.template.md +26 -0
- package/oxe/templates/WORKFLOW_AUTHORING.md +18 -2
- package/oxe/templates/config.template.json +16 -14
- package/oxe/workflows/ask.md +28 -7
- package/oxe/workflows/capabilities.md +2 -0
- package/oxe/workflows/dashboard.md +12 -2
- package/oxe/workflows/debug.md +9 -4
- package/oxe/workflows/discuss.md +12 -6
- package/oxe/workflows/execute.md +34 -12
- package/oxe/workflows/forensics.md +14 -9
- package/oxe/workflows/help.md +20 -9
- package/oxe/workflows/loop.md +13 -7
- package/oxe/workflows/next.md +6 -4
- package/oxe/workflows/plan-agent.md +3 -2
- package/oxe/workflows/plan.md +26 -3
- package/oxe/workflows/quick.md +10 -3
- package/oxe/workflows/references/reasoning-discovery.md +28 -0
- package/oxe/workflows/references/reasoning-execution.md +29 -0
- package/oxe/workflows/references/reasoning-planning.md +32 -0
- package/oxe/workflows/references/reasoning-review.md +29 -0
- package/oxe/workflows/references/reasoning-status.md +24 -0
- package/oxe/workflows/references/workflow-runtime-contracts.json +879 -0
- package/oxe/workflows/research.md +8 -2
- package/oxe/workflows/retro.md +7 -2
- package/oxe/workflows/review-pr.md +12 -8
- package/oxe/workflows/route.md +16 -13
- package/oxe/workflows/security.md +3 -2
- package/oxe/workflows/session.md +44 -0
- package/oxe/workflows/skill.md +44 -0
- package/oxe/workflows/spec.md +21 -18
- package/oxe/workflows/ui-review.md +13 -7
- package/oxe/workflows/update.md +3 -1
- package/oxe/workflows/validate-gaps.md +12 -6
- package/oxe/workflows/verify-audit.md +73 -0
- package/oxe/workflows/verify.md +40 -16
- 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
|
+
};
|
package/bin/lib/oxe-security.cjs
CHANGED
|
@@ -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,
|