@codeledger/harness 0.1.1
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/LICENSE +27 -0
- package/dist/adapters/generic.d.ts +7 -0
- package/dist/adapters/generic.d.ts.map +1 -0
- package/dist/adapters/generic.js +70 -0
- package/dist/adapters/generic.js.map +1 -0
- package/dist/adapters/guided.d.ts +7 -0
- package/dist/adapters/guided.d.ts.map +1 -0
- package/dist/adapters/guided.js +60 -0
- package/dist/adapters/guided.js.map +1 -0
- package/dist/adapters/types.d.ts +6 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/comparator.d.ts +7 -0
- package/dist/comparator.d.ts.map +1 -0
- package/dist/comparator.js +135 -0
- package/dist/comparator.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/injection.d.ts +5 -0
- package/dist/injection.d.ts.map +1 -0
- package/dist/injection.js +107 -0
- package/dist/injection.js.map +1 -0
- package/dist/scenario-runner.d.ts +26 -0
- package/dist/scenario-runner.d.ts.map +1 -0
- package/dist/scenario-runner.js +443 -0
- package/dist/scenario-runner.js.map +1 -0
- package/dist/scenario-runner.test.d.ts +2 -0
- package/dist/scenario-runner.test.d.ts.map +1 -0
- package/dist/scenario-runner.test.js +28 -0
- package/dist/scenario-runner.test.js.map +1 -0
- package/dist/worktree.d.ts +25 -0
- package/dist/worktree.d.ts.map +1 -0
- package/dist/worktree.js +171 -0
- package/dist/worktree.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { LedgerStore } from '@codeledger/core';
|
|
5
|
+
import { buildBundleViaEngine } from '@codeledger/core-engine';
|
|
6
|
+
import { runPreflight, validatePreflight, getHeadCommit, execCommand, RunRecorder, } from '@codeledger/instrument';
|
|
7
|
+
import { GenericAdapter } from './adapters/generic.js';
|
|
8
|
+
import { GuidedAdapter } from './adapters/guided.js';
|
|
9
|
+
import { injectIntoPrompt, renderBundleMarkdown } from './injection.js';
|
|
10
|
+
import { createWorktree, cleanupAllWorktrees } from './worktree.js';
|
|
11
|
+
// ─── Signal-safe worktree cleanup ──────────────────────────────────────────
|
|
12
|
+
// When a SIGINT/SIGTERM arrives mid-run we need synchronous cleanup because
|
|
13
|
+
// Node won't await async work in signal handlers. The worktree cleanup
|
|
14
|
+
// closure only does execFileSync internally so a sync wrapper is safe here.
|
|
15
|
+
let activeCleanup = null;
|
|
16
|
+
function signalCleanupHandler(signal) {
|
|
17
|
+
if (activeCleanup) {
|
|
18
|
+
try {
|
|
19
|
+
activeCleanup();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Best effort — process is exiting anyway
|
|
23
|
+
}
|
|
24
|
+
activeCleanup = null;
|
|
25
|
+
}
|
|
26
|
+
// Re-raise so the default handler fires (correct exit code for callers)
|
|
27
|
+
process.kill(process.pid, signal);
|
|
28
|
+
}
|
|
29
|
+
let signalHandlersInstalled = false;
|
|
30
|
+
function installSignalHandlers() {
|
|
31
|
+
if (signalHandlersInstalled)
|
|
32
|
+
return;
|
|
33
|
+
signalHandlersInstalled = true;
|
|
34
|
+
process.once('SIGINT', signalCleanupHandler);
|
|
35
|
+
process.once('SIGTERM', signalCleanupHandler);
|
|
36
|
+
}
|
|
37
|
+
function removeSignalHandlers() {
|
|
38
|
+
if (!signalHandlersInstalled)
|
|
39
|
+
return;
|
|
40
|
+
signalHandlersInstalled = false;
|
|
41
|
+
process.removeListener('SIGINT', signalCleanupHandler);
|
|
42
|
+
process.removeListener('SIGTERM', signalCleanupHandler);
|
|
43
|
+
}
|
|
44
|
+
export async function runScenario(opts) {
|
|
45
|
+
const { cwd, force = false } = opts;
|
|
46
|
+
// Load config
|
|
47
|
+
const configPath = join(cwd, '.codeledger', 'config.json');
|
|
48
|
+
if (!existsSync(configPath)) {
|
|
49
|
+
throw new Error('No .codeledger/config.json found. Run "codeledger init" first.');
|
|
50
|
+
}
|
|
51
|
+
let config;
|
|
52
|
+
try {
|
|
53
|
+
config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
throw new Error(`Failed to parse config at ${configPath}. Ensure it contains valid JSON.`);
|
|
57
|
+
}
|
|
58
|
+
// Preflight
|
|
59
|
+
const indexPath = join(cwd, config.workspace.cache_dir, 'repo-index.json');
|
|
60
|
+
const preflight = runPreflight(cwd, configPath, indexPath);
|
|
61
|
+
const validation = validatePreflight(preflight, 'run', force);
|
|
62
|
+
for (const err of validation.errors) {
|
|
63
|
+
console.error(`❌ ${err}`);
|
|
64
|
+
}
|
|
65
|
+
for (const warn of validation.warnings) {
|
|
66
|
+
console.warn(`⚠️ ${warn}`);
|
|
67
|
+
}
|
|
68
|
+
if (!validation.ok) {
|
|
69
|
+
throw new Error('Preflight checks failed.');
|
|
70
|
+
}
|
|
71
|
+
// Load scenario — validate scenario file exists and has safe ID
|
|
72
|
+
if (!existsSync(opts.scenarioPath)) {
|
|
73
|
+
throw new Error(`Scenario file not found: ${opts.scenarioPath}`);
|
|
74
|
+
}
|
|
75
|
+
let scenario;
|
|
76
|
+
try {
|
|
77
|
+
scenario = JSON.parse(readFileSync(opts.scenarioPath, 'utf-8'));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
throw new Error(`Failed to parse scenario at ${opts.scenarioPath}. Ensure it contains valid JSON.`);
|
|
81
|
+
}
|
|
82
|
+
validateScenarioId(scenario.id);
|
|
83
|
+
// Load repo index
|
|
84
|
+
let repoIndex;
|
|
85
|
+
try {
|
|
86
|
+
repoIndex = JSON.parse(readFileSync(indexPath, 'utf-8'));
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
throw new Error(`Failed to parse repo index at ${indexPath}. Run "codeledger scan" to regenerate.`);
|
|
90
|
+
}
|
|
91
|
+
// Init ledger
|
|
92
|
+
const dbPath = join(cwd, config.workspace.db_path);
|
|
93
|
+
const ledger = new LedgerStore(dbPath);
|
|
94
|
+
ledger.init();
|
|
95
|
+
const headCommit = getHeadCommit(cwd);
|
|
96
|
+
const useWorktree = opts.useWorktree ?? true;
|
|
97
|
+
const worktreeRoot = opts.worktreeRoot ?? join(cwd, config.workspace.worktree_root);
|
|
98
|
+
// Clean up any orphaned worktrees from previous crashed runs
|
|
99
|
+
const orphansRemoved = cleanupAllWorktrees(cwd, worktreeRoot);
|
|
100
|
+
if (orphansRemoved > 0) {
|
|
101
|
+
console.log(`🧹 Cleaned up ${orphansRemoved} orphaned worktree${orphansRemoved > 1 ? 's' : ''} from a previous run`);
|
|
102
|
+
}
|
|
103
|
+
const results = [];
|
|
104
|
+
try {
|
|
105
|
+
for (const mode of scenario.modes) {
|
|
106
|
+
console.log(`\n🔄 Running mode: ${mode.name}`);
|
|
107
|
+
const modeAgentCmd = selectAgentCmdForMode(mode.name, opts);
|
|
108
|
+
const recorder = new RunRecorder(ledger);
|
|
109
|
+
recorder.startRun({
|
|
110
|
+
scenario_id: scenario.id,
|
|
111
|
+
mode: mode.name,
|
|
112
|
+
injection_mode: mode.context_injection === 'on'
|
|
113
|
+
? (opts.injectionOverride ?? mode.injection_mode ?? config.injection.default_mode)
|
|
114
|
+
: null,
|
|
115
|
+
repeat_num: 1,
|
|
116
|
+
agent: modeAgentCmd ?? (opts.guided ? 'guided' : null),
|
|
117
|
+
git_ref: headCommit,
|
|
118
|
+
started_at: new Date().toISOString(),
|
|
119
|
+
});
|
|
120
|
+
let worktreePath = cwd;
|
|
121
|
+
let cleanup = null;
|
|
122
|
+
// Create worktree sandbox if enabled
|
|
123
|
+
if (useWorktree && preflight.worktree_supported) {
|
|
124
|
+
try {
|
|
125
|
+
const wt = createWorktree({
|
|
126
|
+
repoRoot: cwd,
|
|
127
|
+
worktreeRoot,
|
|
128
|
+
commit: headCommit,
|
|
129
|
+
prefix: mode.name,
|
|
130
|
+
dependencyMode: config.harness.worktree_dependency_mode,
|
|
131
|
+
installCmd: config.harness.worktree_install_cmd,
|
|
132
|
+
});
|
|
133
|
+
worktreePath = wt.path;
|
|
134
|
+
cleanup = wt.cleanup;
|
|
135
|
+
// Register synchronous signal handler so Ctrl+C still cleans up
|
|
136
|
+
installSignalHandlers();
|
|
137
|
+
activeCleanup = () => {
|
|
138
|
+
try {
|
|
139
|
+
execFileSync('git', ['worktree', 'remove', '--force', wt.path], {
|
|
140
|
+
cwd,
|
|
141
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Best effort
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
console.log(` 📁 Worktree: ${worktreePath}`);
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
console.warn(` ⚠️ Failed to create worktree, running in-place: ${err}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
// Generate bundle if with_codeledger
|
|
156
|
+
let bundle = null;
|
|
157
|
+
let bundleMarkdown = null;
|
|
158
|
+
if (mode.context_injection === 'on') {
|
|
159
|
+
const budget = mode.budget ?? config.selector.default_budget;
|
|
160
|
+
bundle = await buildBundleViaEngine({
|
|
161
|
+
taskText: scenario.task.prompt,
|
|
162
|
+
repoIndex,
|
|
163
|
+
selectorConfig: config.selector,
|
|
164
|
+
budget,
|
|
165
|
+
indexPath: indexPath,
|
|
166
|
+
});
|
|
167
|
+
bundleMarkdown = renderBundleMarkdown(bundle);
|
|
168
|
+
recorder.recordEvent('bundle_generated', {
|
|
169
|
+
bundle_id: bundle.bundle_id,
|
|
170
|
+
files: bundle.files.length,
|
|
171
|
+
tokens: bundle.total_tokens,
|
|
172
|
+
});
|
|
173
|
+
console.log(` 📦 Bundle: ${bundle.files.length} files, ~${bundle.total_tokens} tokens`);
|
|
174
|
+
}
|
|
175
|
+
// Select adapter
|
|
176
|
+
const adapter = opts.guided
|
|
177
|
+
? new GuidedAdapter()
|
|
178
|
+
: new GenericAdapter();
|
|
179
|
+
// Run agent
|
|
180
|
+
recorder.recordEvent('agent_prompt', {
|
|
181
|
+
task: scenario.task.prompt,
|
|
182
|
+
mode: mode.name,
|
|
183
|
+
injection: mode.context_injection,
|
|
184
|
+
});
|
|
185
|
+
// Pick per-mode agent command if provided, otherwise fall back to agentCmd
|
|
186
|
+
const effectiveAgentCmd = mode.name === 'with_codeledger'
|
|
187
|
+
? (opts.agentCmdWith ?? opts.agentCmd)
|
|
188
|
+
: (opts.agentCmdWithout ?? opts.agentCmd);
|
|
189
|
+
if (effectiveAgentCmd) {
|
|
190
|
+
const source = mode.name === 'with_codeledger' && opts.agentCmdWith
|
|
191
|
+
? '--agentCmdWith'
|
|
192
|
+
: mode.name === 'without_codeledger' && opts.agentCmdWithout
|
|
193
|
+
? '--agentCmdWithout'
|
|
194
|
+
: '--agentCmd';
|
|
195
|
+
console.log(` 🔧 Agent command (${source}): ${effectiveAgentCmd}`);
|
|
196
|
+
}
|
|
197
|
+
const agentResult = await adapter.run({
|
|
198
|
+
scenario,
|
|
199
|
+
mode,
|
|
200
|
+
worktree_path: worktreePath,
|
|
201
|
+
bundle: bundle ?? undefined,
|
|
202
|
+
bundle_markdown: bundleMarkdown ?? undefined,
|
|
203
|
+
agent_cmd: modeAgentCmd,
|
|
204
|
+
guided_timeout_sec: opts.guidedTimeoutSec ?? config.harness.guided_timeout_sec,
|
|
205
|
+
});
|
|
206
|
+
console.log(` 🤖 Agent completed (exit: ${agentResult.exit_code}, ${agentResult.duration_ms}ms)`);
|
|
207
|
+
// Run acceptance commands
|
|
208
|
+
const acceptanceResults = [];
|
|
209
|
+
let acceptanceDurationMs = 0;
|
|
210
|
+
const usedCmdNames = new Set();
|
|
211
|
+
for (const cmd of scenario.task.acceptance.commands) {
|
|
212
|
+
let cmdName = inferCommandName(cmd);
|
|
213
|
+
if (usedCmdNames.has(cmdName)) {
|
|
214
|
+
let suffix = 2;
|
|
215
|
+
while (usedCmdNames.has(`${cmdName}_${suffix}`))
|
|
216
|
+
suffix++;
|
|
217
|
+
cmdName = `${cmdName}_${suffix}`;
|
|
218
|
+
}
|
|
219
|
+
usedCmdNames.add(cmdName);
|
|
220
|
+
console.log(` 🧪 Running: ${cmd}`);
|
|
221
|
+
const execResult = execCommand(cmd, { cwd: worktreePath, timeout: 120_000 });
|
|
222
|
+
const passed = execResult.exit_code === 0;
|
|
223
|
+
acceptanceDurationMs += execResult.duration_ms;
|
|
224
|
+
acceptanceResults.push({
|
|
225
|
+
name: cmdName,
|
|
226
|
+
pass: passed,
|
|
227
|
+
failures: passed ? 0 : 1,
|
|
228
|
+
});
|
|
229
|
+
recorder.recordEvent('cmd_exec', {
|
|
230
|
+
command: cmd,
|
|
231
|
+
exit_code: execResult.exit_code,
|
|
232
|
+
duration_ms: execResult.duration_ms,
|
|
233
|
+
});
|
|
234
|
+
console.log(` ${passed ? '✅' : '❌'} ${cmdName}: ${passed ? 'passed' : 'failed'}`);
|
|
235
|
+
}
|
|
236
|
+
const accepted = acceptanceResults.every((r) => r.pass);
|
|
237
|
+
const totalDurationMs = agentResult.duration_ms + acceptanceDurationMs;
|
|
238
|
+
const promptTokenEstimate = estimatePromptTokenUsage(scenario.task.prompt, bundleMarkdown, mode.context_injection, opts.injectionOverride ?? mode.injection_mode ?? config.injection.default_mode);
|
|
239
|
+
// Collect real metrics from the worktree
|
|
240
|
+
const wtMetrics = collectWorktreeMetrics(worktreePath, agentResult.stdout_path ?? null);
|
|
241
|
+
// Build metrics
|
|
242
|
+
const metrics = {
|
|
243
|
+
duration_sec: Math.round(totalDurationMs / 1000),
|
|
244
|
+
duration_ms_total: totalDurationMs,
|
|
245
|
+
agent_duration_ms: agentResult.duration_ms,
|
|
246
|
+
acceptance_duration_ms: acceptanceDurationMs,
|
|
247
|
+
attempt_number: 1,
|
|
248
|
+
accepted,
|
|
249
|
+
commands: Object.fromEntries(acceptanceResults.map((r) => [r.name, r])),
|
|
250
|
+
files_read_count: wtMetrics.filesRead,
|
|
251
|
+
files_changed_count: wtMetrics.filesChanged,
|
|
252
|
+
iterations: wtMetrics.iterations,
|
|
253
|
+
reverts: wtMetrics.reverts,
|
|
254
|
+
token_estimate: promptTokenEstimate,
|
|
255
|
+
};
|
|
256
|
+
// Build injection proof
|
|
257
|
+
let injectionProof = null;
|
|
258
|
+
if (bundle && mode.context_injection === 'on') {
|
|
259
|
+
injectionProof = {
|
|
260
|
+
mode: opts.injectionOverride ?? mode.injection_mode ?? config.injection.default_mode,
|
|
261
|
+
bundle_id: bundle.bundle_id,
|
|
262
|
+
bundle_tokens: bundle.total_tokens,
|
|
263
|
+
selected_files: bundle.files.length,
|
|
264
|
+
top_files: bundle.files.slice(0, 5).map((f) => f.path),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
const reportRun = {
|
|
268
|
+
mode: mode.name,
|
|
269
|
+
injection: injectionProof,
|
|
270
|
+
repeat: 1,
|
|
271
|
+
metrics,
|
|
272
|
+
...(bundle
|
|
273
|
+
? {
|
|
274
|
+
bundle: {
|
|
275
|
+
bundle_id: bundle.bundle_id,
|
|
276
|
+
token_budget: bundle.budget.tokens ?? null,
|
|
277
|
+
max_files: bundle.budget.max_files ?? null,
|
|
278
|
+
selected_files: bundle.files.length,
|
|
279
|
+
},
|
|
280
|
+
}
|
|
281
|
+
: {}),
|
|
282
|
+
};
|
|
283
|
+
results.push(reportRun);
|
|
284
|
+
recorder.finishRun('completed', metrics);
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
recorder.finishRun('failed', { error: String(err) });
|
|
288
|
+
throw err;
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
activeCleanup = null;
|
|
292
|
+
if (cleanup && !opts.keepWorktree) {
|
|
293
|
+
await cleanup();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
finally {
|
|
299
|
+
removeSignalHandlers();
|
|
300
|
+
}
|
|
301
|
+
ledger.close();
|
|
302
|
+
return results;
|
|
303
|
+
}
|
|
304
|
+
const SAFE_SCENARIO_ID = /^[a-zA-Z0-9_-]+$/;
|
|
305
|
+
function validateScenarioId(id) {
|
|
306
|
+
if (!SAFE_SCENARIO_ID.test(id)) {
|
|
307
|
+
throw new Error(`Invalid scenario ID: "${id}". IDs must contain only alphanumeric, underscore, and hyphen characters.`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
export function selectAgentCmdForMode(modeName, opts) {
|
|
311
|
+
if (modeName === 'without_codeledger') {
|
|
312
|
+
return opts.agentCmdWithout ?? opts.agentCmd;
|
|
313
|
+
}
|
|
314
|
+
if (modeName === 'with_codeledger') {
|
|
315
|
+
return opts.agentCmdWith ?? opts.agentCmd;
|
|
316
|
+
}
|
|
317
|
+
return opts.agentCmd;
|
|
318
|
+
}
|
|
319
|
+
function estimatePromptTokenUsage(taskText, bundleMarkdown, contextInjection, injectionMode) {
|
|
320
|
+
if (contextInjection === 'off') {
|
|
321
|
+
return estimateTextTokens(taskText);
|
|
322
|
+
}
|
|
323
|
+
if (!bundleMarkdown) {
|
|
324
|
+
return estimateTextTokens(taskText);
|
|
325
|
+
}
|
|
326
|
+
if (injectionMode === 'prompt_prepend') {
|
|
327
|
+
return estimateTextTokens(injectIntoPrompt(taskText, bundleMarkdown));
|
|
328
|
+
}
|
|
329
|
+
// file_drop and guided include prompt + separate bundle exposure.
|
|
330
|
+
return estimateTextTokens(taskText) + estimateTextTokens(bundleMarkdown);
|
|
331
|
+
}
|
|
332
|
+
function estimateTextTokens(text) {
|
|
333
|
+
return Math.ceil(text.length / 4);
|
|
334
|
+
}
|
|
335
|
+
function inferCommandName(cmd) {
|
|
336
|
+
if (cmd.includes('test'))
|
|
337
|
+
return 'tests';
|
|
338
|
+
if (cmd.includes('lint'))
|
|
339
|
+
return 'lint';
|
|
340
|
+
if (cmd.includes('build'))
|
|
341
|
+
return 'build';
|
|
342
|
+
return cmd;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Collect real metrics from the worktree after the agent has run.
|
|
346
|
+
*
|
|
347
|
+
* - filesChanged: count of files modified/added/deleted vs HEAD (git diff)
|
|
348
|
+
* - filesRead: heuristic count from agent stdout (lines matching common
|
|
349
|
+
* file-read patterns), falls back to 0 when no log is available.
|
|
350
|
+
* - iterations: heuristic count of retry/re-attempt cycles detected in the
|
|
351
|
+
* agent stdout log. Defaults to 1 (the initial attempt always counts).
|
|
352
|
+
* - reverts: count of revert-like git operations (reset, checkout, revert)
|
|
353
|
+
* detected in the agent stdout log or git reflog.
|
|
354
|
+
*/
|
|
355
|
+
function collectWorktreeMetrics(worktreePath, stdoutLogPath) {
|
|
356
|
+
// --- files changed via git diff ---
|
|
357
|
+
let filesChanged = 0;
|
|
358
|
+
try {
|
|
359
|
+
const diffOutput = execFileSync('git', ['diff', '--name-only', 'HEAD'], { cwd: worktreePath, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
360
|
+
// Also count untracked files the agent may have created
|
|
361
|
+
const untrackedOutput = execFileSync('git', ['ls-files', '--others', '--exclude-standard'], { cwd: worktreePath, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
362
|
+
filesChanged =
|
|
363
|
+
(diffOutput ? diffOutput.split('\n').length : 0) +
|
|
364
|
+
(untrackedOutput ? untrackedOutput.split('\n').length : 0);
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
// Not inside a git worktree or git unavailable — leave at 0
|
|
368
|
+
}
|
|
369
|
+
// --- Parse agent stdout log for file reads, iterations, and reverts ---
|
|
370
|
+
let filesRead = 0;
|
|
371
|
+
let iterations = 1; // The initial attempt always counts
|
|
372
|
+
let logReverts = 0;
|
|
373
|
+
if (stdoutLogPath && existsSync(stdoutLogPath)) {
|
|
374
|
+
try {
|
|
375
|
+
const log = readFileSync(stdoutLogPath, 'utf-8');
|
|
376
|
+
// Count unique file-path-like tokens the agent mentioned reading
|
|
377
|
+
const fileRefs = new Set();
|
|
378
|
+
for (const match of log.matchAll(/(?:reading|read|open(?:ed)?|cat)\s+["']?([^\s"']+\.\w{1,6})["']?/gi)) {
|
|
379
|
+
fileRefs.add(match[1]);
|
|
380
|
+
}
|
|
381
|
+
filesRead = fileRefs.size;
|
|
382
|
+
// Detect iteration/retry patterns in agent output
|
|
383
|
+
// Common patterns: "retry", "retrying", "attempt N", "re-running",
|
|
384
|
+
// "trying again", "iteration N", loop indicators
|
|
385
|
+
const retryPatterns = [
|
|
386
|
+
/\bretry(?:ing)?\b/gi,
|
|
387
|
+
/\bre-?running\b/gi,
|
|
388
|
+
/\btrying again\b/gi,
|
|
389
|
+
/\battempt\s+(\d+)/gi,
|
|
390
|
+
/\biteration\s+(\d+)/gi,
|
|
391
|
+
/\bfailed[,.]?\s*(?:re-?)?trying\b/gi,
|
|
392
|
+
/\broll(?:ing)?\s*back\b/gi,
|
|
393
|
+
];
|
|
394
|
+
for (const pattern of retryPatterns) {
|
|
395
|
+
for (const match of log.matchAll(pattern)) {
|
|
396
|
+
// If a numbered attempt/iteration, use it directly if higher
|
|
397
|
+
const num = match[1] ? parseInt(match[1], 10) : 0;
|
|
398
|
+
if (num > iterations) {
|
|
399
|
+
iterations = num;
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
// Each retry match beyond the initial attempt adds 1
|
|
403
|
+
iterations++;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
// Detect revert-like operations in agent output
|
|
408
|
+
const revertPatterns = [
|
|
409
|
+
/\bgit\s+revert\b/gi,
|
|
410
|
+
/\bgit\s+reset\b/gi,
|
|
411
|
+
/\bgit\s+checkout\s+--\s/gi,
|
|
412
|
+
/\bgit\s+restore\b/gi,
|
|
413
|
+
/\breverting\b/gi,
|
|
414
|
+
/\bundoing\b/gi,
|
|
415
|
+
];
|
|
416
|
+
for (const pattern of revertPatterns) {
|
|
417
|
+
const matches = log.match(pattern);
|
|
418
|
+
logReverts += matches ? matches.length : 0;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
// Non-fatal
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// --- Detect reverts via git reflog in worktree ---
|
|
426
|
+
let gitReverts = 0;
|
|
427
|
+
try {
|
|
428
|
+
const reflogOutput = execFileSync('git', ['reflog', '--format=%gs'], { cwd: worktreePath, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
429
|
+
if (reflogOutput) {
|
|
430
|
+
for (const line of reflogOutput.split('\n')) {
|
|
431
|
+
if (/\b(?:reset|revert|checkout)\b/i.test(line)) {
|
|
432
|
+
gitReverts++;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
// Not inside a git worktree or git unavailable
|
|
439
|
+
}
|
|
440
|
+
const reverts = logReverts + gitReverts;
|
|
441
|
+
return { filesChanged, filesRead, iterations, reverts };
|
|
442
|
+
}
|
|
443
|
+
//# sourceMappingURL=scenario-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenario-runner.js","sourceRoot":"","sources":["../src/scenario-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenario-runner.test.d.ts","sourceRoot":"","sources":["../src/scenario-runner.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { selectAgentCmdForMode } from './scenario-runner.js';
|
|
3
|
+
describe('selectAgentCmdForMode', () => {
|
|
4
|
+
it('prefers mode-specific agent command for without_codeledger', () => {
|
|
5
|
+
const cmd = selectAgentCmdForMode('without_codeledger', {
|
|
6
|
+
agentCmd: 'agent default',
|
|
7
|
+
agentCmdWithout: 'agent without',
|
|
8
|
+
agentCmdWith: 'agent with',
|
|
9
|
+
});
|
|
10
|
+
expect(cmd).toBe('agent without');
|
|
11
|
+
});
|
|
12
|
+
it('prefers mode-specific agent command for with_codeledger', () => {
|
|
13
|
+
const cmd = selectAgentCmdForMode('with_codeledger', {
|
|
14
|
+
agentCmd: 'agent default',
|
|
15
|
+
agentCmdWithout: 'agent without',
|
|
16
|
+
agentCmdWith: 'agent with',
|
|
17
|
+
});
|
|
18
|
+
expect(cmd).toBe('agent with');
|
|
19
|
+
});
|
|
20
|
+
it('falls back to default agent command when override is missing', () => {
|
|
21
|
+
const cmd = selectAgentCmdForMode('with_codeledger', {
|
|
22
|
+
agentCmd: 'agent default',
|
|
23
|
+
agentCmdWithout: 'agent without',
|
|
24
|
+
});
|
|
25
|
+
expect(cmd).toBe('agent default');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=scenario-runner.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenario-runner.test.js","sourceRoot":"","sources":["../src/scenario-runner.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,GAAG,GAAG,qBAAqB,CAAC,oBAAoB,EAAE;YACtD,QAAQ,EAAE,eAAe;YACzB,eAAe,EAAE,eAAe;YAChC,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAC;QAEH,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,GAAG,GAAG,qBAAqB,CAAC,iBAAiB,EAAE;YACnD,QAAQ,EAAE,eAAe;YACzB,eAAe,EAAE,eAAe;YAChC,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAC;QAEH,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,GAAG,GAAG,qBAAqB,CAAC,iBAAiB,EAAE;YACnD,QAAQ,EAAE,eAAe;YACzB,eAAe,EAAE,eAAe;SACjC,CAAC,CAAC;QAEH,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { WorktreeHandle, WorktreeDependencyMode } from '@codeledger/types';
|
|
2
|
+
export interface WorktreeSpec {
|
|
3
|
+
repoRoot: string;
|
|
4
|
+
worktreeRoot: string;
|
|
5
|
+
commit: string;
|
|
6
|
+
prefix?: string;
|
|
7
|
+
dependencyMode?: WorktreeDependencyMode;
|
|
8
|
+
/** Custom install command that replaces the default pnpm/npm fallback chain. */
|
|
9
|
+
installCmd?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function createWorktree(spec: WorktreeSpec): WorktreeHandle;
|
|
12
|
+
/**
|
|
13
|
+
* Remove all orphaned codeledger sandbox worktrees.
|
|
14
|
+
*
|
|
15
|
+
* 1. Runs `git worktree prune` to clean stale bookkeeping entries.
|
|
16
|
+
* 2. Lists remaining worktrees via `git worktree list --porcelain`.
|
|
17
|
+
* 3. Removes any whose path contains `codeledger-sandbox-`.
|
|
18
|
+
* 4. Deletes leftover sandbox directories under `worktreeRoot` that git
|
|
19
|
+
* no longer tracks (e.g. from a hard kill where the entry was never
|
|
20
|
+
* registered or was already pruned but the directory survived).
|
|
21
|
+
*
|
|
22
|
+
* Returns the number of worktrees removed.
|
|
23
|
+
*/
|
|
24
|
+
export declare function cleanupAllWorktrees(repoRoot: string, worktreeRoot?: string): number;
|
|
25
|
+
//# sourceMappingURL=worktree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAKhF,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,cAAc,CA0CjE;AAoDD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CA4EnF"}
|