@magnusekdahl/parallix 1.2.0 → 1.3.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 (127) hide show
  1. package/index.js +190 -185
  2. package/lib/agents/agents.js +737 -838
  3. package/lib/agents/agents.ts +930 -0
  4. package/lib/agents/claude-telemetry.js +149 -154
  5. package/lib/agents/claude-telemetry.ts +233 -0
  6. package/lib/agents/claude.js +121 -131
  7. package/lib/agents/claude.ts +156 -0
  8. package/lib/agents/codex-telemetry.js +176 -164
  9. package/lib/agents/codex-telemetry.ts +205 -0
  10. package/lib/agents/codex.js +168 -201
  11. package/lib/agents/codex.ts +236 -0
  12. package/lib/agents/limit-hit.js +216 -226
  13. package/lib/agents/limit-hit.ts +262 -0
  14. package/lib/agents/mistral-telemetry.js +7 -10
  15. package/lib/agents/mistral-telemetry.ts +44 -0
  16. package/lib/agents/mistral.js +40 -58
  17. package/lib/agents/mistral.ts +84 -0
  18. package/lib/agents/opencode-export.js +126 -127
  19. package/lib/agents/opencode-export.ts +160 -0
  20. package/lib/agents/opencode-telemetry.js +282 -261
  21. package/lib/agents/opencode-telemetry.ts +350 -0
  22. package/lib/agents/opencode.js +263 -305
  23. package/lib/agents/opencode.ts +370 -0
  24. package/lib/agents/stage-telemetry.js +23 -24
  25. package/lib/agents/stage-telemetry.ts +47 -0
  26. package/lib/commands/active.js +502 -579
  27. package/lib/commands/active.ts +665 -0
  28. package/lib/commands/checkpoint.js +98 -74
  29. package/lib/commands/checkpoint.ts +79 -0
  30. package/lib/commands/config.js +61 -31
  31. package/lib/commands/config.ts +47 -0
  32. package/lib/commands/coverage-gate.js +271 -246
  33. package/lib/commands/coverage-gate.ts +362 -0
  34. package/lib/commands/diff.js +140 -119
  35. package/lib/commands/diff.ts +125 -0
  36. package/lib/commands/draft.js +770 -923
  37. package/lib/commands/draft.ts +1021 -0
  38. package/lib/commands/handoff.js +588 -608
  39. package/lib/commands/handoff.ts +694 -0
  40. package/lib/commands/integrate.js +1332 -1463
  41. package/lib/commands/integrate.ts +1688 -0
  42. package/lib/commands/mission-start.js +279 -237
  43. package/lib/commands/mission-start.ts +263 -0
  44. package/lib/commands/rebase.js +606 -582
  45. package/lib/commands/rebase.ts +635 -0
  46. package/lib/commands/repair-handoff.js +181 -179
  47. package/lib/commands/repair-handoff.ts +228 -0
  48. package/lib/commands/resolve-conflict.js +121 -102
  49. package/lib/commands/resolve-conflict.ts +112 -0
  50. package/lib/commands/review.js +3 -4
  51. package/lib/commands/review.ts +14 -0
  52. package/lib/commands/setup-review.js +4 -5
  53. package/lib/commands/setup-review.ts +14 -0
  54. package/lib/commands/setup.js +3 -3
  55. package/lib/commands/setup.ts +2 -0
  56. package/lib/commands/stats-backfill.js +394 -359
  57. package/lib/commands/stats-backfill.ts +418 -0
  58. package/lib/commands/stats.js +1424 -1665
  59. package/lib/commands/stats.ts +2186 -0
  60. package/lib/commands/status.js +219 -200
  61. package/lib/commands/status.ts +219 -0
  62. package/lib/commands/verify.js +3 -1
  63. package/lib/commands/verify.ts +2 -0
  64. package/lib/core/fmt.js +140 -211
  65. package/lib/core/fmt.ts +189 -0
  66. package/lib/core/git.js +121 -102
  67. package/lib/core/git.ts +146 -0
  68. package/lib/core/gitignore.js +59 -106
  69. package/lib/core/gitignore.ts +103 -0
  70. package/lib/core/mission-utils.js +833 -875
  71. package/lib/core/mission-utils.ts +998 -0
  72. package/lib/core/nels.js +112 -127
  73. package/lib/core/nels.ts +189 -0
  74. package/lib/core/persistent-data-migration.js +247 -205
  75. package/lib/core/persistent-data-migration.ts +255 -0
  76. package/lib/core/product-config.js +400 -476
  77. package/lib/core/product-config.ts +518 -0
  78. package/lib/core/runtime-matrix.js +41 -87
  79. package/lib/core/runtime-matrix.ts +84 -0
  80. package/lib/core/spawn-tee.js +152 -192
  81. package/lib/core/spawn-tee.ts +202 -0
  82. package/lib/core/state-map.js +92 -92
  83. package/lib/core/state-map.ts +115 -0
  84. package/lib/core/storage.js +101 -129
  85. package/lib/core/storage.ts +182 -0
  86. package/lib/core/subagent-limit.js +15 -16
  87. package/lib/core/subagent-limit.ts +25 -0
  88. package/lib/core/verification.js +148 -138
  89. package/lib/core/verification.ts +200 -0
  90. package/lib/index.js +142 -59
  91. package/lib/index.ts +134 -0
  92. package/lib/review/rebase.js +142 -145
  93. package/lib/review/rebase.ts +188 -0
  94. package/lib/review/review-adapter.js +99 -87
  95. package/lib/review/review-adapter.ts +198 -0
  96. package/lib/review/review-artifacts.js +486 -629
  97. package/lib/review/review-artifacts.ts +622 -0
  98. package/lib/review/review-commands.js +1220 -1373
  99. package/lib/review/review-commands.ts +1579 -0
  100. package/lib/review/review-events.js +544 -886
  101. package/lib/review/review-events.ts +954 -0
  102. package/lib/review/review-loop.js +945 -1020
  103. package/lib/review/review-loop.ts +1174 -0
  104. package/lib/review/review-polling.js +86 -115
  105. package/lib/review/review-polling.ts +194 -0
  106. package/lib/review/review-prompts.js +154 -189
  107. package/lib/review/review-prompts.ts +182 -0
  108. package/lib/review/review-state.js +246 -225
  109. package/lib/review/review-state.ts +353 -0
  110. package/lib/review/review.js +63 -84
  111. package/lib/review/review.ts +138 -0
  112. package/lib/tools/backlog.js +694 -680
  113. package/lib/tools/backlog.ts +835 -0
  114. package/lib/tools/forgejo.js +1270 -1405
  115. package/lib/tools/forgejo.ts +1793 -0
  116. package/lib/tools/gatekeeper.js +106 -96
  117. package/lib/tools/gatekeeper.ts +124 -0
  118. package/lib/tools/redgreen.js +200 -181
  119. package/lib/tools/redgreen.ts +221 -0
  120. package/lib/tools/sessions.js +58 -52
  121. package/lib/tools/sessions.ts +81 -0
  122. package/lib/tools/setup-review.js +902 -975
  123. package/lib/tools/setup-review.ts +1152 -0
  124. package/package.json +7 -5
  125. package/prompts/draft.md +1 -0
  126. package/prompts/portfolio.md +1 -1
  127. package/px.js +226 -210
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
-
2
+ "use strict";
3
3
  /**
4
4
  * coverage-gate.js - run parallix tests with Node's built-in coverage support
5
5
  * and enforce a configurable line-coverage threshold (default 90%).
@@ -47,122 +47,156 @@
47
47
  * Cleanup strategy: per-process inventory (PER_RUN_SCRATCH) + exit/SIGINT/SIGTERM
48
48
  * handlers (lines 131-141) that remove only tracked dirs. No blanket rm -rf.
49
49
  */
