autor3search-typescript 0.1.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 (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/dist/benchproto/types.d.ts +32 -0
  4. package/dist/benchproto/types.js +28 -0
  5. package/dist/benchproto/types.js.map +1 -0
  6. package/dist/benchrun/child.d.ts +1 -0
  7. package/dist/benchrun/child.js +222 -0
  8. package/dist/benchrun/child.js.map +1 -0
  9. package/dist/benchrun/invoke.d.ts +61 -0
  10. package/dist/benchrun/invoke.js +152 -0
  11. package/dist/benchrun/invoke.js.map +1 -0
  12. package/dist/cli/cmd-baseline.d.ts +15 -0
  13. package/dist/cli/cmd-baseline.js +292 -0
  14. package/dist/cli/cmd-baseline.js.map +1 -0
  15. package/dist/cli/cmd-doctor.d.ts +11 -0
  16. package/dist/cli/cmd-doctor.js +26 -0
  17. package/dist/cli/cmd-doctor.js.map +1 -0
  18. package/dist/cli/cmd-eval.d.ts +30 -0
  19. package/dist/cli/cmd-eval.js +173 -0
  20. package/dist/cli/cmd-eval.js.map +1 -0
  21. package/dist/cli/cmd-init.d.ts +16 -0
  22. package/dist/cli/cmd-init.js +343 -0
  23. package/dist/cli/cmd-init.js.map +1 -0
  24. package/dist/cli/cmd-profile.d.ts +11 -0
  25. package/dist/cli/cmd-profile.js +140 -0
  26. package/dist/cli/cmd-profile.js.map +1 -0
  27. package/dist/cli/cmd-report.d.ts +15 -0
  28. package/dist/cli/cmd-report.js +70 -0
  29. package/dist/cli/cmd-report.js.map +1 -0
  30. package/dist/cli/cmd-status.d.ts +11 -0
  31. package/dist/cli/cmd-status.js +143 -0
  32. package/dist/cli/cmd-status.js.map +1 -0
  33. package/dist/cli/cmd-stop.d.ts +15 -0
  34. package/dist/cli/cmd-stop.js +143 -0
  35. package/dist/cli/cmd-stop.js.map +1 -0
  36. package/dist/cli/main.d.ts +31 -0
  37. package/dist/cli/main.js +133 -0
  38. package/dist/cli/main.js.map +1 -0
  39. package/dist/cli/runctx.d.ts +29 -0
  40. package/dist/cli/runctx.js +49 -0
  41. package/dist/cli/runctx.js.map +1 -0
  42. package/dist/cli/speedup.d.ts +14 -0
  43. package/dist/cli/speedup.js +25 -0
  44. package/dist/cli/speedup.js.map +1 -0
  45. package/dist/config/load.d.ts +6 -0
  46. package/dist/config/load.js +172 -0
  47. package/dist/config/load.js.map +1 -0
  48. package/dist/config/schema.d.ts +42 -0
  49. package/dist/config/schema.js +25 -0
  50. package/dist/config/schema.js.map +1 -0
  51. package/dist/discover/benchmarks.d.ts +26 -0
  52. package/dist/discover/benchmarks.js +88 -0
  53. package/dist/discover/benchmarks.js.map +1 -0
  54. package/dist/discover/files.d.ts +23 -0
  55. package/dist/discover/files.js +82 -0
  56. package/dist/discover/files.js.map +1 -0
  57. package/dist/doctor/doctor.d.ts +56 -0
  58. package/dist/doctor/doctor.js +255 -0
  59. package/dist/doctor/doctor.js.map +1 -0
  60. package/dist/freeze/freeze.d.ts +23 -0
  61. package/dist/freeze/freeze.js +107 -0
  62. package/dist/freeze/freeze.js.map +1 -0
  63. package/dist/freeze/manifest.d.ts +6 -0
  64. package/dist/freeze/manifest.js +9 -0
  65. package/dist/freeze/manifest.js.map +1 -0
  66. package/dist/freeze/safepath.d.ts +8 -0
  67. package/dist/freeze/safepath.js +22 -0
  68. package/dist/freeze/safepath.js.map +1 -0
  69. package/dist/gitx/git.d.ts +87 -0
  70. package/dist/gitx/git.js +235 -0
  71. package/dist/gitx/git.js.map +1 -0
  72. package/dist/measure/interleave.d.ts +32 -0
  73. package/dist/measure/interleave.js +42 -0
  74. package/dist/measure/interleave.js.map +1 -0
  75. package/dist/pipeline/eval.d.ts +67 -0
  76. package/dist/pipeline/eval.js +495 -0
  77. package/dist/pipeline/eval.js.map +1 -0
  78. package/dist/pm/detect.d.ts +17 -0
  79. package/dist/pm/detect.js +131 -0
  80. package/dist/pm/detect.js.map +1 -0
  81. package/dist/profile/profile.d.ts +53 -0
  82. package/dist/profile/profile.js +105 -0
  83. package/dist/profile/profile.js.map +1 -0
  84. package/dist/results/results.d.ts +65 -0
  85. package/dist/results/results.js +214 -0
  86. package/dist/results/results.js.map +1 -0
  87. package/dist/runner/exec.d.ts +69 -0
  88. package/dist/runner/exec.js +185 -0
  89. package/dist/runner/exec.js.map +1 -0
  90. package/dist/scope/scope.d.ts +20 -0
  91. package/dist/scope/scope.js +70 -0
  92. package/dist/scope/scope.js.map +1 -0
  93. package/dist/state/baseline.d.ts +35 -0
  94. package/dist/state/baseline.js +38 -0
  95. package/dist/state/baseline.js.map +1 -0
  96. package/dist/state/home.d.ts +9 -0
  97. package/dist/state/home.js +72 -0
  98. package/dist/state/home.js.map +1 -0
  99. package/dist/state/lock.d.ts +11 -0
  100. package/dist/state/lock.js +89 -0
  101. package/dist/state/lock.js.map +1 -0
  102. package/dist/state/runnaming.d.ts +27 -0
  103. package/dist/state/runnaming.js +47 -0
  104. package/dist/state/runnaming.js.map +1 -0
  105. package/dist/state/stop.d.ts +28 -0
  106. package/dist/state/stop.js +59 -0
  107. package/dist/state/stop.js.map +1 -0
  108. package/dist/stats/delta.d.ts +29 -0
  109. package/dist/stats/delta.js +41 -0
  110. package/dist/stats/delta.js.map +1 -0
  111. package/dist/stats/geomean.d.ts +4 -0
  112. package/dist/stats/geomean.js +21 -0
  113. package/dist/stats/geomean.js.map +1 -0
  114. package/dist/stats/mannwhitney.d.ts +27 -0
  115. package/dist/stats/mannwhitney.js +158 -0
  116. package/dist/stats/mannwhitney.js.map +1 -0
  117. package/dist/verdict/verdict.d.ts +46 -0
  118. package/dist/verdict/verdict.js +102 -0
  119. package/dist/verdict/verdict.js.map +1 -0
  120. package/dist/version.d.ts +2 -0
  121. package/dist/version.js +3 -0
  122. package/dist/version.js.map +1 -0
  123. package/package.json +35 -0
  124. package/templates/program.md +131 -0
@@ -0,0 +1,140 @@
1
+ import path from 'node:path';
2
+ import { parseArgs } from 'node:util';
3
+ import { loadConfig, parseDuration } from '../config/load.js';
4
+ import { CONFIG_PATH } from '../config/schema.js';
5
+ import { discoverBenchmarks } from '../discover/benchmarks.js';
6
+ import { profileBenchmark } from '../profile/profile.js';
7
+ /** How many hot functions to print per benchmark. */
8
+ const TOP_N = 15;
9
+ /**
10
+ * Repo-relative directory `.cpuprofile` files are written under. Alongside
11
+ * `config.yaml`, not in the (baseline-only) state home: `profile` must work
12
+ * without a baseline ever existing, and the output is meant for a human to
13
+ * open locally, not for the harness's own cross-run state.
14
+ */
15
+ const PROFILE_DIRNAME = path.join('.autor3search', 'profiles');
16
+ function fail(message) {
17
+ process.stderr.write(`error: ${message}\n`);
18
+ return 2;
19
+ }
20
+ function messageOf(e) {
21
+ return e instanceof Error ? e.message : String(e);
22
+ }
23
+ /** Declared benchmark ids that are not among the discovered benchmarks. Mirrors cmd-baseline.ts. */
24
+ function missingDeclared(declared, discovered) {
25
+ const found = new Set(discovered.map((b) => b.id));
26
+ return declared.filter((id) => !found.has(id));
27
+ }
28
+ function printHotFrames(frames, totalSelfUs) {
29
+ if (frames.length === 0) {
30
+ process.stdout.write(' no user-code samples were captured (every sampled frame was Node internals) -- this ' +
31
+ 'benchmark may be too fast to profile meaningfully; try a longer -benchtime.\n');
32
+ return;
33
+ }
34
+ for (const f of frames.slice(0, TOP_N)) {
35
+ const pct = totalSelfUs > 0 ? (f.selfTimeUs / totalSelfUs) * 100 : 0;
36
+ const name = f.functionName.length > 0 ? f.functionName : '(anonymous)';
37
+ process.stdout.write(` ${pct.toFixed(1).padStart(5)}% ${name} ${f.url}:${f.lineNumber}\n`);
38
+ }
39
+ }
40
+ /**
41
+ * `profile`: runs the declared benchmarks under Node's built-in `--cpu-prof`
42
+ * sampling profiler and prints where time actually goes.
43
+ *
44
+ * Deliberately does not touch `baseline` or the state home at all: this is
45
+ * reconnaissance a human runs BEFORE starting the loop, to decide whether
46
+ * the benchmarks even point at the right code, so it must work with only
47
+ * `.autor3search/config.yaml` in place.
48
+ */
49
+ export async function cmdProfile(ctx, argv) {
50
+ let benchtimeOverride;
51
+ try {
52
+ const normalized = argv.map((a) => (/^-[A-Za-z][A-Za-z-]+$/.test(a) ? `-${a}` : a));
53
+ const { values } = parseArgs({
54
+ args: normalized,
55
+ options: { benchtime: { type: 'string' } },
56
+ strict: true,
57
+ allowPositionals: false,
58
+ });
59
+ benchtimeOverride = values.benchtime;
60
+ }
61
+ catch (e) {
62
+ return fail(messageOf(e));
63
+ }
64
+ let config;
65
+ try {
66
+ config = await loadConfig(ctx.configPath);
67
+ }
68
+ catch (e) {
69
+ return fail(messageOf(e));
70
+ }
71
+ let discovered;
72
+ try {
73
+ discovered = await discoverBenchmarks(ctx.repoRoot);
74
+ }
75
+ catch (e) {
76
+ return fail(`could not discover benchmarks: ${messageOf(e)}`);
77
+ }
78
+ let benchmarks;
79
+ const declared = config.benchmarks;
80
+ if (declared.length > 0) {
81
+ const missing = missingDeclared(declared, discovered);
82
+ if (missing.length > 0) {
83
+ return fail(`config declares benchmark(s) that no longer exist: ${missing.join(', ')}. Update ` +
84
+ `${CONFIG_PATH} or restore the missing benchmark(s).`);
85
+ }
86
+ const byId = new Map(discovered.map((b) => [b.id, b]));
87
+ benchmarks = declared.map((id) => byId.get(id));
88
+ }
89
+ else {
90
+ benchmarks = discovered;
91
+ }
92
+ if (benchmarks.length === 0) {
93
+ return fail('no benchmarks found: there is nothing to profile');
94
+ }
95
+ let benchtimeMs;
96
+ let warmupMs;
97
+ let timeoutMs;
98
+ try {
99
+ benchtimeMs = parseDuration(benchtimeOverride ?? config.benchtime);
100
+ warmupMs = parseDuration(config.warmup);
101
+ timeoutMs = parseDuration(config.timeout);
102
+ }
103
+ catch (e) {
104
+ return fail(messageOf(e));
105
+ }
106
+ const profileDir = path.join(ctx.repoRoot, PROFILE_DIRNAME);
107
+ process.stdout.write('autor3search-typescript profile -- where does the time actually go?\n\n');
108
+ let failures = 0;
109
+ for (const b of benchmarks) {
110
+ process.stdout.write(`${b.id}\n`);
111
+ try {
112
+ const result = await profileBenchmark({
113
+ cwd: ctx.repoRoot,
114
+ benchFileAbs: path.join(ctx.repoRoot, b.file),
115
+ fn: b.fn,
116
+ id: b.id,
117
+ benchtimeMs,
118
+ warmupMs,
119
+ timeoutMs,
120
+ nodeArgs: config.nodeArgs,
121
+ profileDir,
122
+ });
123
+ printHotFrames(result.hotFrames, result.totalSelfUs);
124
+ process.stdout.write(` profile written to ${result.profilePath}\n`);
125
+ process.stdout.write(' open it in Chrome DevTools (chrome://inspect -> Open dedicated DevTools for Node -> ' +
126
+ 'Profiler tab -> Load) or drag it into https://www.speedscope.app/\n');
127
+ }
128
+ catch (e) {
129
+ failures++;
130
+ process.stderr.write(` error: ${messageOf(e)}\n`);
131
+ }
132
+ process.stdout.write('\n');
133
+ }
134
+ if (failures > 0) {
135
+ process.stderr.write(`${failures} of ${benchmarks.length} benchmark(s) could not be profiled\n`);
136
+ return 2;
137
+ }
138
+ return 0;
139
+ }
140
+ //# sourceMappingURL=cmd-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cmd-profile.js","sourceRoot":"","sources":["../../src/cli/cmd-profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { RunCtx } from './runctx.js';
2
+ /**
3
+ * `report`: what a human reads in the morning after an unattended run --
4
+ * a summary of `results.tsv`, and nothing else. Read-only: it never writes
5
+ * anything, and it never spawns anything (no git, no subprocess), so it is
6
+ * always safe to run alongside a live loop.
7
+ *
8
+ * A missing (or header-only) `results.tsv` is a real, valid state -- a
9
+ * repository that has never run an experiment -- so it is reported as "no
10
+ * experiments recorded yet," not as an error. `loadRows` already treats a
11
+ * missing file as an empty log; this only needs to word the empty case
12
+ * helpfully instead of printing zeroed-out counts a human could misread as
13
+ * "something ran and did nothing."
14
+ */
15
+ export declare function cmdReport(ctx: RunCtx, _argv: readonly string[]): Promise<number>;
@@ -0,0 +1,70 @@
1
+ import { shortSha } from '../gitx/git.js';
2
+ import { RESULTS_PATH, loadRows, summarize } from '../results/results.js';
3
+ import { formatCumulativeSpeedup } from './speedup.js';
4
+ const STATUSES = ['keep', 'discard', 'fail', 'crash'];
5
+ function fail(message) {
6
+ process.stderr.write(`error: ${message}\n`);
7
+ return 2;
8
+ }
9
+ function messageOf(e) {
10
+ return e instanceof Error ? e.message : String(e);
11
+ }
12
+ /**
13
+ * `report`: what a human reads in the morning after an unattended run --
14
+ * a summary of `results.tsv`, and nothing else. Read-only: it never writes
15
+ * anything, and it never spawns anything (no git, no subprocess), so it is
16
+ * always safe to run alongside a live loop.
17
+ *
18
+ * A missing (or header-only) `results.tsv` is a real, valid state -- a
19
+ * repository that has never run an experiment -- so it is reported as "no
20
+ * experiments recorded yet," not as an error. `loadRows` already treats a
21
+ * missing file as an empty log; this only needs to word the empty case
22
+ * helpfully instead of printing zeroed-out counts a human could misread as
23
+ * "something ran and did nothing."
24
+ */
25
+ export async function cmdReport(ctx, _argv) {
26
+ let rows;
27
+ try {
28
+ rows = await loadRows(ctx.resultsPath);
29
+ }
30
+ catch (e) {
31
+ return fail(messageOf(e));
32
+ }
33
+ const lines = ['autor3search-typescript report', ''];
34
+ if (rows.length === 0) {
35
+ lines.push(`no experiments recorded yet (${RESULTS_PATH} does not exist, or has no rows). Run "eval" ` +
36
+ 'at least once to populate it.');
37
+ process.stdout.write(`${lines.join('\n')}\n`);
38
+ return 0;
39
+ }
40
+ const summary = summarize(rows);
41
+ lines.push(`experiments: ${rows.length} total`);
42
+ lines.push(` ${STATUSES.map((s) => `${s}: ${summary.counts[s] ?? 0}`).join(' ')}`);
43
+ lines.push('');
44
+ // Deliberately spelled out rather than left implicit: each KEEP advances
45
+ // the measurement baseline to the just-kept commit, so every kept row's
46
+ // own `score` is only that experiment's incremental contribution on top
47
+ // of the last win -- never a re-measurement against the run's original
48
+ // starting point. Only the PRODUCT of every kept score is the true
49
+ // end-to-end speedup since the run began; printing (or reading) the
50
+ // latest row's score alone would silently understate or overstate that,
51
+ // depending on how many wins came before it.
52
+ lines.push(`cumulative speedup: ${formatCumulativeSpeedup(summary.cumulativeSpeedup)} ` +
53
+ "(the product of every KEPT experiment's own score, compounded across the whole run -- " +
54
+ "not the most recent experiment's score by itself)");
55
+ if (summary.topWins.length > 0) {
56
+ lines.push('');
57
+ lines.push('largest individual wins (own score, biggest improvement first):');
58
+ for (const w of summary.topWins) {
59
+ const desc = w.description.length > 0 ? ` -- ${w.description}` : '';
60
+ lines.push(` ${shortSha(w.commit)} score=${w.score.toFixed(4)} best_bench_delta=${w.bestBenchDelta.toFixed(2)}%${desc}`);
61
+ }
62
+ }
63
+ else {
64
+ lines.push('');
65
+ lines.push('no kept experiments yet.');
66
+ }
67
+ process.stdout.write(`${lines.join('\n')}\n`);
68
+ return 0;
69
+ }
70
+ //# sourceMappingURL=cmd-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cmd-report.js","sourceRoot":"","sources":["../../src/cli/cmd-report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { RunCtx } from './runctx.js';
2
+ /**
3
+ * `status`: reports where a run is, without changing it. It reads only the
4
+ * baseline record, `results.tsv`, the eval lock and the stop file (plus two
5
+ * read-only `git` queries for the human's own current branch/HEAD) --
6
+ * nothing here is ever written. `-tag` resolves the run directory from the
7
+ * repo root and the tag alone (see `state/home.ts`'s `runDir`), independent
8
+ * of whatever branch the repository happens to be on right now, so a human
9
+ * who has wandered off the run branch can still check on it.
10
+ */
11
+ export declare function cmdStatus(ctx: RunCtx, argv: readonly string[]): Promise<number>;
@@ -0,0 +1,143 @@
1
+ import { access } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { parseArgs } from 'node:util';
4
+ import { currentBranch, headCommit, shortSha } from '../gitx/git.js';
5
+ import { loadRows, summarize } from '../results/results.js';
6
+ import { readBaseline } from '../state/baseline.js';
7
+ import { runDir } from '../state/home.js';
8
+ import { readEvalLock } from '../state/lock.js';
9
+ import { BRANCH_PREFIX, WORKTREE_DIRNAME, inferTagFromBranch } from '../state/runnaming.js';
10
+ import { readStop } from '../state/stop.js';
11
+ import { formatCumulativeSpeedup } from './speedup.js';
12
+ const STATUSES = ['keep', 'discard', 'fail', 'crash'];
13
+ function fail(message) {
14
+ process.stderr.write(`error: ${message}\n`);
15
+ return 2;
16
+ }
17
+ function messageOf(e) {
18
+ return e instanceof Error ? e.message : String(e);
19
+ }
20
+ async function exists(p) {
21
+ try {
22
+ await access(p);
23
+ return true;
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }
29
+ /**
30
+ * Mirrors lock.ts's private `isAlive` (not exported): EPERM means the
31
+ * process exists but we lack permission to signal it, which is still
32
+ * "alive." `status` only ever uses this to word its report ("running" vs.
33
+ * "a crashed eval's stale lock") -- it never reclaims anything, which stays
34
+ * `acquireEvalLock`'s job alone, so a merely-informational check here can
35
+ * never race a real acquire.
36
+ */
37
+ function isPidAlive(pid) {
38
+ try {
39
+ process.kill(pid, 0);
40
+ return true;
41
+ }
42
+ catch (err) {
43
+ return err.code === 'EPERM';
44
+ }
45
+ }
46
+ async function tryGit(f) {
47
+ try {
48
+ return await f();
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
54
+ /**
55
+ * `status`: reports where a run is, without changing it. It reads only the
56
+ * baseline record, `results.tsv`, the eval lock and the stop file (plus two
57
+ * read-only `git` queries for the human's own current branch/HEAD) --
58
+ * nothing here is ever written. `-tag` resolves the run directory from the
59
+ * repo root and the tag alone (see `state/home.ts`'s `runDir`), independent
60
+ * of whatever branch the repository happens to be on right now, so a human
61
+ * who has wandered off the run branch can still check on it.
62
+ */
63
+ export async function cmdStatus(ctx, argv) {
64
+ let tag;
65
+ try {
66
+ const normalized = argv.map((a) => (/^-[A-Za-z][A-Za-z-]+$/.test(a) ? `-${a}` : a));
67
+ const { values } = parseArgs({
68
+ args: normalized,
69
+ options: { tag: { type: 'string' } },
70
+ strict: true,
71
+ allowPositionals: false,
72
+ });
73
+ tag = values.tag;
74
+ }
75
+ catch (e) {
76
+ return fail(messageOf(e));
77
+ }
78
+ let resolvedTag;
79
+ try {
80
+ resolvedTag = tag ?? (await inferTagFromBranch(ctx.repoRoot));
81
+ }
82
+ catch (e) {
83
+ return fail(messageOf(e));
84
+ }
85
+ const dir = runDir(ctx.repoRoot, resolvedTag);
86
+ let baseline;
87
+ try {
88
+ baseline = await readBaseline(dir);
89
+ }
90
+ catch (e) {
91
+ return fail(messageOf(e));
92
+ }
93
+ const [branch, head, lock, stop, rows] = await Promise.all([
94
+ tryGit(() => currentBranch(ctx.repoRoot)),
95
+ tryGit(() => headCommit(ctx.repoRoot)),
96
+ readEvalLock(dir),
97
+ readStop(dir),
98
+ loadRows(ctx.resultsPath),
99
+ ]);
100
+ const runBranch = `${BRANCH_PREFIX}${resolvedTag}`;
101
+ const worktreeDir = path.join(dir, WORKTREE_DIRNAME);
102
+ const worktreePresent = await exists(worktreeDir);
103
+ const summary = summarize(rows);
104
+ const lines = [];
105
+ lines.push(`autor3search-typescript status -- tag ${JSON.stringify(resolvedTag)}`);
106
+ lines.push(` run branch: ${runBranch}`);
107
+ lines.push(` current branch: ${branch ?? 'unknown'}` + (branch === runBranch ? '' : ' (not on the run branch)'));
108
+ lines.push(` frozen commit: ${shortSha(baseline.frozenCommit)} (${baseline.frozenCommit})`);
109
+ lines.push(` measure commit: ${shortSha(baseline.measureCommit)} (${baseline.measureCommit})`);
110
+ lines.push(` HEAD: ${head === null ? 'unknown' : `${shortSha(head)} (${head})`}`);
111
+ lines.push(` worktree: ${worktreeDir} (${worktreePresent ? 'present' : 'MISSING'})`);
112
+ if (lock === null) {
113
+ lines.push(' eval: idle');
114
+ }
115
+ else if (isPidAlive(lock.pid)) {
116
+ lines.push(` eval: running (pid ${lock.pid})`);
117
+ }
118
+ else {
119
+ lines.push(` eval: not running (stale lock left by pid ${lock.pid} -- a previous eval likely crashed)`);
120
+ }
121
+ if (stop === null) {
122
+ lines.push(' stop: none pending');
123
+ }
124
+ else {
125
+ const detail = stop.force
126
+ ? 'the running eval was asked to abandon its current experiment immediately'
127
+ : 'the agent will stop after its current experiment reports a verdict';
128
+ lines.push(` stop: requested at ${stop.requestedAt} (force: ${stop.force ? 'yes' : 'no'}) -- ${detail}`);
129
+ }
130
+ lines.push(` experiments: ${rows.length} total`);
131
+ lines.push(` ${STATUSES.map((s) => `${s}: ${summary.counts[s] ?? 0}`).join(' ')}`);
132
+ lines.push(` cumulative speedup: ${formatCumulativeSpeedup(summary.cumulativeSpeedup)}`);
133
+ if (summary.topWins.length > 0) {
134
+ lines.push(' top wins:');
135
+ for (const w of summary.topWins) {
136
+ const desc = w.description.length > 0 ? ` -- ${w.description}` : '';
137
+ lines.push(` ${shortSha(w.commit)} score=${w.score.toFixed(4)}${desc}`);
138
+ }
139
+ }
140
+ process.stdout.write(`${lines.join('\n')}\n`);
141
+ return 0;
142
+ }
143
+ //# sourceMappingURL=cmd-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cmd-status.js","sourceRoot":"","sources":["../../src/cli/cmd-status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { RunCtx } from './runctx.js';
2
+ /**
3
+ * `stop`: asks a running (or about-to-run) `eval` loop to end.
4
+ *
5
+ * Plain `stop` only ever writes `stop.json` -- the agent reads it at its
6
+ * next verdict, so the in-flight experiment always finishes and is scored.
7
+ * `-clear` cancels a pending request. `-force` additionally signals the pid
8
+ * recorded in `eval.lock` (if any) to abandon the current experiment right
9
+ * now -- using `runner/exec.ts`'s own exported `killGroup`, so the pid-1
10
+ * refusal lives in exactly one place -- and then reports the repository
11
+ * state that leaves -- including the exact command that would drop the
12
+ * abandoned commit -- WITHOUT running it. Dropping an agent's work is the
13
+ * human's decision, never this tool's.
14
+ */
15
+ export declare function cmdStop(ctx: RunCtx, argv: readonly string[]): Promise<number>;
@@ -0,0 +1,143 @@
1
+ import { parseArgs } from 'node:util';
2
+ import { currentBranch, headCommit, shortSha } from '../gitx/git.js';
3
+ import { killGroup } from '../runner/exec.js';
4
+ import { readBaseline } from '../state/baseline.js';
5
+ import { runDir } from '../state/home.js';
6
+ import { readEvalLock } from '../state/lock.js';
7
+ import { inferTagFromBranch } from '../state/runnaming.js';
8
+ import { clearStop, requestStop } from '../state/stop.js';
9
+ function fail(message) {
10
+ process.stderr.write(`error: ${message}\n`);
11
+ return 2;
12
+ }
13
+ function messageOf(e) {
14
+ return e instanceof Error ? e.message : String(e);
15
+ }
16
+ async function tryGit(f) {
17
+ try {
18
+ return await f();
19
+ }
20
+ catch {
21
+ return null;
22
+ }
23
+ }
24
+ /**
25
+ * Mirrors lock.ts's private `isAlive` (not exported): EPERM means the
26
+ * process exists but we lack permission to signal it, which is still
27
+ * "alive." Used here only to word the report honestly -- a lock recording a
28
+ * pid that already died (the eval crashed, or a previous `-force` already
29
+ * killed it) must not be reported as "signalled" just because the file is
30
+ * still there; `readEvalLock` itself never checks this (see its own doc
31
+ * comment), so a caller that needs to know still has to.
32
+ */
33
+ function isPidAlive(pid) {
34
+ try {
35
+ process.kill(pid, 0);
36
+ return true;
37
+ }
38
+ catch (err) {
39
+ return err.code === 'EPERM';
40
+ }
41
+ }
42
+ /**
43
+ * `stop`: asks a running (or about-to-run) `eval` loop to end.
44
+ *
45
+ * Plain `stop` only ever writes `stop.json` -- the agent reads it at its
46
+ * next verdict, so the in-flight experiment always finishes and is scored.
47
+ * `-clear` cancels a pending request. `-force` additionally signals the pid
48
+ * recorded in `eval.lock` (if any) to abandon the current experiment right
49
+ * now -- using `runner/exec.ts`'s own exported `killGroup`, so the pid-1
50
+ * refusal lives in exactly one place -- and then reports the repository
51
+ * state that leaves -- including the exact command that would drop the
52
+ * abandoned commit -- WITHOUT running it. Dropping an agent's work is the
53
+ * human's decision, never this tool's.
54
+ */
55
+ export async function cmdStop(ctx, argv) {
56
+ let tag;
57
+ let clear;
58
+ let force;
59
+ try {
60
+ const normalized = argv.map((a) => (/^-[A-Za-z][A-Za-z-]+$/.test(a) ? `-${a}` : a));
61
+ const { values } = parseArgs({
62
+ args: normalized,
63
+ options: {
64
+ tag: { type: 'string' },
65
+ clear: { type: 'boolean', default: false },
66
+ force: { type: 'boolean', default: false },
67
+ },
68
+ strict: true,
69
+ allowPositionals: false,
70
+ });
71
+ tag = values.tag;
72
+ clear = values.clear === true;
73
+ force = values.force === true;
74
+ }
75
+ catch (e) {
76
+ return fail(messageOf(e));
77
+ }
78
+ if (clear && force) {
79
+ return fail('-clear and -force are mutually exclusive');
80
+ }
81
+ let resolvedTag;
82
+ try {
83
+ resolvedTag = tag ?? (await inferTagFromBranch(ctx.repoRoot));
84
+ }
85
+ catch (e) {
86
+ return fail(messageOf(e));
87
+ }
88
+ const dir = runDir(ctx.repoRoot, resolvedTag);
89
+ try {
90
+ // Confirms a run actually exists before writing (or clearing) any state
91
+ // for it. A human who mistypes a tag must be told the tag is unknown,
92
+ // not silently handed a stop request that nothing will ever read.
93
+ // readBaseline never creates anything -- only reads -- so a nonexistent
94
+ // run is refused here before any state is touched.
95
+ await readBaseline(dir);
96
+ }
97
+ catch (e) {
98
+ return fail(messageOf(e));
99
+ }
100
+ if (clear) {
101
+ await clearStop(dir);
102
+ process.stdout.write(`stop request for tag ${JSON.stringify(resolvedTag)} cleared\n`);
103
+ return 0;
104
+ }
105
+ await requestStop(dir, force);
106
+ if (!force) {
107
+ process.stdout.write(`stop requested for tag ${JSON.stringify(resolvedTag)}: the agent will stop after its current ` +
108
+ 'experiment reports a verdict.\n');
109
+ return 0;
110
+ }
111
+ process.stdout.write(`stop -force requested for tag ${JSON.stringify(resolvedTag)}.\n`);
112
+ const lock = await readEvalLock(dir);
113
+ const signalled = lock !== null && isPidAlive(lock.pid);
114
+ if (lock === null) {
115
+ process.stdout.write(' no eval lock is currently held: nothing to signal.\n');
116
+ }
117
+ else if (!signalled) {
118
+ process.stdout.write(` eval.lock names pid ${lock.pid}, but it is no longer running (a previous eval likely ` +
119
+ 'crashed, or was already stopped): nothing to signal.\n');
120
+ }
121
+ else {
122
+ killGroup(lock.pid, 'SIGTERM');
123
+ process.stdout.write(` signalled pid ${lock.pid} (SIGTERM) to abandon its current experiment.\n`);
124
+ }
125
+ const branch = await tryGit(() => currentBranch(ctx.repoRoot));
126
+ const head = await tryGit(() => headCommit(ctx.repoRoot));
127
+ process.stdout.write(` current branch: ${branch ?? 'unknown'}\n`);
128
+ process.stdout.write(` HEAD commit: ${head === null ? 'unknown' : `${shortSha(head)} (${head})`}\n`);
129
+ // Only claim a commit was actually abandoned when something was really
130
+ // signalled -- with no eval lock held, HEAD is just whatever the last
131
+ // completed experiment left behind, not something this invocation
132
+ // interrupted. Either way, nothing has been changed here; dropping a
133
+ // commit is the human's call, never this tool's, so the reset command is
134
+ // only ever printed, never run.
135
+ process.stdout.write(signalled
136
+ ? ' this is very likely the commit for the experiment just abandoned. Nothing here has been\n' +
137
+ ' changed -- dropping it is your call, not this tool\'s. To drop it:\n'
138
+ : ' no eval was running, so nothing was abandoned. If HEAD above is still an unwanted\n' +
139
+ ' experiment, nothing here has been changed -- dropping it is your call. To drop it:\n');
140
+ process.stdout.write('\n git reset --hard HEAD~1\n');
141
+ return 0;
142
+ }
143
+ //# sourceMappingURL=cmd-stop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cmd-stop.js","sourceRoot":"","sources":["../../src/cli/cmd-stop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ import { type RunCtx } from './runctx.js';
3
+ type Command = (ctx: RunCtx, argv: string[]) => Promise<number>;
4
+ /**
5
+ * The command table.
6
+ *
7
+ * Ruling 34: a command implemented and tested but never added here is
8
+ * unreachable, yet looks completely healthy from inside its own task's
9
+ * suite -- the missing piece lives in a file that task never touches, and
10
+ * `--help` would still print successfully, just without it. Every command
11
+ * task must register itself here, and prove it with a test that dispatches
12
+ * through `main`, not one that only calls the command function directly.
13
+ * Exported (only) so the COMMANDS/HELP invariant test below can compare
14
+ * this table's keys against what `--help` actually lists.
15
+ */
16
+ export declare const COMMANDS: Record<string, Command>;
17
+ /**
18
+ * Dispatch only: parse the leading `-C <dir>`, take the subcommand, and
19
+ * delegate. Every command is `(ctx, argv) => Promise<number>`, and its
20
+ * return value becomes the process exit code directly -- exit codes are
21
+ * reserved project-wide (0 KEEP, 1 DISCARD, 2 FAIL, 3 CRASH), and a usage
22
+ * error or an `init` failure is a FAIL (2).
23
+ *
24
+ * This function never rejects: everything below -- including `resolveCtx`
25
+ * failing on a `-C` outside any git repository -- is caught here and
26
+ * turned into `error: <message>` on stderr plus a `2` return, never a raw
27
+ * stack trace. That makes it directly testable (call it, read the
28
+ * returned number) without a test having to unwrap a promise rejection.
29
+ */
30
+ export declare function main(argv: string[]): Promise<number>;
31
+ export {};