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.
Files changed (142) hide show
  1. package/.cursor/commands/oxe-ask.md +4 -2
  2. package/.cursor/commands/oxe-capabilities.md +4 -2
  3. package/.cursor/commands/oxe-checkpoint.md +4 -2
  4. package/.cursor/commands/oxe-compact.md +4 -2
  5. package/.cursor/commands/oxe-dashboard.md +4 -2
  6. package/.cursor/commands/oxe-debug.md +4 -2
  7. package/.cursor/commands/oxe-discuss.md +4 -2
  8. package/.cursor/commands/oxe-execute.md +5 -3
  9. package/.cursor/commands/oxe-forensics.md +4 -2
  10. package/.cursor/commands/oxe-help.md +4 -2
  11. package/.cursor/commands/oxe-loop.md +4 -2
  12. package/.cursor/commands/oxe-milestone.md +4 -2
  13. package/.cursor/commands/oxe-next.md +4 -2
  14. package/.cursor/commands/oxe-obs.md +4 -2
  15. package/.cursor/commands/oxe-plan-agent.md +4 -2
  16. package/.cursor/commands/oxe-plan.md +4 -2
  17. package/.cursor/commands/oxe-project.md +4 -2
  18. package/.cursor/commands/oxe-quick.md +4 -2
  19. package/.cursor/commands/oxe-research.md +4 -2
  20. package/.cursor/commands/oxe-retro.md +4 -2
  21. package/.cursor/commands/oxe-review-pr.md +4 -2
  22. package/.cursor/commands/oxe-route.md +4 -2
  23. package/.cursor/commands/oxe-scan.md +4 -2
  24. package/.cursor/commands/oxe-security.md +4 -2
  25. package/.cursor/commands/oxe-session.md +5 -3
  26. package/.cursor/commands/oxe-ship.md +4 -2
  27. package/.cursor/commands/oxe-skill.md +4 -2
  28. package/.cursor/commands/oxe-spec.md +4 -2
  29. package/.cursor/commands/oxe-ui-review.md +4 -2
  30. package/.cursor/commands/oxe-ui-spec.md +4 -2
  31. package/.cursor/commands/oxe-update.md +4 -2
  32. package/.cursor/commands/oxe-validate-gaps.md +4 -2
  33. package/.cursor/commands/oxe-verify-audit.md +46 -0
  34. package/.cursor/commands/oxe-verify.md +4 -2
  35. package/.cursor/commands/oxe-workflow-authoring.md +47 -0
  36. package/.cursor/commands/oxe-workstream.md +4 -2
  37. package/.cursor/commands/oxe.md +6 -2
  38. package/.github/prompts/oxe-ask.prompt.md +4 -2
  39. package/.github/prompts/oxe-capabilities.prompt.md +4 -2
  40. package/.github/prompts/oxe-checkpoint.prompt.md +4 -2
  41. package/.github/prompts/oxe-compact.prompt.md +5 -3
  42. package/.github/prompts/oxe-dashboard.prompt.md +4 -2
  43. package/.github/prompts/oxe-debug.prompt.md +4 -2
  44. package/.github/prompts/oxe-discuss.prompt.md +6 -2
  45. package/.github/prompts/oxe-execute.prompt.md +5 -3
  46. package/.github/prompts/oxe-forensics.prompt.md +4 -2
  47. package/.github/prompts/oxe-help.prompt.md +6 -2
  48. package/.github/prompts/oxe-loop.prompt.md +4 -2
  49. package/.github/prompts/oxe-milestone.prompt.md +4 -2
  50. package/.github/prompts/oxe-next.prompt.md +6 -2
  51. package/.github/prompts/oxe-obs.prompt.md +4 -2
  52. package/.github/prompts/oxe-plan-agent.prompt.md +5 -2
  53. package/.github/prompts/oxe-plan.prompt.md +4 -2
  54. package/.github/prompts/oxe-project.prompt.md +4 -2
  55. package/.github/prompts/oxe-quick.prompt.md +4 -2
  56. package/.github/prompts/oxe-research.prompt.md +4 -2
  57. package/.github/prompts/oxe-retro.prompt.md +4 -2
  58. package/.github/prompts/oxe-review-pr.prompt.md +4 -2
  59. package/.github/prompts/oxe-route.prompt.md +4 -2
  60. package/.github/prompts/oxe-scan.prompt.md +4 -2
  61. package/.github/prompts/oxe-security.prompt.md +4 -2
  62. package/.github/prompts/oxe-session.prompt.md +5 -3
  63. package/.github/prompts/oxe-ship.prompt.md +4 -2
  64. package/.github/prompts/oxe-skill.prompt.md +4 -2
  65. package/.github/prompts/oxe-spec.prompt.md +4 -2
  66. package/.github/prompts/oxe-ui-review.prompt.md +4 -2
  67. package/.github/prompts/oxe-ui-spec.prompt.md +4 -2
  68. package/.github/prompts/oxe-update.prompt.md +4 -2
  69. package/.github/prompts/oxe-validate-gaps.prompt.md +4 -2
  70. package/.github/prompts/oxe-verify-audit.prompt.md +46 -0
  71. package/.github/prompts/oxe-verify.prompt.md +4 -2
  72. package/.github/prompts/oxe-workflow-authoring.prompt.md +47 -0
  73. package/.github/prompts/oxe-workstream.prompt.md +4 -2
  74. package/.github/prompts/oxe.prompt.md +6 -2
  75. package/.github/workflows/ci.yml +57 -20
  76. package/.github/workflows/release.yml +94 -0
  77. package/AGENTS.md +3 -1
  78. package/CHANGELOG.md +383 -342
  79. package/QUICKSTART.md +99 -0
  80. package/README.md +89 -65
  81. package/bin/lib/oxe-agent-install.cjs +127 -107
  82. package/bin/lib/oxe-install-resolve.cjs +10 -0
  83. package/bin/lib/oxe-operational.cjs +34 -28
  84. package/bin/lib/oxe-project-health.cjs +38 -6
  85. package/bin/lib/oxe-release.cjs +423 -0
  86. package/bin/lib/oxe-runtime-semantics.cjs +68 -24
  87. package/bin/oxe-cc.js +388 -55
  88. package/commands/oxe/ask.md +7 -3
  89. package/commands/oxe/capabilities.md +6 -2
  90. package/commands/oxe/checkpoint.md +5 -1
  91. package/commands/oxe/compact.md +6 -2
  92. package/commands/oxe/dashboard.md +6 -2
  93. package/commands/oxe/debug.md +6 -2
  94. package/commands/oxe/discuss.md +6 -2
  95. package/commands/oxe/execute.md +6 -2
  96. package/commands/oxe/forensics.md +6 -2
  97. package/commands/oxe/help.md +6 -2
  98. package/commands/oxe/loop.md +6 -2
  99. package/commands/oxe/milestone.md +6 -2
  100. package/commands/oxe/next.md +6 -2
  101. package/commands/oxe/obs.md +6 -2
  102. package/commands/oxe/oxe.md +6 -2
  103. package/commands/oxe/plan-agent.md +6 -2
  104. package/commands/oxe/plan.md +6 -2
  105. package/commands/oxe/project.md +6 -2
  106. package/commands/oxe/quick.md +6 -2
  107. package/commands/oxe/research.md +6 -2
  108. package/commands/oxe/retro.md +6 -2
  109. package/commands/oxe/review-pr.md +6 -2
  110. package/commands/oxe/route.md +6 -2
  111. package/commands/oxe/scan.md +6 -2
  112. package/commands/oxe/security.md +6 -2
  113. package/commands/oxe/session.md +6 -2
  114. package/commands/oxe/ship.md +6 -2
  115. package/commands/oxe/skill.md +6 -2
  116. package/commands/oxe/spec.md +6 -2
  117. package/commands/oxe/ui-review.md +6 -2
  118. package/commands/oxe/ui-spec.md +6 -2
  119. package/commands/oxe/update.md +6 -2
  120. package/commands/oxe/validate-gaps.md +6 -2
  121. package/commands/oxe/verify-audit.md +50 -0
  122. package/commands/oxe/verify.md +6 -2
  123. package/commands/oxe/workflow-authoring.md +50 -0
  124. package/commands/oxe/workstream.md +6 -2
  125. package/docs/INCIDENT-PLAYBOOK.md +181 -0
  126. package/docs/RELEASE-READINESS.md +46 -0
  127. package/docs/ROLES.md +129 -0
  128. package/docs/RUNTIME-SMOKE-MATRIX.md +128 -0
  129. package/docs/TEAM-ADOPTION.md +153 -0
  130. package/docs/WALKTHROUGH.md +241 -0
  131. package/lib/runtime/scheduler/multi-agent-coordinator.d.ts +28 -0
  132. package/lib/runtime/scheduler/multi-agent-coordinator.js +152 -26
  133. package/lib/sdk/README.md +2 -0
  134. package/lib/sdk/index.cjs +22 -8
  135. package/lib/sdk/index.d.ts +60 -16
  136. package/oxe/templates/config.template.json +1 -0
  137. package/package.json +30 -22
  138. package/packages/runtime/package.json +1 -1
  139. package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +357 -193
  140. package/vscode-extension/oxe-agents-1.4.0.vsix +0 -0
  141. package/vscode-extension/oxe-agents-1.5.0.vsix +0 -0
  142. 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
- const runDir = path.join(projectRoot, '.oxe', 'runs', runId);
659
- const statePath = path.join(runDir, 'multi-agent-state.json');
660
- const handoffsPath = path.join(runDir, 'handoffs.json');
661
- const arbitrationPath = path.join(runDir, 'arbitration-results.json');
662
- const state = runtime && typeof runtime.loadMultiAgentState === 'function'
663
- ? runtime.loadMultiAgentState(projectRoot, runId)
664
- : readJsonIfExists(statePath);
665
- const handoffs = readJsonIfExists(handoffsPath);
666
- const arbitrationResults = readJsonIfExists(arbitrationPath);
667
- return {
668
- path: statePath,
669
- enabled: Boolean(state),
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
- const layerWithoutAzure = { ...layer };
219
- delete layerWithoutAzure.azure;
220
- Object.assign(merged, layerWithoutAzure);
221
- } else {
222
- Object.assign(merged, layer);
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
+ };