50
-
51
- const { spawnSync } = require('child_process');
52
- const fs = require('fs');
53
- const os = require('os');
54
- const path = require('path');
55
- const fmt = require('../core/fmt');
56
-
50
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
51
+ if (k2 === undefined) k2 = k;
52
+ var desc = Object.getOwnPropertyDescriptor(m, k);
53
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
54
+ desc = { enumerable: true, get: function() { return m[k]; } };
55
+ }
56
+ Object.defineProperty(o, k2, desc);
57
+ }) : (function(o, m, k, k2) {
58
+ if (k2 === undefined) k2 = k;
59
+ o[k2] = m[k];
60
+ }));
61
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
62
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
63
+ }) : function(o, v) {
64
+ o["default"] = v;
65
+ });
66
+ var __importStar = (this && this.__importStar) || (function () {
67
+ var ownKeys = function(o) {
68
+ ownKeys = Object.getOwnPropertyNames || function (o) {
69
+ var ar = [];
70
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
71
+ return ar;
72
+ };
73
+ return ownKeys(o);
74
+ };
75
+ return function (mod) {
76
+ if (mod && mod.__esModule) return mod;
77
+ var result = {};
78
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
79
+ __setModuleDefault(result, mod);
80
+ return result;
81
+ };
82
+ })();
83
+ const node_child_process_1 = require("node:child_process");
84
+ const fs = __importStar(require("node:fs"));
85
+ const os = __importStar(require("node:os"));
86
+ const path = __importStar(require("node:path"));
87
+ const fmt = __importStar(require("../core/fmt.js"));
57
88
  const REPO_ROOT = path.resolve(__dirname, '..', '..');
