oxe-cc 1.4.0 → 1.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.
- package/.cursor/commands/oxe-ask.md +4 -2
- package/.cursor/commands/oxe-capabilities.md +4 -2
- package/.cursor/commands/oxe-checkpoint.md +4 -2
- package/.cursor/commands/oxe-compact.md +4 -2
- package/.cursor/commands/oxe-dashboard.md +4 -2
- package/.cursor/commands/oxe-debug.md +4 -2
- package/.cursor/commands/oxe-discuss.md +4 -2
- package/.cursor/commands/oxe-execute.md +5 -3
- package/.cursor/commands/oxe-forensics.md +4 -2
- package/.cursor/commands/oxe-help.md +4 -2
- package/.cursor/commands/oxe-loop.md +4 -2
- package/.cursor/commands/oxe-milestone.md +4 -2
- package/.cursor/commands/oxe-next.md +4 -2
- package/.cursor/commands/oxe-obs.md +4 -2
- package/.cursor/commands/oxe-plan-agent.md +4 -2
- package/.cursor/commands/oxe-plan.md +4 -2
- package/.cursor/commands/oxe-project.md +4 -2
- package/.cursor/commands/oxe-quick.md +4 -2
- package/.cursor/commands/oxe-research.md +4 -2
- package/.cursor/commands/oxe-retro.md +4 -2
- package/.cursor/commands/oxe-review-pr.md +4 -2
- package/.cursor/commands/oxe-route.md +4 -2
- package/.cursor/commands/oxe-scan.md +4 -2
- package/.cursor/commands/oxe-security.md +4 -2
- package/.cursor/commands/oxe-session.md +5 -3
- package/.cursor/commands/oxe-ship.md +4 -2
- package/.cursor/commands/oxe-skill.md +4 -2
- package/.cursor/commands/oxe-spec.md +4 -2
- package/.cursor/commands/oxe-ui-review.md +4 -2
- package/.cursor/commands/oxe-ui-spec.md +4 -2
- package/.cursor/commands/oxe-update.md +4 -2
- package/.cursor/commands/oxe-validate-gaps.md +4 -2
- package/.cursor/commands/oxe-verify-audit.md +46 -0
- package/.cursor/commands/oxe-verify.md +4 -2
- package/.cursor/commands/oxe-workflow-authoring.md +47 -0
- package/.cursor/commands/oxe-workstream.md +4 -2
- package/.cursor/commands/oxe.md +6 -2
- package/.github/prompts/oxe-ask.prompt.md +4 -2
- package/.github/prompts/oxe-capabilities.prompt.md +4 -2
- package/.github/prompts/oxe-checkpoint.prompt.md +4 -2
- package/.github/prompts/oxe-compact.prompt.md +5 -3
- package/.github/prompts/oxe-dashboard.prompt.md +4 -2
- package/.github/prompts/oxe-debug.prompt.md +4 -2
- package/.github/prompts/oxe-discuss.prompt.md +6 -2
- package/.github/prompts/oxe-execute.prompt.md +5 -3
- package/.github/prompts/oxe-forensics.prompt.md +4 -2
- package/.github/prompts/oxe-help.prompt.md +6 -2
- package/.github/prompts/oxe-loop.prompt.md +4 -2
- package/.github/prompts/oxe-milestone.prompt.md +4 -2
- package/.github/prompts/oxe-next.prompt.md +6 -2
- package/.github/prompts/oxe-obs.prompt.md +4 -2
- package/.github/prompts/oxe-plan-agent.prompt.md +5 -2
- package/.github/prompts/oxe-plan.prompt.md +4 -2
- package/.github/prompts/oxe-project.prompt.md +4 -2
- package/.github/prompts/oxe-quick.prompt.md +4 -2
- package/.github/prompts/oxe-research.prompt.md +4 -2
- package/.github/prompts/oxe-retro.prompt.md +4 -2
- package/.github/prompts/oxe-review-pr.prompt.md +4 -2
- package/.github/prompts/oxe-route.prompt.md +4 -2
- package/.github/prompts/oxe-scan.prompt.md +4 -2
- package/.github/prompts/oxe-security.prompt.md +4 -2
- package/.github/prompts/oxe-session.prompt.md +5 -3
- package/.github/prompts/oxe-ship.prompt.md +4 -2
- package/.github/prompts/oxe-skill.prompt.md +4 -2
- package/.github/prompts/oxe-spec.prompt.md +4 -2
- package/.github/prompts/oxe-ui-review.prompt.md +4 -2
- package/.github/prompts/oxe-ui-spec.prompt.md +4 -2
- package/.github/prompts/oxe-update.prompt.md +4 -2
- package/.github/prompts/oxe-validate-gaps.prompt.md +4 -2
- package/.github/prompts/oxe-verify-audit.prompt.md +46 -0
- package/.github/prompts/oxe-verify.prompt.md +4 -2
- package/.github/prompts/oxe-workflow-authoring.prompt.md +47 -0
- package/.github/prompts/oxe-workstream.prompt.md +4 -2
- package/.github/prompts/oxe.prompt.md +6 -2
- package/.github/workflows/ci.yml +57 -20
- package/.github/workflows/release.yml +94 -0
- package/AGENTS.md +3 -1
- package/CHANGELOG.md +383 -342
- package/QUICKSTART.md +99 -0
- package/README.md +89 -65
- package/bin/lib/oxe-agent-install.cjs +127 -107
- package/bin/lib/oxe-install-resolve.cjs +10 -0
- package/bin/lib/oxe-operational.cjs +34 -28
- package/bin/lib/oxe-project-health.cjs +38 -6
- package/bin/lib/oxe-release.cjs +423 -0
- package/bin/lib/oxe-runtime-semantics.cjs +68 -24
- package/bin/oxe-cc.js +388 -55
- package/commands/oxe/ask.md +7 -3
- package/commands/oxe/capabilities.md +6 -2
- package/commands/oxe/checkpoint.md +5 -1
- package/commands/oxe/compact.md +6 -2
- package/commands/oxe/dashboard.md +6 -2
- package/commands/oxe/debug.md +6 -2
- package/commands/oxe/discuss.md +6 -2
- package/commands/oxe/execute.md +6 -2
- package/commands/oxe/forensics.md +6 -2
- package/commands/oxe/help.md +6 -2
- package/commands/oxe/loop.md +6 -2
- package/commands/oxe/milestone.md +6 -2
- package/commands/oxe/next.md +6 -2
- package/commands/oxe/obs.md +6 -2
- package/commands/oxe/oxe.md +6 -2
- package/commands/oxe/plan-agent.md +6 -2
- package/commands/oxe/plan.md +6 -2
- package/commands/oxe/project.md +6 -2
- package/commands/oxe/quick.md +6 -2
- package/commands/oxe/research.md +6 -2
- package/commands/oxe/retro.md +6 -2
- package/commands/oxe/review-pr.md +6 -2
- package/commands/oxe/route.md +6 -2
- package/commands/oxe/scan.md +6 -2
- package/commands/oxe/security.md +6 -2
- package/commands/oxe/session.md +6 -2
- package/commands/oxe/ship.md +6 -2
- package/commands/oxe/skill.md +6 -2
- package/commands/oxe/spec.md +6 -2
- package/commands/oxe/ui-review.md +6 -2
- package/commands/oxe/ui-spec.md +6 -2
- package/commands/oxe/update.md +6 -2
- package/commands/oxe/validate-gaps.md +6 -2
- package/commands/oxe/verify-audit.md +50 -0
- package/commands/oxe/verify.md +6 -2
- package/commands/oxe/workflow-authoring.md +50 -0
- package/commands/oxe/workstream.md +6 -2
- package/docs/INCIDENT-PLAYBOOK.md +181 -0
- package/docs/RELEASE-READINESS.md +46 -0
- package/docs/ROLES.md +129 -0
- package/docs/RUNTIME-SMOKE-MATRIX.md +128 -0
- package/docs/TEAM-ADOPTION.md +153 -0
- package/docs/WALKTHROUGH.md +241 -0
- package/lib/runtime/scheduler/multi-agent-coordinator.d.ts +28 -0
- package/lib/runtime/scheduler/multi-agent-coordinator.js +152 -26
- package/lib/sdk/README.md +2 -0
- package/lib/sdk/index.cjs +22 -8
- package/lib/sdk/index.d.ts +60 -16
- package/oxe/templates/config.template.json +1 -0
- package/package.json +30 -22
- package/packages/runtime/package.json +1 -1
- package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +357 -193
- package/vscode-extension/oxe-agents-1.4.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.5.0.vsix +0 -0
- package/vscode-extension/package.json +1 -1
|
@@ -637,11 +637,11 @@ async function resolveRuntimeGate(projectRoot, activeSession, options = {}) {
|
|
|
637
637
|
};
|
|
638
638
|
}
|
|
639
639
|
|
|
640
|
-
function readRuntimeMultiAgentStatus(projectRoot, activeSession, options = {}) {
|
|
641
|
-
const runtime = loadRuntimeModule();
|
|
642
|
-
const current = readRunState(projectRoot, activeSession);
|
|
643
|
-
const runId = options.runId || (current && current.run_id) || null;
|
|
644
|
-
if (!runId) {
|
|
640
|
+
function readRuntimeMultiAgentStatus(projectRoot, activeSession, options = {}) {
|
|
641
|
+
const runtime = loadRuntimeModule();
|
|
642
|
+
const current = readRunState(projectRoot, activeSession);
|
|
643
|
+
const runId = options.runId || (current && current.run_id) || null;
|
|
644
|
+
if (!runId) {
|
|
645
645
|
return {
|
|
646
646
|
path: null,
|
|
647
647
|
enabled: false,
|
|
@@ -649,34 +649,40 @@ function readRuntimeMultiAgentStatus(projectRoot, activeSession, options = {}) {
|
|
|
649
649
|
mode: null,
|
|
650
650
|
workspaceIsolationEnforced: false,
|
|
651
651
|
agents: [],
|
|
652
|
-
ownership: [],
|
|
653
|
-
orphanReassignments: [],
|
|
654
|
-
handoffs: [],
|
|
655
|
-
arbitrationResults: [],
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
const
|
|
660
|
-
const
|
|
661
|
-
const
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
652
|
+
ownership: [],
|
|
653
|
+
orphanReassignments: [],
|
|
654
|
+
handoffs: [],
|
|
655
|
+
arbitrationResults: [],
|
|
656
|
+
summary: null,
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
const runDir = path.join(projectRoot, '.oxe', 'runs', runId);
|
|
660
|
+
const statePath = path.join(runDir, 'multi-agent-state.json');
|
|
661
|
+
const summaryPath = path.join(runDir, 'multi-agent-summary.json');
|
|
662
|
+
const handoffsPath = path.join(runDir, 'handoffs.json');
|
|
663
|
+
const arbitrationPath = path.join(runDir, 'arbitration-results.json');
|
|
664
|
+
const state = runtime && typeof runtime.loadMultiAgentState === 'function'
|
|
665
|
+
? runtime.loadMultiAgentState(projectRoot, runId)
|
|
666
|
+
: readJsonIfExists(statePath);
|
|
667
|
+
const summary = runtime && typeof runtime.loadMultiAgentSummary === 'function'
|
|
668
|
+
? runtime.loadMultiAgentSummary(projectRoot, runId)
|
|
669
|
+
: readJsonIfExists(summaryPath);
|
|
670
|
+
const handoffs = readJsonIfExists(handoffsPath);
|
|
671
|
+
const arbitrationResults = readJsonIfExists(arbitrationPath);
|
|
672
|
+
return {
|
|
673
|
+
path: statePath,
|
|
674
|
+
enabled: Boolean(state),
|
|
670
675
|
runId,
|
|
671
676
|
mode: state && state.mode ? state.mode : null,
|
|
672
677
|
workspaceIsolationEnforced: Boolean(state && state.workspace_isolation_enforced),
|
|
673
678
|
agents: state && Array.isArray(state.agent_results) ? state.agent_results : [],
|
|
674
679
|
ownership: state && Array.isArray(state.ownership) ? state.ownership : [],
|
|
675
|
-
orphanReassignments: state && Array.isArray(state.orphan_reassignments) ? state.orphan_reassignments : [],
|
|
676
|
-
handoffs: Array.isArray(handoffs) ? handoffs : [],
|
|
677
|
-
arbitrationResults: Array.isArray(arbitrationResults) ? arbitrationResults : [],
|
|
678
|
-
|
|
679
|
-
}
|
|
680
|
+
orphanReassignments: state && Array.isArray(state.orphan_reassignments) ? state.orphan_reassignments : [],
|
|
681
|
+
handoffs: Array.isArray(handoffs) ? handoffs : [],
|
|
682
|
+
arbitrationResults: Array.isArray(arbitrationResults) ? arbitrationResults : [],
|
|
683
|
+
summary: summary || null,
|
|
684
|
+
};
|
|
685
|
+
}
|
|
680
686
|
|
|
681
687
|
function loadRuntimeVerificationArtifacts(projectRoot, runState) {
|
|
682
688
|
const runtime = loadRuntimeModule();
|
|
@@ -24,6 +24,7 @@ const ALLOWED_CONFIG_KEYS = [
|
|
|
24
24
|
'verification_depth',
|
|
25
25
|
'plan_confidence_threshold',
|
|
26
26
|
'security_in_verify',
|
|
27
|
+
'adversarial_verify',
|
|
27
28
|
'install',
|
|
28
29
|
'plugins',
|
|
29
30
|
'workstreams',
|
|
@@ -52,7 +53,7 @@ const INSTALL_PROFILES = ['recommended', 'cursor', 'copilot', 'core', 'cli', 'al
|
|
|
52
53
|
const INSTALL_REPO_LAYOUTS = ['nested', 'classic'];
|
|
53
54
|
|
|
54
55
|
/** @type {string[]} */
|
|
55
|
-
const INSTALL_OBJECT_KEYS = ['profile', 'repo_layout', 'vscode', 'include_commands_dir', 'include_agents_md'];
|
|
56
|
+
const INSTALL_OBJECT_KEYS = ['profile', 'repo_layout', 'ide_scope', 'vscode', 'include_commands_dir', 'include_agents_md'];
|
|
56
57
|
|
|
57
58
|
const EXPECTED_CODEBASE_MAPS = [
|
|
58
59
|
'OVERVIEW.md',
|
|
@@ -152,6 +153,8 @@ function loadOxeConfigMerged(targetProject) {
|
|
|
152
153
|
scan_ignore_globs: [],
|
|
153
154
|
spec_required_sections: [],
|
|
154
155
|
plan_max_tasks_per_wave: 0,
|
|
156
|
+
lessons_max_age_days: 0,
|
|
157
|
+
install: {},
|
|
155
158
|
azure: {
|
|
156
159
|
enabled: false,
|
|
157
160
|
default_resource_group: '',
|
|
@@ -212,15 +215,18 @@ function loadOxeConfigMerged(targetProject) {
|
|
|
212
215
|
if (typeof layer.profile === 'string') {
|
|
213
216
|
Object.assign(merged, expandExecutionProfile(layer.profile));
|
|
214
217
|
}
|
|
218
|
+
const layerFlat = { ...layer };
|
|
215
219
|
// Azure: merge aninhado para não sobrescrever campos não especificados
|
|
216
220
|
if (layer.azure && typeof layer.azure === 'object' && !Array.isArray(layer.azure)) {
|
|
217
221
|
merged.azure = { .../** @type {any} */ (merged.azure), ...layer.azure };
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
222
|
+
delete layerFlat.azure;
|
|
223
|
+
}
|
|
224
|
+
// Install: merge aninhado para não sobrescrever campos não especificados
|
|
225
|
+
if (layer.install && typeof layer.install === 'object' && !Array.isArray(layer.install)) {
|
|
226
|
+
merged.install = { .../** @type {any} */ (merged.install || {}), ...layer.install };
|
|
227
|
+
delete layerFlat.install;
|
|
223
228
|
}
|
|
229
|
+
Object.assign(merged, layerFlat);
|
|
224
230
|
}
|
|
225
231
|
|
|
226
232
|
const primaryPath = sources.project || sources.user || sources.system || null;
|
|
@@ -262,6 +268,11 @@ function validateConfigShape(cfg) {
|
|
|
262
268
|
);
|
|
263
269
|
}
|
|
264
270
|
}
|
|
271
|
+
if (inst.ide_scope != null) {
|
|
272
|
+
if (typeof inst.ide_scope !== 'string' || !['global', 'local'].includes(inst.ide_scope)) {
|
|
273
|
+
typeErrors.push('install.ide_scope deve ser "global" ou "local"');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
265
276
|
if (inst.vscode != null && typeof inst.vscode !== 'boolean') {
|
|
266
277
|
typeErrors.push('install.vscode deve ser boolean');
|
|
267
278
|
}
|
|
@@ -314,6 +325,27 @@ function validateConfigShape(cfg) {
|
|
|
314
325
|
if (cfg.scale_adaptive != null && typeof cfg.scale_adaptive !== 'boolean') {
|
|
315
326
|
typeErrors.push('scale_adaptive deve ser boolean');
|
|
316
327
|
}
|
|
328
|
+
if (cfg.discuss_before_plan != null && typeof cfg.discuss_before_plan !== 'boolean') {
|
|
329
|
+
typeErrors.push('discuss_before_plan deve ser boolean');
|
|
330
|
+
}
|
|
331
|
+
if (cfg.after_verify_suggest_pr != null && typeof cfg.after_verify_suggest_pr !== 'boolean') {
|
|
332
|
+
typeErrors.push('after_verify_suggest_pr deve ser boolean');
|
|
333
|
+
}
|
|
334
|
+
if (cfg.after_verify_draft_commit != null && typeof cfg.after_verify_draft_commit !== 'boolean') {
|
|
335
|
+
typeErrors.push('after_verify_draft_commit deve ser boolean');
|
|
336
|
+
}
|
|
337
|
+
if (cfg.security_in_verify != null && typeof cfg.security_in_verify !== 'boolean') {
|
|
338
|
+
typeErrors.push('security_in_verify deve ser boolean');
|
|
339
|
+
}
|
|
340
|
+
if (cfg.adversarial_verify != null && typeof cfg.adversarial_verify !== 'boolean') {
|
|
341
|
+
typeErrors.push('adversarial_verify deve ser boolean');
|
|
342
|
+
}
|
|
343
|
+
if (cfg.lessons_max_age_days != null && typeof cfg.lessons_max_age_days !== 'number') {
|
|
344
|
+
typeErrors.push('lessons_max_age_days deve ser número (use 0 para desligar)');
|
|
345
|
+
}
|
|
346
|
+
if (cfg.default_verify_command != null && typeof cfg.default_verify_command !== 'string') {
|
|
347
|
+
typeErrors.push('default_verify_command deve ser string');
|
|
348
|
+
}
|
|
317
349
|
if (cfg.azure != null) {
|
|
318
350
|
if (typeof cfg.azure !== 'object' || Array.isArray(cfg.azure)) {
|
|
319
351
|
typeErrors.push('azure deve ser um objeto');
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawnSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const oxeManifest = require('./oxe-manifest.cjs');
|
|
8
|
+
|
|
9
|
+
const REQUIRED_RUNTIMES = [
|
|
10
|
+
'cursor',
|
|
11
|
+
'copilot_vscode',
|
|
12
|
+
'claude_code',
|
|
13
|
+
'codex',
|
|
14
|
+
'opencode',
|
|
15
|
+
'gemini',
|
|
16
|
+
'windsurf',
|
|
17
|
+
'antigravity',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const WRAPPER_TARGETS = [
|
|
21
|
+
{
|
|
22
|
+
key: '.github/prompts',
|
|
23
|
+
dir: '.github/prompts',
|
|
24
|
+
filter: (name) => (name === 'oxe.prompt.md' || name.startsWith('oxe-')) && name.endsWith('.prompt.md'),
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
key: 'commands/oxe',
|
|
28
|
+
dir: 'commands/oxe',
|
|
29
|
+
filter: (name) => name.endsWith('.md'),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
key: '.cursor/commands',
|
|
33
|
+
dir: '.cursor/commands',
|
|
34
|
+
filter: (name) => (name === 'oxe.md' || name.startsWith('oxe-')) && name.endsWith('.md'),
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
function releasePaths(projectRoot) {
|
|
39
|
+
const releaseDir = path.join(projectRoot, '.oxe', 'release');
|
|
40
|
+
return {
|
|
41
|
+
releaseDir,
|
|
42
|
+
manifest: path.join(releaseDir, 'release-manifest.json'),
|
|
43
|
+
smokeReport: path.join(releaseDir, 'runtime-smoke-report.json'),
|
|
44
|
+
recoveryFixtureReport: path.join(releaseDir, 'recovery-fixture-report.json'),
|
|
45
|
+
multiAgentSoakReport: path.join(releaseDir, 'multi-agent-soak-report.json'),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function ensureDirForFile(filePath) {
|
|
50
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function readJsonIfExists(filePath) {
|
|
54
|
+
try {
|
|
55
|
+
if (!fs.existsSync(filePath)) return null;
|
|
56
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readTextIfExists(filePath) {
|
|
63
|
+
try {
|
|
64
|
+
if (!fs.existsSync(filePath)) return null;
|
|
65
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function readPackageVersion(filePath) {
|
|
72
|
+
const json = readJsonIfExists(filePath);
|
|
73
|
+
if (!json || typeof json.version !== 'string') return null;
|
|
74
|
+
return json.version.trim();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function readReadmeVersion(filePath) {
|
|
78
|
+
const text = readTextIfExists(filePath);
|
|
79
|
+
if (!text) return null;
|
|
80
|
+
const match = text.match(/\*\*Versão:\*\*\s*`([^`]+)`/i);
|
|
81
|
+
return match ? match[1].trim() : null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function parseChangelogTop(filePath) {
|
|
85
|
+
const text = readTextIfExists(filePath);
|
|
86
|
+
if (!text) {
|
|
87
|
+
return {
|
|
88
|
+
path: filePath,
|
|
89
|
+
version: null,
|
|
90
|
+
date: null,
|
|
91
|
+
headerLine: null,
|
|
92
|
+
hasHighlights: false,
|
|
93
|
+
highlights: [],
|
|
94
|
+
ok: false,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const match = text.match(/^## \[([^\]]+)\]\s+—\s+(\d{4}-\d{2}-\d{2})\s*$/m);
|
|
98
|
+
if (!match) {
|
|
99
|
+
return {
|
|
100
|
+
path: filePath,
|
|
101
|
+
version: null,
|
|
102
|
+
date: null,
|
|
103
|
+
headerLine: null,
|
|
104
|
+
hasHighlights: false,
|
|
105
|
+
highlights: [],
|
|
106
|
+
ok: false,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const headerLine = match[0];
|
|
110
|
+
const start = match.index + headerLine.length;
|
|
111
|
+
const nextHeader = text.slice(start).match(/\n## \[/);
|
|
112
|
+
const section = nextHeader ? text.slice(start, start + nextHeader.index) : text.slice(start);
|
|
113
|
+
const highlights = section
|
|
114
|
+
.split(/\r?\n/)
|
|
115
|
+
.map((line) => line.trim())
|
|
116
|
+
.filter((line) => /^-\s+\S+/.test(line));
|
|
117
|
+
return {
|
|
118
|
+
path: filePath,
|
|
119
|
+
version: match[1].trim(),
|
|
120
|
+
date: match[2].trim(),
|
|
121
|
+
headerLine,
|
|
122
|
+
hasHighlights: highlights.length > 0,
|
|
123
|
+
highlights,
|
|
124
|
+
ok: true,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function parseBannerVersion(filePath, fallbackVersion) {
|
|
129
|
+
const text = readTextIfExists(filePath);
|
|
130
|
+
if (!text) return { version: null, mode: 'missing' };
|
|
131
|
+
if (text.includes('v{version}')) return { version: fallbackVersion || null, mode: 'placeholder' };
|
|
132
|
+
const match = text.match(/v(\d+\.\d+\.\d+)/);
|
|
133
|
+
return { version: match ? match[1] : null, mode: match ? 'fixed' : 'unknown' };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function hashObject(value) {
|
|
137
|
+
const json = JSON.stringify(value, null, 2);
|
|
138
|
+
const tempDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'oxe-release-hash-'));
|
|
139
|
+
const tempFile = path.join(tempDir, 'payload.json');
|
|
140
|
+
fs.writeFileSync(tempFile, json, 'utf8');
|
|
141
|
+
const hash = oxeManifest.sha256File(tempFile);
|
|
142
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
143
|
+
return hash;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function collectWrapperHashes(projectRoot) {
|
|
147
|
+
const wrappers = {};
|
|
148
|
+
for (const target of WRAPPER_TARGETS) {
|
|
149
|
+
const dir = path.join(projectRoot, target.dir);
|
|
150
|
+
if (!fs.existsSync(dir)) {
|
|
151
|
+
wrappers[target.key] = {
|
|
152
|
+
dir: target.dir,
|
|
153
|
+
missing: true,
|
|
154
|
+
fileCount: 0,
|
|
155
|
+
aggregateHash: null,
|
|
156
|
+
files: [],
|
|
157
|
+
};
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const files = fs.readdirSync(dir)
|
|
161
|
+
.filter((name) => target.filter(name))
|
|
162
|
+
.sort()
|
|
163
|
+
.map((name) => {
|
|
164
|
+
const filePath = path.join(dir, name);
|
|
165
|
+
return {
|
|
166
|
+
path: path.relative(projectRoot, filePath).replace(/\\/g, '/'),
|
|
167
|
+
hash: oxeManifest.sha256File(filePath),
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
wrappers[target.key] = {
|
|
171
|
+
dir: target.dir,
|
|
172
|
+
missing: false,
|
|
173
|
+
fileCount: files.length,
|
|
174
|
+
aggregateHash: hashObject(files),
|
|
175
|
+
files,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return wrappers;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function diffWrapperHashes(before, after) {
|
|
182
|
+
const mismatches = [];
|
|
183
|
+
const keys = new Set([...Object.keys(before || {}), ...Object.keys(after || {})]);
|
|
184
|
+
for (const key of keys) {
|
|
185
|
+
const a = before[key] || null;
|
|
186
|
+
const b = after[key] || null;
|
|
187
|
+
if (!a || !b || a.aggregateHash !== b.aggregateHash || a.fileCount !== b.fileCount) {
|
|
188
|
+
mismatches.push({
|
|
189
|
+
target: key,
|
|
190
|
+
before: a,
|
|
191
|
+
after: b,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return mismatches;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function runSyncScript(packageRoot, scriptName, projectRoot) {
|
|
199
|
+
const scriptPath = path.join(packageRoot, 'scripts', scriptName);
|
|
200
|
+
const result = spawnSync(process.execPath, [scriptPath], {
|
|
201
|
+
cwd: packageRoot,
|
|
202
|
+
encoding: 'utf8',
|
|
203
|
+
env: {
|
|
204
|
+
...process.env,
|
|
205
|
+
OXE_SYNC_REPO_ROOT: projectRoot,
|
|
206
|
+
OXE_NO_BANNER: '1',
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
return {
|
|
210
|
+
script: scriptName,
|
|
211
|
+
status: result.status,
|
|
212
|
+
ok: result.status === 0,
|
|
213
|
+
stdout: String(result.stdout || ''),
|
|
214
|
+
stderr: String(result.stderr || ''),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function syncWrappers(projectRoot, packageRoot) {
|
|
219
|
+
const before = collectWrapperHashes(projectRoot);
|
|
220
|
+
const syncRuntime = runSyncScript(packageRoot, 'sync-runtime-metadata.cjs', projectRoot);
|
|
221
|
+
const syncCursor = runSyncScript(packageRoot, 'sync-cursor-from-prompts.cjs', projectRoot);
|
|
222
|
+
const after = collectWrapperHashes(projectRoot);
|
|
223
|
+
const mismatches = diffWrapperHashes(before, after);
|
|
224
|
+
return {
|
|
225
|
+
before,
|
|
226
|
+
after,
|
|
227
|
+
scripts: [syncRuntime, syncCursor],
|
|
228
|
+
mismatches,
|
|
229
|
+
ok: syncRuntime.ok && syncCursor.ok && mismatches.length === 0,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function readReportSummary(reportPath, requiredItems, extractOk) {
|
|
234
|
+
const data = readJsonIfExists(reportPath);
|
|
235
|
+
if (!data || !Array.isArray(data.results)) {
|
|
236
|
+
return {
|
|
237
|
+
path: reportPath,
|
|
238
|
+
present: false,
|
|
239
|
+
ok: false,
|
|
240
|
+
total: 0,
|
|
241
|
+
failures: ['report_missing'],
|
|
242
|
+
missingRequired: Array.isArray(requiredItems) ? requiredItems.slice() : [],
|
|
243
|
+
results: [],
|
|
244
|
+
raw: data,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
const results = data.results;
|
|
248
|
+
const failures = results.filter((item) => !extractOk(item)).map((item) => item.runtime || item.fixture || item.scenario || item.name || 'unknown');
|
|
249
|
+
const missingRequired = Array.isArray(requiredItems)
|
|
250
|
+
? requiredItems.filter((name) => !results.some((item) => (item.runtime || item.fixture || item.scenario || item.name) === name))
|
|
251
|
+
: [];
|
|
252
|
+
return {
|
|
253
|
+
path: reportPath,
|
|
254
|
+
present: true,
|
|
255
|
+
ok: failures.length === 0 && missingRequired.length === 0,
|
|
256
|
+
total: results.length,
|
|
257
|
+
failures,
|
|
258
|
+
missingRequired,
|
|
259
|
+
results,
|
|
260
|
+
raw: data,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function loadRuntimeSmokeReport(projectRoot) {
|
|
265
|
+
return readReportSummary(releasePaths(projectRoot).smokeReport, REQUIRED_RUNTIMES, (item) => {
|
|
266
|
+
return Boolean(
|
|
267
|
+
item
|
|
268
|
+
&& item.install_ok
|
|
269
|
+
&& item.oxe_present
|
|
270
|
+
&& item.workflow_resolution_ok
|
|
271
|
+
&& item.wrapper_drift_ok !== false
|
|
272
|
+
&& item.uninstall_ok
|
|
273
|
+
);
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function loadRecoveryFixtureReport(projectRoot) {
|
|
278
|
+
return readReportSummary(releasePaths(projectRoot).recoveryFixtureReport, null, (item) => Boolean(item && item.ok));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function loadMultiAgentSoakReport(projectRoot) {
|
|
282
|
+
return readReportSummary(releasePaths(projectRoot).multiAgentSoakReport, null, (item) => Boolean(item && item.ok));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function readVersionSnapshot(projectRoot) {
|
|
286
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
287
|
+
const runtimePackagePath = path.join(projectRoot, 'packages', 'runtime', 'package.json');
|
|
288
|
+
const vscodePackagePath = path.join(projectRoot, 'vscode-extension', 'package.json');
|
|
289
|
+
const readmePath = path.join(projectRoot, 'README.md');
|
|
290
|
+
const changelogPath = path.join(projectRoot, 'CHANGELOG.md');
|
|
291
|
+
const bannerPath = path.join(projectRoot, 'bin', 'banner.txt');
|
|
292
|
+
const rootVersion = readPackageVersion(packageJsonPath);
|
|
293
|
+
const changelog = parseChangelogTop(changelogPath);
|
|
294
|
+
return {
|
|
295
|
+
rootPackage: { path: packageJsonPath, version: rootVersion },
|
|
296
|
+
runtimePackage: { path: runtimePackagePath, version: readPackageVersion(runtimePackagePath) },
|
|
297
|
+
vscodeExtension: { path: vscodePackagePath, version: readPackageVersion(vscodePackagePath) },
|
|
298
|
+
readme: { path: readmePath, version: readReadmeVersion(readmePath) },
|
|
299
|
+
changelog,
|
|
300
|
+
banner: { path: bannerPath, ...parseBannerVersion(bannerPath, rootVersion) },
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function buildReleaseManifest(projectRoot, options = {}) {
|
|
305
|
+
const packageRoot = path.resolve(options.packageRoot || projectRoot);
|
|
306
|
+
const paths = releasePaths(projectRoot);
|
|
307
|
+
const versions = readVersionSnapshot(projectRoot);
|
|
308
|
+
const runtimeEntry = path.join(packageRoot, 'lib', 'runtime', 'index.js');
|
|
309
|
+
const wrapperSync = options.skipWrapperSync ? {
|
|
310
|
+
before: collectWrapperHashes(projectRoot),
|
|
311
|
+
after: collectWrapperHashes(projectRoot),
|
|
312
|
+
scripts: [],
|
|
313
|
+
mismatches: [],
|
|
314
|
+
ok: true,
|
|
315
|
+
} : syncWrappers(projectRoot, packageRoot);
|
|
316
|
+
const smoke = loadRuntimeSmokeReport(projectRoot);
|
|
317
|
+
const recovery = loadRecoveryFixtureReport(projectRoot);
|
|
318
|
+
const multiAgent = loadMultiAgentSoakReport(projectRoot);
|
|
319
|
+
const manifest = {
|
|
320
|
+
schema_version: 1,
|
|
321
|
+
generated_at: new Date().toISOString(),
|
|
322
|
+
project_root: projectRoot,
|
|
323
|
+
package_root: packageRoot,
|
|
324
|
+
release_contract: {
|
|
325
|
+
execute_verify: 'runtime-first',
|
|
326
|
+
promotion_target: 'pr_draft',
|
|
327
|
+
multi_agent_backend: 'git_worktree',
|
|
328
|
+
branch_push: 'advanced_only',
|
|
329
|
+
},
|
|
330
|
+
versions,
|
|
331
|
+
runtime_compiled: {
|
|
332
|
+
path: runtimeEntry,
|
|
333
|
+
ok: fs.existsSync(runtimeEntry),
|
|
334
|
+
},
|
|
335
|
+
wrappers: {
|
|
336
|
+
hash_before_sync: wrapperSync.before,
|
|
337
|
+
hash_after_sync: wrapperSync.after,
|
|
338
|
+
sync: {
|
|
339
|
+
ok: wrapperSync.ok,
|
|
340
|
+
mismatches: wrapperSync.mismatches,
|
|
341
|
+
scripts: wrapperSync.scripts,
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
reports: {
|
|
345
|
+
runtime_smoke: smoke,
|
|
346
|
+
recovery_fixtures: recovery,
|
|
347
|
+
multi_agent_soak: multiAgent,
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
if (options.writeManifest) {
|
|
351
|
+
ensureDirForFile(paths.manifest);
|
|
352
|
+
fs.writeFileSync(paths.manifest, JSON.stringify(manifest, null, 2) + '\n', 'utf8');
|
|
353
|
+
}
|
|
354
|
+
return manifest;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function checkReleaseConsistency(projectRoot, options = {}) {
|
|
358
|
+
const manifest = buildReleaseManifest(projectRoot, options);
|
|
359
|
+
const blockers = [];
|
|
360
|
+
const warnings = [];
|
|
361
|
+
const versions = manifest.versions;
|
|
362
|
+
const canonicalVersion = versions.rootPackage.version;
|
|
363
|
+
const compareTargets = [
|
|
364
|
+
['packages/runtime/package.json', versions.runtimePackage.version],
|
|
365
|
+
['vscode-extension/package.json', versions.vscodeExtension.version],
|
|
366
|
+
['README.md', versions.readme.version],
|
|
367
|
+
['CHANGELOG.md', versions.changelog.version],
|
|
368
|
+
['bin/banner.txt', versions.banner.version],
|
|
369
|
+
];
|
|
370
|
+
for (const [label, version] of compareTargets) {
|
|
371
|
+
if (!version || version !== canonicalVersion) {
|
|
372
|
+
blockers.push(`${label} diverge da versão raiz (${canonicalVersion || 'ausente'})`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (!versions.changelog.ok) {
|
|
376
|
+
blockers.push('CHANGELOG.md sem cabeçalho de versão no topo');
|
|
377
|
+
} else {
|
|
378
|
+
if (!versions.changelog.date) blockers.push('CHANGELOG.md topo sem data');
|
|
379
|
+
if (!versions.changelog.hasHighlights) blockers.push('CHANGELOG.md topo sem highlights');
|
|
380
|
+
}
|
|
381
|
+
if (!manifest.runtime_compiled.ok) {
|
|
382
|
+
blockers.push('runtime não compilado (lib/runtime/index.js ausente)');
|
|
383
|
+
}
|
|
384
|
+
if (!manifest.wrappers.sync.ok) {
|
|
385
|
+
if (manifest.wrappers.sync.scripts.some((entry) => !entry.ok)) {
|
|
386
|
+
blockers.push('sync de wrappers falhou');
|
|
387
|
+
}
|
|
388
|
+
if (manifest.wrappers.sync.mismatches.length > 0) {
|
|
389
|
+
blockers.push('wrappers ficam dirty após sync-runtime-metadata/sync:cursor');
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (!manifest.reports.runtime_smoke.present || !manifest.reports.runtime_smoke.ok) {
|
|
393
|
+
blockers.push('runtime smoke matrix incompleta ou com falhas');
|
|
394
|
+
}
|
|
395
|
+
if (!manifest.reports.recovery_fixtures.present || !manifest.reports.recovery_fixtures.ok) {
|
|
396
|
+
blockers.push('recovery fixture report incompleto ou com falhas');
|
|
397
|
+
}
|
|
398
|
+
if (!manifest.reports.multi_agent_soak.present || !manifest.reports.multi_agent_soak.ok) {
|
|
399
|
+
blockers.push('multi-agent soak report incompleto ou com falhas');
|
|
400
|
+
}
|
|
401
|
+
if (versions.banner.mode === 'unknown') {
|
|
402
|
+
warnings.push('banner.txt sem placeholder v{version} nem versão fixa detectável');
|
|
403
|
+
}
|
|
404
|
+
return {
|
|
405
|
+
ok: blockers.length === 0,
|
|
406
|
+
blockers,
|
|
407
|
+
warnings,
|
|
408
|
+
manifest,
|
|
409
|
+
manifestPath: releasePaths(projectRoot).manifest,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
module.exports = {
|
|
414
|
+
REQUIRED_RUNTIMES,
|
|
415
|
+
WRAPPER_TARGETS,
|
|
416
|
+
releasePaths,
|
|
417
|
+
collectWrapperHashes,
|
|
418
|
+
loadRuntimeSmokeReport,
|
|
419
|
+
loadRecoveryFixtureReport,
|
|
420
|
+
loadMultiAgentSoakReport,
|
|
421
|
+
buildReleaseManifest,
|
|
422
|
+
checkReleaseConsistency,
|
|
423
|
+
};
|