58
89
  const SELF_TEST_FILE = 'coverage-gate.test.js';
59
90
  const TEMP_DIR_PREFIXES = [
60
- 'agents-',
61
- 'sessions-',
62
- 'review-',
63
- 'forgejo-',
64
- 'workflow-',
65
- 'gatekeeper-',
66
- 'graphify-',
67
- 'claude-',
68
- 'codex-',
69
- 'gemini-',
70
- 'opencode-',
71
- 'startagent-',
72
- 'test-',
73
- 'mission-task-',
74
- 'visualBoard-task-',
75
- 'backlog-'
91
+ 'agents-',
92
+ 'sessions-',
93
+ 'review-',
94
+ 'forgejo-',
95
+ 'workflow-',
96
+ 'gatekeeper-',
97
+ 'graphify-',
98
+ 'claude-',
99
+ 'codex-',
100
+ 'gemini-',
101
+ 'opencode-',
102
+ 'startagent-',
103
+ 'test-',
104
+ 'mission-task-',
105
+ 'visualBoard-task-',
106
+ 'backlog-'
76
107
  ];
77
108
  const COVERAGE_INCLUDES = [
78
- 'index.js',
79
- 'lib/**/*.js'
109
+ 'index.js',
110
+ 'lib/**/*.js'
80
111
  ];
81
112
  const COVERAGE_EXCLUDES = [
82
- 'test/**',
83
- 'prompts/**',
84
- 'config/*.json',
85
- '.workflow/**',
86
- 'node_modules/**'
113
+ 'test/**',
114
+ 'prompts/**',
115
+ 'config/*.json',
116
+ '.workflow/**',
117
+ 'node_modules/**'
87
118
  ];
88
119
  // The full suite regularly exceeds 10 minutes in this repository, especially
89
120
  // under cold caches. Keep the gate generous so it can finish without a manual
90
121
  // override while still failing on real hangs.
91
122
  const DEFAULT_TEST_TIMEOUT_MS = 3_600_000;
92
- /** @type {string[]} */
93
123
  const PER_RUN_SCRATCH = [];
94
124
  let threshold = 90;
95
125
  let dryRun = false;
96
126
  let lcov = false;
97
127
  let cleanupDone = false;
98
-
99
128
  for (let i = 2; i < process.argv.length; i++) {
100
- if (process.argv[i] === '--threshold' && process.argv[i + 1]) {
101
- threshold = parseFloat(process.argv[i + 1]);
102
- i++;
103
- } else if (process.argv[i] === '--dry-run') {
104
- dryRun = true;
105
- } else if (process.argv[i] === '--lcov') {
106
- lcov = true;
107
- }
129
+ if (process.argv[i] === '--threshold' && process.argv[i + 1]) {
130
+ threshold = parseFloat(process.argv[i + 1]);
131
+ i++;
132
+ }
133
+ else if (process.argv[i] === '--dry-run') {
134
+ dryRun = true;
135
+ }
136
+ else if (process.argv[i] === '--lcov') {
137
+ lcov = true;
138
+ }
108
139
  }
109
-
110
140
  function discoverTestFiles() {
111
- const testDir = path.join(REPO_ROOT, 'test');
112
- if (!fs.existsSync(testDir)) {return [];}
113
- return fs.readdirSync(testDir)
114
- .filter(file => file.endsWith('.test.js'))
115
- .filter(file => file !== SELF_TEST_FILE)
116
- .map(file => path.join(testDir, file))
117
- .sort();
141
+ const testDir = path.join(REPO_ROOT, 'test');
142
+ if (!fs.existsSync(testDir)) {
143
+ return [];
144
+ }
145
+ return fs.readdirSync(testDir)
146
+ .filter(file => file.endsWith('.test.js'))
147
+ .filter(file => file !== SELF_TEST_FILE)
148
+ .map(file => path.join(testDir, file))
149
+ .sort();
118
150
  }
119
-
120
151
  function listTempEntries(tmpRoot = os.tmpdir()) {
121
- if (!fs.existsSync(tmpRoot)) {return new Set();}
122
- return new Set(fs.readdirSync(tmpRoot));
152
+ if (!fs.existsSync(tmpRoot)) {
153
+ return new Set();
154
+ }
155
+ return new Set(fs.readdirSync(tmpRoot));
123
156
  }
124
-
125
- /** @param {string} name */
126
157
  function shouldCleanTempDir(name) {
127
- return TEMP_DIR_PREFIXES.some(prefix => name.startsWith(prefix));
158
+ return TEMP_DIR_PREFIXES.some(prefix => name.startsWith(prefix));
128
159
  }
129
-
130
- /** @param {Set<string>} beforeEntries @param {string} tmpRoot */
131
160
  function cleanupNewTempDirs(beforeEntries, tmpRoot = os.tmpdir()) {
132
- if (!fs.existsSync(tmpRoot)) {return;}
133
- for (const name of fs.readdirSync(tmpRoot)) {
134
- if (beforeEntries.has(name) || !shouldCleanTempDir(name)) {continue;}
135
- const fullPath = path.join(tmpRoot, name);
136
- let stat = null;
137
- try {
138
- stat = fs.statSync(fullPath);
139
- } catch (_) {
140
- continue;
161
+ if (!fs.existsSync(tmpRoot)) {
162
+ return;
163
+ }
164
+ for (const name of fs.readdirSync(tmpRoot)) {
165
+ if (beforeEntries.has(name) || !shouldCleanTempDir(name)) {
166
+ continue;
167
+ }
168
+ const fullPath = path.join(tmpRoot, name);
169
+ let stat = null;
170
+ try {
171
+ stat = fs.statSync(fullPath);
172
+ }
173
+ catch (_) {
174
+ continue;
175
+ }
176
+ if (!stat.isDirectory()) {
177
+ continue;
178
+ }
179
+ try {
180
+ fs.rmSync(fullPath, { recursive: true, force: true });
181
+ }
182
+ catch (_) { }
141
183
  }
142
- if (!stat.isDirectory()) {continue;}
143
- try {
144
- fs.rmSync(fullPath, { recursive: true, force: true });
145
- } catch (_) {}
146
- }
147
184
  }
148
-
149
185
  function createPerRunScratchDirs() {
150
- const coverageDir = fs.mkdtempSync(path.join(os.tmpdir(), 'node-coverage-'));
151
- PER_RUN_SCRATCH.push(coverageDir);
152
- return coverageDir;
186
+ const coverageDir = fs.mkdtempSync(path.join(os.tmpdir(), 'node-coverage-'));
187
+ PER_RUN_SCRATCH.push(coverageDir);
188
+ return coverageDir;
153
189
  }
154
-
155
190
  function createPerRunTmpRoot() {
156
- const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'coverage-gate-tmp-'));
157
- PER_RUN_SCRATCH.push(tmpRoot);
158
- return tmpRoot;
191
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'coverage-gate-tmp-'));
192
+ PER_RUN_SCRATCH.push(tmpRoot);
193
+ return tmpRoot;
159
194
  }
160
-
161
195
  function createMockGraphifyBin() {
162
- const graphifyDir = fs.mkdtempSync(path.join(os.tmpdir(), 'graphify-'));
163
- PER_RUN_SCRATCH.push(graphifyDir);
164
- const graphifyBin = path.join(graphifyDir, 'graphify');
165
- fs.writeFileSync(graphifyBin, `#!/usr/bin/env bash
196
+ const graphifyDir = fs.mkdtempSync(path.join(os.tmpdir(), 'graphify-'));
197
+ PER_RUN_SCRATCH.push(graphifyDir);
198
+ const graphifyBin = path.join(graphifyDir, 'graphify');
199
+ fs.writeFileSync(graphifyBin, `#!/usr/bin/env bash
166
200
  set -euo pipefail
167
201
  if [[ "\${1:-}" == "--help" ]]; then
168
202
  echo "mock graphify"
@@ -178,188 +212,179 @@ fi
178
212
  echo "Unhandled graphify command: $*" >&2
179
213
  exit 1
180
214
  `, { mode: 0o755 });
181
- return graphifyBin;
215
+ return graphifyBin;
182
216
  }
183
-
184
217
  function cleanupPerRunScratch() {
185
- if (cleanupDone) {return;}
186
- cleanupDone = true;
187
- for (const dir of PER_RUN_SCRATCH) {
188
- try {
189
- if (fs.existsSync(dir)) {
190
- fs.rmSync(dir, { recursive: true, force: true });
191
- }
192
- } catch (_) {}
193
- }
194
- PER_RUN_SCRATCH.length = 0;
218
+ if (cleanupDone) {
219
+ return;
220
+ }
221
+ cleanupDone = true;
222
+ for (const dir of PER_RUN_SCRATCH) {
223
+ try {
224
+ if (fs.existsSync(dir)) {
225
+ fs.rmSync(dir, { recursive: true, force: true });
226
+ }
227
+ }
228
+ catch (_) { }
229
+ }
230
+ PER_RUN_SCRATCH.length = 0;
195
231
  }
196
-
197
232
  function registerExitHandlers() {
198
- process.on('exit', cleanupPerRunScratch);
199
- process.on('SIGINT', () => {
200
- cleanupPerRunScratch();
201
- process.exit(130);
202
- });
203
- process.on('SIGTERM', () => {
204
- cleanupPerRunScratch();
205
- process.exit(143);
206
- });
233
+ process.on('exit', cleanupPerRunScratch);
234
+ process.on('SIGINT', () => {
235
+ cleanupPerRunScratch();
236
+ process.exit(130);
237
+ });
238
+ process.on('SIGTERM', () => {
239
+ cleanupPerRunScratch();
240
+ process.exit(143);
241
+ });
207
242
  }
208
-
209
243
  function resetPerRunScratchState() {
210
- PER_RUN_SCRATCH.length = 0;
211
- cleanupDone = false;
244
+ PER_RUN_SCRATCH.length = 0;
245
+ cleanupDone = false;
212
246
  }
213
-
214
- /** @param {string[]} testFiles @param {number} coverageThreshold @param {boolean} useLcov */
215
247
  function buildCoverageArgs(testFiles, coverageThreshold = threshold, useLcov = lcov) {
216
- const args = [
217
- '--test',
218
- '--experimental-test-coverage',
219
- `--test-coverage-lines=${coverageThreshold}`,
220
- ...COVERAGE_INCLUDES.flatMap(pattern => ['--test-coverage-include', pattern]),
221
- ...COVERAGE_EXCLUDES.flatMap(pattern => ['--test-coverage-exclude', pattern]),
222
- ];
223
-
224
- if (useLcov) {
225
- const coverageDir = path.join(REPO_ROOT, 'coverage');
226
- if (!fs.existsSync(coverageDir)) {
227
- fs.mkdirSync(coverageDir, { recursive: true });
248
+ const args = [
249
+ '--test',
250
+ '--experimental-test-coverage',
251
+ `--test-coverage-lines=${coverageThreshold}`,
252
+ ...COVERAGE_INCLUDES.flatMap(pattern => ['--test-coverage-include', pattern]),
253
+ ...COVERAGE_EXCLUDES.flatMap(pattern => ['--test-coverage-exclude', pattern]),
254
+ ];
255
+ if (useLcov) {
256
+ const coverageDir = path.join(REPO_ROOT, 'coverage');
257
+ if (!fs.existsSync(coverageDir)) {
258
+ fs.mkdirSync(coverageDir, { recursive: true });
259
+ }
260
+ args.push('--test-reporter=lcov');
261
+ args.push(`--test-reporter-destination=${path.join(coverageDir, 'lcov.info')}`);
262
+ args.push('--test-reporter=spec');
263
+ args.push('--test-reporter-destination=stdout');
228
264
  }
229
- args.push('--test-reporter=lcov');
230
- args.push(`--test-reporter-destination=${path.join(coverageDir, 'lcov.info')}`);
231
- args.push('--test-reporter=spec');
232
- args.push('--test-reporter-destination=stdout');
233
- }
234
-
235
- args.push(...testFiles);
236
- return args;
265
+ args.push(...testFiles);
266
+ return args;
237
267
  }
238
-
239
268
  function resolveTestTimeoutMs(env = process.env) {
240
- const raw = env.WORKFLOW_COVERAGE_GATE_TIMEOUT_MS;
241
- if (raw === undefined || raw === '') {return DEFAULT_TEST_TIMEOUT_MS;}
242
- const parsed = Number(raw);
243
- return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TEST_TIMEOUT_MS;
269
+ const raw = env.WORKFLOW_COVERAGE_GATE_TIMEOUT_MS;
270
+ if (raw === undefined || raw === '') {
271
+ return DEFAULT_TEST_TIMEOUT_MS;
272
+ }
273
+ const parsed = Number(raw);
274
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TEST_TIMEOUT_MS;
244
275
  }
245
-
246
- /** @param {string[]} testFiles @param {number} coverageThreshold @param {Function} _spawnSync */
247
- function runTests(testFiles, coverageThreshold = threshold, _spawnSync = spawnSync) {
248
- const tmpRoot = createPerRunTmpRoot();
249
- const tmpEntriesBefore = listTempEntries(tmpRoot);
250
- const nodeCoverageDir = createPerRunScratchDirs();
251
- const childEnv = { ...process.env };
252
- delete childEnv.NODE_TEST_CONTEXT;
253
- delete childEnv.NODE_OPTIONS;
254
- childEnv.TMPDIR = tmpRoot;
255
- childEnv.NODE_V8_COVERAGE = nodeCoverageDir;
256
- childEnv.GRAPHIFY_BIN = createMockGraphifyBin();
257
-
258
- const result = _spawnSync(process.execPath, buildCoverageArgs(testFiles, coverageThreshold), {
259
- encoding: 'utf8',
260
- cwd: REPO_ROOT,
261
- env: childEnv,
262
- stdio: ['pipe', 'inherit', 'inherit'],
263
- timeout: resolveTestTimeoutMs(),
264
- });
265
-
266
- cleanupNewTempDirs(tmpEntriesBefore, tmpRoot);
267
- cleanupPerRunScratch();
268
-
269
- if (result.error) {
270
- fmt.log.error(`[coverage-gate] failed to spawn test runner: ${result.error.message}`);
271
- return 1;
272
- }
273
-
274
- if (result.signal) {
275
- fmt.log.error(`[coverage-gate] test runner killed by signal ${result.signal}`);
276
- return 1;
277
- }
278
-
279
- return result.status === null ? 1 : result.status;
276
+ function runTests(testFiles, coverageThreshold = threshold, _spawnSync = node_child_process_1.spawnSync) {
277
+ const tmpRoot = createPerRunTmpRoot();
278
+ const tmpEntriesBefore = listTempEntries(tmpRoot);
279
+ const nodeCoverageDir = createPerRunScratchDirs();
280
+ const childEnv = { ...process.env };
281
+ delete childEnv.NODE_TEST_CONTEXT;
282
+ delete childEnv.NODE_OPTIONS;
283
+ childEnv.TMPDIR = tmpRoot;
284
+ childEnv.NODE_V8_COVERAGE = nodeCoverageDir;
285
+ childEnv.GRAPHIFY_BIN = createMockGraphifyBin();
286
+ const result = _spawnSync(process.execPath, buildCoverageArgs(testFiles, coverageThreshold), {
287
+ encoding: 'utf8',
288
+ cwd: REPO_ROOT,
289
+ env: childEnv,
290
+ stdio: ['pipe', 'inherit', 'inherit'],
291
+ timeout: resolveTestTimeoutMs(),
292
+ });
293
+ cleanupNewTempDirs(tmpEntriesBefore, tmpRoot);
294
+ cleanupPerRunScratch();
295
+ if (result.error) {
296
+ fmt.log.error(`[coverage-gate] failed to spawn test runner: ${result.error.message}`);
297
+ return 1;
298
+ }
299
+ if (result.signal) {
300
+ fmt.log.error(`[coverage-gate] test runner killed by signal ${result.signal}`);
301
+ return 1;
302
+ }
303
+ return result.status === null ? 1 : result.status;
280
304
  }
281
-
282
305
  function main() {
283
- const testFiles = discoverTestFiles();
284
- if (testFiles.length === 0) {
285
- fmt.log.fail('no parallix test files found under test/');
286
- process.exit(1);
287
- }
288
-
289
- fmt.log.info(`Found ${testFiles.length} test file(s)`);
290
-
291
- if (dryRun) {
292
- fmt.log.info(`DRY-RUN mode — threshold=${threshold}%`);
293
- fmt.log.info('Denominator: index.js + lib/**/*.js');
294
- fmt.log.info(`Include globs: ${COVERAGE_INCLUDES.join(', ')}`);
295
- fmt.log.info(`Exclude globs: ${COVERAGE_EXCLUDES.join(', ')}`);
296
- fmt.log.info(`Would run: ${fmt.command(`${process.execPath} ${buildCoverageArgs(testFiles, threshold).join(' ')}`)}`);
297
- process.exit(0);
298
- }
299
-
300
- registerExitHandlers();
301
- process.exit(runTests(testFiles, threshold));
302
- }
303
-
304
- if (require.main === module) {
305
- main();
306
- }
307
-
308
- /** @param {string[]} args @param {{exitFn?: Function}} options */
309
- function run(args, options = {}) {
310
- /** @type{Function} */
311
- const exitFn = options.exitFn || process.exit;
312
- let threshold_ = 90;
313
- let dryRun_ = false;
314
- for (let i = 0; i < args.length; i++) {
315
- if (args[i] === '--threshold' && args[i + 1]) {
316
- threshold_ = parseFloat(args[i + 1]);
317
- i++;
318
- } else if (args[i] === '--dry-run') {
319
- dryRun_ = true;
306
+ const testFiles = discoverTestFiles();
307
+ if (testFiles.length === 0) {
308
+ fmt.log.fail('no parallix test files found under test/');
309
+ process.exit(1);
320
310
  }
321
- }
322
- const savedThreshold = threshold;
323
- const savedDryRun = dryRun;
324
- threshold = threshold_;
325
- dryRun = dryRun_;
326
- try {
327
- if (dryRun_) {
328
- const testFiles = discoverTestFiles();
329
- if (testFiles.length === 0) {
330
- if (typeof exitFn === 'function') {exitFn(1);}
331
- } else {
332
- fmt.log.info(`Found ${testFiles.length} test file(s)`);
311
+ fmt.log.info(`Found ${testFiles.length} test file(s)`);
312
+ if (dryRun) {
333
313
  fmt.log.info(`DRY-RUN mode — threshold=${threshold}%`);
334
314
  fmt.log.info('Denominator: index.js + lib/**/*.js');
335
315
  fmt.log.info(`Include globs: ${COVERAGE_INCLUDES.join(', ')}`);
336
316
  fmt.log.info(`Exclude globs: ${COVERAGE_EXCLUDES.join(', ')}`);
337
317
  fmt.log.info(`Would run: ${fmt.command(`${process.execPath} ${buildCoverageArgs(testFiles, threshold).join(' ')}`)}`);
338
- if (typeof exitFn === 'function') {exitFn(0);}
339
- else {process.exit(0);}
340
- }
341
- } else {
342
- registerExitHandlers();
343
- exitFn(runTests(discoverTestFiles(), threshold));
318
+ process.exit(0);
319
+ }
320
+ registerExitHandlers();
321
+ process.exit(runTests(testFiles, threshold));
322
+ }
323
+ if (typeof require !== 'undefined' && require.main === module) {
324
+ main();
325
+ }
326
+ function run(args, options = {}) {
327
+ const exitFn = options.exitFn || process.exit;
328
+ let threshold_ = 90;
329
+ let dryRun_ = false;
330
+ for (let i = 0; i < args.length; i++) {
331
+ if (args[i] === '--threshold' && args[i + 1]) {
332
+ threshold_ = parseFloat(args[i + 1]);
333
+ i++;
334
+ }
335
+ else if (args[i] === '--dry-run') {
336
+ dryRun_ = true;
337
+ }
338
+ }
339
+ const savedThreshold = threshold;
340
+ const savedDryRun = dryRun;
341
+ threshold = threshold_;
342
+ dryRun = dryRun_;
343
+ try {
344
+ if (dryRun_) {
345
+ const testFiles = discoverTestFiles();
346
+ if (testFiles.length === 0) {
347
+ if (typeof exitFn === 'function') {
348
+ exitFn(1);
349
+ }
350
+ }
351
+ else {
352
+ fmt.log.info(`Found ${testFiles.length} test file(s)`);
353
+ fmt.log.info(`DRY-RUN mode — threshold=${threshold}%`);
354
+ fmt.log.info('Denominator: index.js + lib/**/*.js');
355
+ fmt.log.info(`Include globs: ${COVERAGE_INCLUDES.join(', ')}`);
356
+ fmt.log.info(`Exclude globs: ${COVERAGE_EXCLUDES.join(', ')}`);
357
+ fmt.log.info(`Would run: ${fmt.command(`${process.execPath} ${buildCoverageArgs(testFiles, threshold).join(' ')}`)}`);
358
+ if (typeof exitFn === 'function') {
359
+ exitFn(0);
360
+ }
361
+ else {
362
+ process.exit(0);
363
+ }
364
+ }
365
+ }
366
+ else {
367
+ registerExitHandlers();
368
+ exitFn(runTests(discoverTestFiles(), threshold));
369
+ }
370
+ }
371
+ finally {
372
+ threshold = savedThreshold;
373
+ dryRun = savedDryRun;
344
374
  }
345
- } finally {
346
- threshold = savedThreshold;
347
- dryRun = savedDryRun;
348
- }
349
375
  }
350
-
376
+ run.buildCoverageArgs = buildCoverageArgs;
377
+ run.cleanupNewTempDirs = cleanupNewTempDirs;
378
+ run.cleanupPerRunScratch = cleanupPerRunScratch;
379
+ run.createPerRunScratchDirs = createPerRunScratchDirs;
380
+ run.COVERAGE_EXCLUDES = COVERAGE_EXCLUDES;
381
+ run.COVERAGE_INCLUDES = COVERAGE_INCLUDES;
382
+ run.DEFAULT_TEST_TIMEOUT_MS = DEFAULT_TEST_TIMEOUT_MS;
383
+ run.discoverTestFiles = discoverTestFiles;
384
+ run.listTempEntries = listTempEntries;
385
+ run.registerExitHandlers = registerExitHandlers;
386
+ run.resetPerRunScratchState = resetPerRunScratchState;
387
+ run.resolveTestTimeoutMs = resolveTestTimeoutMs;
388
+ run.runTests = runTests;
389
+ run.shouldCleanTempDir = shouldCleanTempDir;
351
390
  module.exports = run;
352
- module.exports.buildCoverageArgs = buildCoverageArgs;
353
- module.exports.cleanupNewTempDirs = cleanupNewTempDirs;
354
- module.exports.cleanupPerRunScratch = cleanupPerRunScratch;
355
- module.exports.createPerRunScratchDirs = createPerRunScratchDirs;
356
- module.exports.COVERAGE_EXCLUDES = COVERAGE_EXCLUDES;
357
- module.exports.COVERAGE_INCLUDES = COVERAGE_INCLUDES;
358
- module.exports.DEFAULT_TEST_TIMEOUT_MS = DEFAULT_TEST_TIMEOUT_MS;
359
- module.exports.discoverTestFiles = discoverTestFiles;
360
- module.exports.listTempEntries = listTempEntries;
361
- module.exports.registerExitHandlers = registerExitHandlers;
362
- module.exports.resetPerRunScratchState = resetPerRunScratchState;
363
- module.exports.resolveTestTimeoutMs = resolveTestTimeoutMs;
364
- module.exports.runTests = runTests;
365
- module.exports.shouldCleanTempDir = shouldCleanTempDir;