opencode-autoresearch 3.13.2 → 3.14.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/.opencode-plugin/plugin.json +1 -1
- package/INSTALL.md +52 -2
- package/README.md +5 -3
- package/VERSION +1 -1
- package/dist/cli.js +562 -35
- package/dist/cli.js.map +1 -1
- package/dist/compaction.d.ts +17 -0
- package/dist/compaction.d.ts.map +1 -0
- package/dist/compaction.js +175 -0
- package/dist/compaction.js.map +1 -0
- package/dist/constants.d.ts +3 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +9 -1
- package/dist/constants.js.map +1 -1
- package/dist/error-categories.d.ts +12 -0
- package/dist/error-categories.d.ts.map +1 -0
- package/dist/error-categories.js +137 -0
- package/dist/error-categories.js.map +1 -0
- package/dist/evidence.d.ts +24 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +82 -0
- package/dist/evidence.js.map +1 -0
- package/dist/helpers.d.ts +13 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +40 -0
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/leaderboard.d.ts +27 -0
- package/dist/leaderboard.d.ts.map +1 -0
- package/dist/leaderboard.js +195 -0
- package/dist/leaderboard.js.map +1 -0
- package/dist/memory-manager.d.ts.map +1 -1
- package/dist/memory-manager.js +8 -2
- package/dist/memory-manager.js.map +1 -1
- package/dist/metric-comparator.d.ts +15 -0
- package/dist/metric-comparator.d.ts.map +1 -0
- package/dist/metric-comparator.js +58 -0
- package/dist/metric-comparator.js.map +1 -0
- package/dist/run-manager.d.ts +1 -0
- package/dist/run-manager.d.ts.map +1 -1
- package/dist/run-manager.js +41 -14
- package/dist/run-manager.js.map +1 -1
- package/dist/serialize.d.ts +8 -0
- package/dist/serialize.d.ts.map +1 -0
- package/dist/serialize.js +50 -0
- package/dist/serialize.js.map +1 -0
- package/dist/strategy-pack.d.ts +31 -0
- package/dist/strategy-pack.d.ts.map +1 -0
- package/dist/strategy-pack.js +90 -0
- package/dist/strategy-pack.js.map +1 -0
- package/dist/subagent-pool.d.ts.map +1 -1
- package/dist/subagent-pool.js +22 -14
- package/dist/subagent-pool.js.map +1 -1
- package/dist/task-queue.d.ts +36 -0
- package/dist/task-queue.d.ts.map +1 -0
- package/dist/task-queue.js +65 -0
- package/dist/task-queue.js.map +1 -0
- package/dist/types.d.ts +45 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/verifier-parser.d.ts.map +1 -1
- package/dist/verifier-parser.js +3 -1
- package/dist/verifier-parser.js.map +1 -1
- package/dist/whats-new.d.ts +12 -0
- package/dist/whats-new.d.ts.map +1 -0
- package/dist/whats-new.js +106 -0
- package/dist/whats-new.js.map +1 -0
- package/dist/worker.d.ts +11 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +75 -0
- package/dist/worker.js.map +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { closeSync, existsSync, fstatSync, openSync, readFileSync, readSync, readdirSync } from "fs";
|
|
2
|
+
import { closeSync, constants as fsConstants, existsSync, fstatSync, lstatSync, openSync, readFileSync, readSync, readdirSync } from "fs";
|
|
3
3
|
import { resolve } from "path";
|
|
4
4
|
import { execSync } from "child_process";
|
|
5
5
|
import { MAX_DRAFTS } from "./constants.js";
|
|
6
|
-
import { printJson, resolveRepo, parseRunState, parsePositiveInt, sanitizeForTerminal, getInstalledPackagePath, getInstalledPackageInfo, readUpdateCache, getGlobalNpmPrefix, readGoalDoc, atomicWriteTextInRepo } from "./helpers.js";
|
|
6
|
+
import { printJson, printJsonEnvelope, resolveRepo, parseRunState, parsePositiveInt, sanitizeForTerminal, getInstalledPackagePath, getInstalledPackageInfo, readUpdateCache, getGlobalNpmPrefix, readGoalDoc, atomicWriteTextInRepo } from "./helpers.js";
|
|
7
7
|
const VERSION_FLAGS = ["--version", "-v"];
|
|
8
8
|
const HELP_FLAGS = ["--help", "-h", "help"];
|
|
9
9
|
const BRANCH_POLICIES = ["best", "roulette", "diverse"];
|
|
10
|
+
const shouldSkipUpdateCheck = (args) => {
|
|
11
|
+
if (args.length > 0 && VERSION_FLAGS.includes(args[0])) {
|
|
12
|
+
return { skip: true, reason: "version_flag" };
|
|
13
|
+
}
|
|
14
|
+
if (args.length > 0 && HELP_FLAGS.includes(args[0])) {
|
|
15
|
+
return { skip: true, reason: "help_flag" };
|
|
16
|
+
}
|
|
17
|
+
if (process.env.AUTORESEARCH_NO_UPDATE === "1") {
|
|
18
|
+
return { skip: true, reason: "env_opt_out" };
|
|
19
|
+
}
|
|
20
|
+
if (process.env.CI === "true" || process.env.CI === "1") {
|
|
21
|
+
return { skip: true, reason: "ci_environment" };
|
|
22
|
+
}
|
|
23
|
+
return { skip: false, reason: null };
|
|
24
|
+
};
|
|
10
25
|
const usage = () => {
|
|
11
26
|
console.error("Usage: autoresearch <command> [options]");
|
|
12
27
|
console.error("");
|
|
@@ -22,6 +37,7 @@ const usage = () => {
|
|
|
22
37
|
console.error(" score Run the configured scorer and show normalized output");
|
|
23
38
|
console.error(" digest Generate re-entry digest for operator handoff");
|
|
24
39
|
console.error(" config Show runtime configuration");
|
|
40
|
+
console.error(" contract Print runtime contract schemas");
|
|
25
41
|
console.error(" summary Aggregate stats across runs");
|
|
26
42
|
console.error(" suggest Suggest next goal from memory");
|
|
27
43
|
console.error(" launch Launch a background run");
|
|
@@ -29,6 +45,9 @@ const usage = () => {
|
|
|
29
45
|
console.error(" stop Request a background run stop");
|
|
30
46
|
console.error(" resume Resume a background run");
|
|
31
47
|
console.error(" record Record an experiment result");
|
|
48
|
+
console.error(" queue Manage background task queue");
|
|
49
|
+
console.error(" pack Export and inspect strategy packs");
|
|
50
|
+
console.error(" leaderboard Show local leaderboard across runs");
|
|
32
51
|
console.error(" doctor Verify package installation and version");
|
|
33
52
|
console.error(" help Show this help");
|
|
34
53
|
console.error("");
|
|
@@ -55,6 +74,7 @@ const usage = () => {
|
|
|
55
74
|
console.error(" --duration Wall-clock cap (e.g., 5h or 300m)");
|
|
56
75
|
console.error(` --num-drafts Number of parallel drafts (default: 1, max: ${MAX_DRAFTS})`);
|
|
57
76
|
console.error(" --branch-policy Branch selection policy: best, roulette, diverse");
|
|
77
|
+
console.error(' --branch-policy-overrides JSON object mapping draft IDs to policies (e.g. {"draft-0":"diverse"})');
|
|
58
78
|
console.error(" --max-debug-depth Max debug experiment depth before stop");
|
|
59
79
|
console.error(" --branch-failure-budget Per-branch failure budget before stop");
|
|
60
80
|
console.error(" --json Output raw JSON (default: human-readable)");
|
|
@@ -84,7 +104,13 @@ const parseArgs = (args) => {
|
|
|
84
104
|
const result = {};
|
|
85
105
|
for (let i = 0; i < args.length; i++) {
|
|
86
106
|
if (args[i].startsWith("--")) {
|
|
87
|
-
const
|
|
107
|
+
const longArg = args[i];
|
|
108
|
+
const equalsIndex = longArg.indexOf("=");
|
|
109
|
+
if (equalsIndex > 2) {
|
|
110
|
+
result[longArg.slice(2, equalsIndex)] = longArg.slice(equalsIndex + 1);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const key = longArg.slice(2);
|
|
88
114
|
if (i + 1 < args.length && !args[i + 1].startsWith("--") && !args[i + 1].startsWith("-")) {
|
|
89
115
|
result[key] = args[++i];
|
|
90
116
|
}
|
|
@@ -183,6 +209,39 @@ const formatTimestamp = (ts) => {
|
|
|
183
209
|
return ts;
|
|
184
210
|
}
|
|
185
211
|
};
|
|
212
|
+
const MAX_SCORE_HISTORY_BYTES = 10 * 1024 * 1024;
|
|
213
|
+
const assertRegularBoundedFile = (filePath) => {
|
|
214
|
+
const linkStats = lstatSync(filePath);
|
|
215
|
+
if (linkStats.isSymbolicLink()) {
|
|
216
|
+
throw new Error(`Refusing to read score history symlink: ${filePath}`);
|
|
217
|
+
}
|
|
218
|
+
if (!linkStats.isFile()) {
|
|
219
|
+
throw new Error(`Refusing to read non-regular score history file: ${filePath}`);
|
|
220
|
+
}
|
|
221
|
+
if (linkStats.size > MAX_SCORE_HISTORY_BYTES) {
|
|
222
|
+
throw new Error(`Score history is too large to read safely (${linkStats.size} bytes; max ${MAX_SCORE_HISTORY_BYTES} bytes): ${filePath}`);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const readScoreHistoryFile = (filePath) => {
|
|
226
|
+
assertRegularBoundedFile(filePath);
|
|
227
|
+
if (typeof fsConstants.O_NOFOLLOW !== "number") {
|
|
228
|
+
throw new Error(`Refusing to read score history because this platform does not support O_NOFOLLOW: ${filePath}`);
|
|
229
|
+
}
|
|
230
|
+
const fd = openSync(filePath, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
|
|
231
|
+
try {
|
|
232
|
+
const fileStats = fstatSync(fd);
|
|
233
|
+
if (!fileStats.isFile()) {
|
|
234
|
+
throw new Error(`Refusing to read non-regular score history file: ${filePath}`);
|
|
235
|
+
}
|
|
236
|
+
if (fileStats.size > MAX_SCORE_HISTORY_BYTES) {
|
|
237
|
+
throw new Error(`Score history is too large to read safely (${fileStats.size} bytes; max ${MAX_SCORE_HISTORY_BYTES} bytes): ${filePath}`);
|
|
238
|
+
}
|
|
239
|
+
return readFileSync(fd, "utf-8");
|
|
240
|
+
}
|
|
241
|
+
finally {
|
|
242
|
+
closeSync(fd);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
186
245
|
const readTailLines = (filePath, limit) => {
|
|
187
246
|
if (limit <= 0)
|
|
188
247
|
return [];
|
|
@@ -245,6 +304,41 @@ const normalizeBranchPolicy = (value) => {
|
|
|
245
304
|
return value;
|
|
246
305
|
throw new Error(`Invalid branch policy: ${value}. Expected one of: ${BRANCH_POLICIES.join(", ")}`);
|
|
247
306
|
};
|
|
307
|
+
const PROTO_POISON_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
308
|
+
const normalizeOverrideBranchPolicy = (branchId, value) => {
|
|
309
|
+
const trimmed = value.trim();
|
|
310
|
+
if (trimmed === "") {
|
|
311
|
+
throw new Error(`Invalid branch policy override for ${branchId}: value must not be empty`);
|
|
312
|
+
}
|
|
313
|
+
if (BRANCH_POLICIES.includes(trimmed))
|
|
314
|
+
return trimmed;
|
|
315
|
+
throw new Error(`Invalid branch policy override for ${branchId}: "${trimmed}" is not one of: ${BRANCH_POLICIES.join(", ")}`);
|
|
316
|
+
};
|
|
317
|
+
const parseBranchPolicyOverrides = (value) => {
|
|
318
|
+
if (value == null || value === "")
|
|
319
|
+
return undefined;
|
|
320
|
+
let parsed;
|
|
321
|
+
try {
|
|
322
|
+
parsed = JSON.parse(value);
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
throw new Error("Invalid branch policy overrides: expected a JSON object mapping draft IDs to branch policies");
|
|
326
|
+
}
|
|
327
|
+
if (parsed == null || Array.isArray(parsed) || typeof parsed !== "object") {
|
|
328
|
+
throw new Error("Invalid branch policy overrides: expected a JSON object mapping draft IDs to branch policies");
|
|
329
|
+
}
|
|
330
|
+
const overrides = Object.create(null);
|
|
331
|
+
for (const [branchId, branchPolicy] of Object.entries(parsed)) {
|
|
332
|
+
if (PROTO_POISON_KEYS.has(branchId)) {
|
|
333
|
+
throw new Error(`Invalid branch policy override key: "${branchId}" is not a valid draft ID`);
|
|
334
|
+
}
|
|
335
|
+
if (typeof branchPolicy !== "string") {
|
|
336
|
+
throw new Error(`Invalid branch policy override for ${branchId}: expected a string policy`);
|
|
337
|
+
}
|
|
338
|
+
overrides[branchId] = normalizeOverrideBranchPolicy(branchId, branchPolicy);
|
|
339
|
+
}
|
|
340
|
+
return overrides;
|
|
341
|
+
};
|
|
248
342
|
const main = async () => {
|
|
249
343
|
const args = process.argv.slice(2);
|
|
250
344
|
// Handle standalone flags
|
|
@@ -298,7 +392,7 @@ const main = async () => {
|
|
|
298
392
|
stop_condition: grouped["stop-condition"],
|
|
299
393
|
rollback_strategy: grouped["rollback-strategy"],
|
|
300
394
|
};
|
|
301
|
-
|
|
395
|
+
printJsonEnvelope("wizard", buildSetupSummary(grouped.repo, config));
|
|
302
396
|
break;
|
|
303
397
|
}
|
|
304
398
|
case "init": {
|
|
@@ -335,6 +429,7 @@ const main = async () => {
|
|
|
335
429
|
baseline: grouped.baseline,
|
|
336
430
|
num_drafts: parsePositiveInt(grouped["num-drafts"], "num_drafts", { max: MAX_DRAFTS }) ?? 1,
|
|
337
431
|
branch_selection_policy: normalizeBranchPolicy(grouped["branch-policy"]),
|
|
432
|
+
branch_policy_overrides: parseBranchPolicyOverrides(grouped["branch-policy-overrides"]),
|
|
338
433
|
outcome_metric: grouped["outcome-metric"],
|
|
339
434
|
outcome_direction: grouped["outcome-direction"],
|
|
340
435
|
instrument_metric: grouped["instrument-metric"],
|
|
@@ -350,7 +445,7 @@ const main = async () => {
|
|
|
350
445
|
const { buildSupervisorSnapshot } = await import("./run-manager.js");
|
|
351
446
|
const snapshot = await buildSupervisorSnapshot(grouped.repo, grouped["results-path"], grouped["state-path"]);
|
|
352
447
|
if (useJson) {
|
|
353
|
-
|
|
448
|
+
printJsonEnvelope("status", snapshot);
|
|
354
449
|
}
|
|
355
450
|
else {
|
|
356
451
|
const s = snapshot;
|
|
@@ -372,7 +467,8 @@ const main = async () => {
|
|
|
372
467
|
console.log(`Results: ${s.results_rows} rows`);
|
|
373
468
|
const lastIter = s.last_iteration;
|
|
374
469
|
if (lastIter && lastIter.iteration) {
|
|
375
|
-
|
|
470
|
+
const stageTag = lastIter.stage ? ` [${lastIter.stage}]` : "";
|
|
471
|
+
console.log(`Last: iter ${formatDisplayValue(lastIter.iteration)}${stageTag} — ${formatDisplayValue(lastIter.decision)} (${formatMetricValue(lastIter.metric_value)})`);
|
|
376
472
|
if (lastIter.score_components != null && typeof lastIter.score_components === "object") {
|
|
377
473
|
const parts = Object.entries(lastIter.score_components)
|
|
378
474
|
.map(([k, v]) => `${formatDisplayValue(k)}:${typeof v === "number" ? v.toFixed(4) : formatDisplayValue(v)}`)
|
|
@@ -380,12 +476,21 @@ const main = async () => {
|
|
|
380
476
|
if (parts.length > 0)
|
|
381
477
|
console.log(` Components: [${parts}]`);
|
|
382
478
|
}
|
|
479
|
+
if (lastIter.selected_action)
|
|
480
|
+
console.log(` Action: ${formatDisplayValue(lastIter.selected_action)}`);
|
|
383
481
|
}
|
|
384
482
|
const flags = s.flags;
|
|
385
483
|
if (flags?.needs_human)
|
|
386
484
|
console.log("⚠ Needs human input");
|
|
387
485
|
if (flags?.stop_requested)
|
|
388
486
|
console.log("⏹ Stop requested");
|
|
487
|
+
if (flags?.context_pressure) {
|
|
488
|
+
const cp = flags.context_pressure;
|
|
489
|
+
if (cp.warnings.length > 0) {
|
|
490
|
+
for (const w of cp.warnings)
|
|
491
|
+
console.log(`🆘 Context pressure: ${w}`);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
389
494
|
}
|
|
390
495
|
break;
|
|
391
496
|
}
|
|
@@ -397,7 +502,7 @@ const main = async () => {
|
|
|
397
502
|
const lastIter = s.last_iteration;
|
|
398
503
|
const flags = s.flags;
|
|
399
504
|
if (useJson) {
|
|
400
|
-
|
|
505
|
+
printJsonEnvelope("explain", snapshot);
|
|
401
506
|
break;
|
|
402
507
|
}
|
|
403
508
|
const statusEmoji = {
|
|
@@ -466,7 +571,7 @@ const main = async () => {
|
|
|
466
571
|
}
|
|
467
572
|
return obj;
|
|
468
573
|
});
|
|
469
|
-
|
|
574
|
+
printJsonEnvelope("history", { count: records.length, records: parsed });
|
|
470
575
|
break;
|
|
471
576
|
}
|
|
472
577
|
for (const r of records) {
|
|
@@ -493,7 +598,7 @@ const main = async () => {
|
|
|
493
598
|
const limit = parsePositiveInt(grouped.limit, "limit") ?? 10;
|
|
494
599
|
const showTopComponents = grouped["top-components"] === "true";
|
|
495
600
|
if (showTopComponents) {
|
|
496
|
-
const allLines =
|
|
601
|
+
const allLines = readScoreHistoryFile(scoreHistoryPath)
|
|
497
602
|
.split("\n")
|
|
498
603
|
.map((l) => l.trim())
|
|
499
604
|
.filter(Boolean);
|
|
@@ -512,7 +617,7 @@ const main = async () => {
|
|
|
512
617
|
const { rankComponents } = await import("./score-parser.js");
|
|
513
618
|
const ranking = rankComponents(allParsed);
|
|
514
619
|
if (useJson) {
|
|
515
|
-
|
|
620
|
+
printJsonEnvelope("scores", { count: allParsed.length, scores: allParsed.slice(-limit), ranking });
|
|
516
621
|
break;
|
|
517
622
|
}
|
|
518
623
|
console.log("Component Rankings:");
|
|
@@ -548,7 +653,7 @@ const main = async () => {
|
|
|
548
653
|
return null;
|
|
549
654
|
}
|
|
550
655
|
}).filter(Boolean);
|
|
551
|
-
|
|
656
|
+
printJsonEnvelope("scores", { count: parsed.length, scores: parsed });
|
|
552
657
|
break;
|
|
553
658
|
}
|
|
554
659
|
console.log("Score History (latest " + Math.min(limit, records.length) + "):");
|
|
@@ -651,7 +756,7 @@ const main = async () => {
|
|
|
651
756
|
const normalized = scored.score / scored.max;
|
|
652
757
|
const percent = (normalized * 100).toFixed(1) + "%";
|
|
653
758
|
if (useJson) {
|
|
654
|
-
|
|
759
|
+
printJsonEnvelope("score", {
|
|
655
760
|
score: scored.score,
|
|
656
761
|
max: scored.max,
|
|
657
762
|
normalized,
|
|
@@ -687,7 +792,7 @@ const main = async () => {
|
|
|
687
792
|
}
|
|
688
793
|
const state = parseRunState(readJsonFile(statePath));
|
|
689
794
|
if (useJson) {
|
|
690
|
-
|
|
795
|
+
printJsonEnvelope("config", {
|
|
691
796
|
goal: state.goal,
|
|
692
797
|
mode: state.mode,
|
|
693
798
|
metric: state.metric,
|
|
@@ -719,6 +824,148 @@ const main = async () => {
|
|
|
719
824
|
console.log(` Pool: ${state.subagent_pool ? "configured" : "none"}`);
|
|
720
825
|
break;
|
|
721
826
|
}
|
|
827
|
+
case "contract": {
|
|
828
|
+
const schemas = {
|
|
829
|
+
schema_version: "1.0.0",
|
|
830
|
+
description: "Auto Research runtime contract schemas",
|
|
831
|
+
state: {
|
|
832
|
+
type: "object",
|
|
833
|
+
required: ["schema_version", "run_id", "created_at", "updated_at", "status", "mode", "operating_mode", "goal", "scope", "metric", "verify", "label_requirements", "artifact_paths", "stats", "flags"],
|
|
834
|
+
properties: {
|
|
835
|
+
schema_version: { type: "number", description: "State schema version" },
|
|
836
|
+
run_id: { type: "string", description: "Unique run identifier" },
|
|
837
|
+
created_at: { type: "string", format: "date-time", description: "Run creation timestamp" },
|
|
838
|
+
updated_at: { type: "string", format: "date-time", description: "Last update timestamp" },
|
|
839
|
+
status: { type: "string", enum: ["initialized", "running", "stopping", "stopped", "completed", "needs_human"], description: "Run status" },
|
|
840
|
+
mode: { type: "string", enum: ["foreground", "background"], description: "Execution mode" },
|
|
841
|
+
operating_mode: { type: "string", enum: ["converge", "continuous", "supervised"], description: "Operating mode" },
|
|
842
|
+
goal: { type: "string", description: "Run goal description" },
|
|
843
|
+
scope: { type: "string", description: "Target scope" },
|
|
844
|
+
metric: {
|
|
845
|
+
type: "object",
|
|
846
|
+
required: ["name", "direction"],
|
|
847
|
+
properties: {
|
|
848
|
+
name: { type: "string" },
|
|
849
|
+
direction: { type: "string", enum: ["higher", "lower"] },
|
|
850
|
+
baseline: { type: "string" },
|
|
851
|
+
best: { type: "string" },
|
|
852
|
+
latest: { type: "string" },
|
|
853
|
+
},
|
|
854
|
+
},
|
|
855
|
+
instrument_metric: { type: "object", description: "Optional secondary metric" },
|
|
856
|
+
verify: { type: "string", description: "Verification command" },
|
|
857
|
+
guard: { type: "string", description: "Guard command" },
|
|
858
|
+
scorer: { type: "string", description: "Scorer command" },
|
|
859
|
+
iterations_cap: { type: "number", description: "Maximum iterations" },
|
|
860
|
+
duration: { type: "string", description: "Duration limit" },
|
|
861
|
+
duration_seconds: { type: "number" },
|
|
862
|
+
deadline_at: { type: "string", format: "date-time" },
|
|
863
|
+
label_requirements: {
|
|
864
|
+
type: "object",
|
|
865
|
+
required: ["keep", "stop"],
|
|
866
|
+
properties: {
|
|
867
|
+
keep: { type: "array", items: { type: "string" } },
|
|
868
|
+
stop: { type: "array", items: { type: "string" } },
|
|
869
|
+
},
|
|
870
|
+
},
|
|
871
|
+
artifact_paths: {
|
|
872
|
+
type: "object",
|
|
873
|
+
required: ["results", "state"],
|
|
874
|
+
properties: {
|
|
875
|
+
results: { type: "string" },
|
|
876
|
+
state: { type: "string" },
|
|
877
|
+
},
|
|
878
|
+
},
|
|
879
|
+
stats: {
|
|
880
|
+
type: "object",
|
|
881
|
+
required: ["total_iterations", "kept", "discarded", "needs_human"],
|
|
882
|
+
properties: {
|
|
883
|
+
total_iterations: { type: "number" },
|
|
884
|
+
kept: { type: "number" },
|
|
885
|
+
discarded: { type: "number" },
|
|
886
|
+
needs_human: { type: "number" },
|
|
887
|
+
consecutive_discards: { type: "number" },
|
|
888
|
+
best_iteration: { type: "number" },
|
|
889
|
+
debug_depth: { type: "number" },
|
|
890
|
+
},
|
|
891
|
+
},
|
|
892
|
+
flags: {
|
|
893
|
+
type: "object",
|
|
894
|
+
required: ["stop_requested", "needs_human", "background_active", "stop_ready"],
|
|
895
|
+
properties: {
|
|
896
|
+
stop_requested: { type: "boolean" },
|
|
897
|
+
needs_human: { type: "boolean" },
|
|
898
|
+
background_active: { type: "boolean" },
|
|
899
|
+
stop_ready: { type: "boolean" },
|
|
900
|
+
},
|
|
901
|
+
},
|
|
902
|
+
last_iteration: {
|
|
903
|
+
type: "object",
|
|
904
|
+
properties: {
|
|
905
|
+
iteration: { type: "number" },
|
|
906
|
+
decision: { type: "string", enum: ["keep", "discard", "needs_human"] },
|
|
907
|
+
metric_value: { type: "string" },
|
|
908
|
+
change_summary: { type: "string" },
|
|
909
|
+
labels: { type: "array", items: { type: "string" } },
|
|
910
|
+
timestamp: { type: "string", format: "date-time" },
|
|
911
|
+
},
|
|
912
|
+
},
|
|
913
|
+
draft_pool: { type: "object", description: "Draft pool configuration" },
|
|
914
|
+
lineage: { type: "object", description: "Experiment lineage" },
|
|
915
|
+
budget_exhausted: { type: "boolean" },
|
|
916
|
+
budget_blocker_reason: { type: "string" },
|
|
917
|
+
},
|
|
918
|
+
},
|
|
919
|
+
result_row: {
|
|
920
|
+
type: "object",
|
|
921
|
+
description: "Single iteration result row in TSV format",
|
|
922
|
+
properties: {
|
|
923
|
+
iteration: { type: "number" },
|
|
924
|
+
decision: { type: "string" },
|
|
925
|
+
metric_value: { type: "string" },
|
|
926
|
+
verify_status: { type: "string" },
|
|
927
|
+
guard_status: { type: "string" },
|
|
928
|
+
change_summary: { type: "string" },
|
|
929
|
+
labels: { type: "array", items: { type: "string" } },
|
|
930
|
+
timestamp: { type: "string" },
|
|
931
|
+
note: { type: "string" },
|
|
932
|
+
},
|
|
933
|
+
},
|
|
934
|
+
goal_doc: {
|
|
935
|
+
type: "object",
|
|
936
|
+
required: ["goal", "metric", "direction", "verify"],
|
|
937
|
+
properties: {
|
|
938
|
+
goal: { type: "string" },
|
|
939
|
+
metric: { type: "string" },
|
|
940
|
+
direction: { type: "string", enum: ["higher", "lower"] },
|
|
941
|
+
verify: { type: "string" },
|
|
942
|
+
guard: { type: "string" },
|
|
943
|
+
constraints: { type: "string" },
|
|
944
|
+
file_map: { type: "string" },
|
|
945
|
+
stop_conditions: { type: "string" },
|
|
946
|
+
},
|
|
947
|
+
},
|
|
948
|
+
};
|
|
949
|
+
if (useJson) {
|
|
950
|
+
printJsonEnvelope("contract", schemas);
|
|
951
|
+
break;
|
|
952
|
+
}
|
|
953
|
+
console.log("Auto Research Contract Schemas");
|
|
954
|
+
console.log("==============================");
|
|
955
|
+
console.log("");
|
|
956
|
+
console.log("State Schema:");
|
|
957
|
+
console.log(` Version: ${schemas.state.properties.schema_version.type}`);
|
|
958
|
+
console.log(` Required: ${schemas.state.required.join(", ")}`);
|
|
959
|
+
console.log("");
|
|
960
|
+
console.log("Result Row Schema:");
|
|
961
|
+
console.log(` Properties: ${Object.keys(schemas.result_row.properties).join(", ")}`);
|
|
962
|
+
console.log("");
|
|
963
|
+
console.log("Goal Doc Schema:");
|
|
964
|
+
console.log(` Required: ${schemas.goal_doc.required.join(", ")}`);
|
|
965
|
+
console.log("");
|
|
966
|
+
console.log("Use --json for full machine-readable schema output.");
|
|
967
|
+
break;
|
|
968
|
+
}
|
|
722
969
|
case "summary": {
|
|
723
970
|
const { resolvePath } = await import("./helpers.js");
|
|
724
971
|
const { RESULTS_DEFAULT } = await import("./constants.js");
|
|
@@ -746,7 +993,7 @@ const main = async () => {
|
|
|
746
993
|
runIds.add(iterTags[0]);
|
|
747
994
|
}
|
|
748
995
|
if (useJson) {
|
|
749
|
-
|
|
996
|
+
printJsonEnvelope("summary", {
|
|
750
997
|
total_records: records.length,
|
|
751
998
|
total_kept: totalKept,
|
|
752
999
|
total_discarded: totalDiscarded,
|
|
@@ -789,7 +1036,7 @@ const main = async () => {
|
|
|
789
1036
|
if (!grouped.verify)
|
|
790
1037
|
errors.push("Missing required: --verify");
|
|
791
1038
|
if (useJson) {
|
|
792
|
-
|
|
1039
|
+
printJsonEnvelope("validate", { valid: errors.length === 0, errors });
|
|
793
1040
|
return errors.length > 0 ? 1 : 0;
|
|
794
1041
|
}
|
|
795
1042
|
if (errors.length === 0) {
|
|
@@ -827,7 +1074,7 @@ const main = async () => {
|
|
|
827
1074
|
results = resultLines.slice(1).filter(Boolean);
|
|
828
1075
|
}
|
|
829
1076
|
if (useJson) {
|
|
830
|
-
|
|
1077
|
+
printJsonEnvelope("report", { state, results_count: results.length });
|
|
831
1078
|
break;
|
|
832
1079
|
}
|
|
833
1080
|
console.log(`# Auto Research Report`);
|
|
@@ -861,6 +1108,40 @@ const main = async () => {
|
|
|
861
1108
|
}
|
|
862
1109
|
}
|
|
863
1110
|
}
|
|
1111
|
+
// Milestone Progress
|
|
1112
|
+
console.log(`\n## Milestone Progress`);
|
|
1113
|
+
if (state.stats) {
|
|
1114
|
+
const s = state.stats;
|
|
1115
|
+
const total = s.total_iterations;
|
|
1116
|
+
const successRate = total > 0 ? ((s.kept / total) * 100).toFixed(1) : "0";
|
|
1117
|
+
console.log(`- **Progress:** ${formatMarkdownField(s.kept)} kept / ${formatMarkdownField(total)} total iterations (${formatMarkdownField(successRate)}% success rate)`);
|
|
1118
|
+
if (state.iterations_cap) {
|
|
1119
|
+
const progressPct = ((total / state.iterations_cap) * 100).toFixed(1);
|
|
1120
|
+
console.log(`- **Cap:** ${formatMarkdownField(total)} / ${formatMarkdownField(state.iterations_cap)} iterations (${formatMarkdownField(progressPct)}% of cap)`);
|
|
1121
|
+
}
|
|
1122
|
+
if (state.created_at) {
|
|
1123
|
+
const startedAtMs = Date.parse(state.created_at);
|
|
1124
|
+
const endedAtMs = state.updated_at ? Date.parse(state.updated_at) : Date.now();
|
|
1125
|
+
if (!Number.isNaN(startedAtMs) && !Number.isNaN(endedAtMs) && endedAtMs >= startedAtMs) {
|
|
1126
|
+
const elapsedMin = Math.round((endedAtMs - startedAtMs) / 1000 / 60);
|
|
1127
|
+
console.log(`- **Elapsed:** ${formatMarkdownField(elapsedMin)} minutes`);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
// Next candidate
|
|
1131
|
+
if (state.last_iteration && state.last_iteration.decision === "keep") {
|
|
1132
|
+
console.log(`- **Next candidate:** Iteration ${formatMarkdownField(state.last_iteration.iteration)} (kept)`);
|
|
1133
|
+
}
|
|
1134
|
+
else if (s.best_iteration) {
|
|
1135
|
+
console.log(`- **Best candidate:** Iteration ${formatMarkdownField(s.best_iteration)}`);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
// Artifact pointers
|
|
1139
|
+
console.log(`\n## Artifacts`);
|
|
1140
|
+
console.log(`- State: ${formatMarkdownField(state.artifact_paths?.state || ".autoresearch/state.json")}`);
|
|
1141
|
+
console.log(`- Results: ${formatMarkdownField(state.artifact_paths?.results || "autoresearch-results.tsv")}`);
|
|
1142
|
+
if (grouped.repo) {
|
|
1143
|
+
console.log(`- Repository: ${formatMarkdownField(grouped.repo)}`);
|
|
1144
|
+
}
|
|
864
1145
|
// Failed branches information
|
|
865
1146
|
if (state.draft_pool && state.draft_pool.active_drafts) {
|
|
866
1147
|
const failedBranches = state.draft_pool.active_drafts.filter(draft => draft.status === "discarded");
|
|
@@ -920,6 +1201,37 @@ const main = async () => {
|
|
|
920
1201
|
break;
|
|
921
1202
|
}
|
|
922
1203
|
case "suggest": {
|
|
1204
|
+
const evidenceGated = grouped["evidence"] === "true";
|
|
1205
|
+
if (evidenceGated) {
|
|
1206
|
+
const { generateIssueCandidate } = await import("./evidence.js");
|
|
1207
|
+
const candidate = generateIssueCandidate(grouped.repo, grouped.goal, grouped.metric, grouped.verify, grouped["score-history-path"]);
|
|
1208
|
+
if (!candidate) {
|
|
1209
|
+
if (useJson) {
|
|
1210
|
+
printJsonEnvelope("suggest", { candidates: [], reason: "insufficient_evidence" });
|
|
1211
|
+
}
|
|
1212
|
+
else {
|
|
1213
|
+
console.log("No evidence-gated issue candidates found.");
|
|
1214
|
+
console.log("Insufficient failure clusters or score history not available.");
|
|
1215
|
+
}
|
|
1216
|
+
break;
|
|
1217
|
+
}
|
|
1218
|
+
if (useJson) {
|
|
1219
|
+
printJsonEnvelope("suggest", { candidates: [candidate], evidence_gated: true });
|
|
1220
|
+
}
|
|
1221
|
+
else {
|
|
1222
|
+
console.log(`Evidence-Gated Issue Candidate:`);
|
|
1223
|
+
console.log(` Title: ${candidate.title}`);
|
|
1224
|
+
console.log(` Goal: ${candidate.goal}`);
|
|
1225
|
+
console.log(` Metric: ${candidate.metric}`);
|
|
1226
|
+
console.log(` Evidence: ${candidate.evidence.total_discards} discards in ${candidate.evidence.total_runs} cluster(s)`);
|
|
1227
|
+
console.log(``);
|
|
1228
|
+
console.log(` Suggested command:`);
|
|
1229
|
+
console.log(` ${candidate.suggest_command}`);
|
|
1230
|
+
console.log(``);
|
|
1231
|
+
console.log(`Review before opening. This candidate is NOT auto-submitted.`);
|
|
1232
|
+
}
|
|
1233
|
+
break;
|
|
1234
|
+
}
|
|
923
1235
|
const { resolvePath } = await import("./helpers.js");
|
|
924
1236
|
const { MEMORY_DEFAULT } = await import("./constants.js");
|
|
925
1237
|
const memoryPath = resolvePath(grouped.repo, grouped["memory-path"], MEMORY_DEFAULT);
|
|
@@ -928,10 +1240,10 @@ const main = async () => {
|
|
|
928
1240
|
break;
|
|
929
1241
|
}
|
|
930
1242
|
const memory = readFileSync(memoryPath, "utf-8");
|
|
931
|
-
const patterns = memory.match(
|
|
1243
|
+
const patterns = memory.match(/^### Pattern: [^\n]+/gm) ?? [];
|
|
932
1244
|
const suggestions = patterns.map(parseMemoryPatternHeading);
|
|
933
1245
|
if (useJson) {
|
|
934
|
-
|
|
1246
|
+
printJsonEnvelope("suggest", { patterns_found: suggestions.length, suggestions });
|
|
935
1247
|
break;
|
|
936
1248
|
}
|
|
937
1249
|
console.log("Memory Patterns — candidate next goals:");
|
|
@@ -974,7 +1286,7 @@ const main = async () => {
|
|
|
974
1286
|
},
|
|
975
1287
|
};
|
|
976
1288
|
if (format === "json") {
|
|
977
|
-
|
|
1289
|
+
printJsonEnvelope("export", exportData);
|
|
978
1290
|
}
|
|
979
1291
|
else if (format === "md" || format === "markdown") {
|
|
980
1292
|
console.log(`# Auto Research Export`);
|
|
@@ -1000,8 +1312,8 @@ const main = async () => {
|
|
|
1000
1312
|
}
|
|
1001
1313
|
case "completion": {
|
|
1002
1314
|
const shell = grouped.shell || "bash";
|
|
1003
|
-
const commands = ["init", "goal", "wizard", "status", "explain", "history", "config", "summary", "suggest", "launch", "complete", "stop", "resume", "record", "doctor", "export", "completion", "help"];
|
|
1004
|
-
const options = ["--repo", "--goal", "--metric", "--direction", "--verify", "--guard", "--mode", "--scope", "--iterations", "--duration", "--num-drafts", "--branch-policy", "--json", "--results-path", "--state-path", "--fresh-start", "--memory-path", "--format", "--shell", "--goal-path", "--template"];
|
|
1315
|
+
const commands = ["init", "goal", "wizard", "status", "explain", "history", "config", "summary", "suggest", "launch", "complete", "stop", "resume", "record", "doctor", "pack", "export", "completion", "help"];
|
|
1316
|
+
const options = ["--repo", "--goal", "--metric", "--direction", "--verify", "--guard", "--mode", "--scope", "--iterations", "--duration", "--num-drafts", "--branch-policy", "--branch-policy-overrides", "--json", "--results-path", "--state-path", "--fresh-start", "--memory-path", "--format", "--shell", "--goal-path", "--template"];
|
|
1005
1317
|
if (shell === "bash" || shell === "zsh") {
|
|
1006
1318
|
console.log(`# Auto Research CLI completion for ${shell}`);
|
|
1007
1319
|
console.log(`_autoresearch() {`);
|
|
@@ -1056,6 +1368,7 @@ const main = async () => {
|
|
|
1056
1368
|
baseline: grouped.baseline,
|
|
1057
1369
|
num_drafts: parsePositiveInt(grouped["num-drafts"], "num_drafts", { max: MAX_DRAFTS }) ?? 1,
|
|
1058
1370
|
branch_selection_policy: normalizeBranchPolicy(grouped["branch-policy"]),
|
|
1371
|
+
branch_policy_overrides: parseBranchPolicyOverrides(grouped["branch-policy-overrides"]),
|
|
1059
1372
|
outcome_metric: grouped["outcome-metric"],
|
|
1060
1373
|
outcome_direction: grouped["outcome-direction"],
|
|
1061
1374
|
instrument_metric: grouped["instrument-metric"],
|
|
@@ -1071,7 +1384,7 @@ const main = async () => {
|
|
|
1071
1384
|
const { writeFileSync } = await import("fs");
|
|
1072
1385
|
const state = await initializeRun(grouped.repo, grouped["results-path"], grouped["state-path"], config, grouped["fresh-start"] === "true");
|
|
1073
1386
|
writeFileSync(launchPath, JSON.stringify({ run_id: state.run_id, goal: state.goal, mode: "background" }, null, 2) + "\n", "utf-8");
|
|
1074
|
-
|
|
1387
|
+
printJsonEnvelope("launch", { status: "launched", run_id: state.run_id, launch_path: launchPath });
|
|
1075
1388
|
break;
|
|
1076
1389
|
}
|
|
1077
1390
|
case "complete": {
|
|
@@ -1081,7 +1394,7 @@ const main = async () => {
|
|
|
1081
1394
|
}
|
|
1082
1395
|
const { completeRun } = await import("./run-manager.js");
|
|
1083
1396
|
const state = await completeRun(grouped.repo, grouped["state-path"]);
|
|
1084
|
-
|
|
1397
|
+
printJsonEnvelope("complete", { status: "completed", run_id: state.run_id });
|
|
1085
1398
|
break;
|
|
1086
1399
|
}
|
|
1087
1400
|
case "stop": {
|
|
@@ -1091,7 +1404,7 @@ const main = async () => {
|
|
|
1091
1404
|
}
|
|
1092
1405
|
const { setStopRequested } = await import("./run-manager.js");
|
|
1093
1406
|
const state = await setStopRequested(grouped.repo, grouped["state-path"]);
|
|
1094
|
-
|
|
1407
|
+
printJsonEnvelope("stop", { status: "stop_requested", run_id: state.run_id });
|
|
1095
1408
|
break;
|
|
1096
1409
|
}
|
|
1097
1410
|
case "resume": {
|
|
@@ -1101,7 +1414,7 @@ const main = async () => {
|
|
|
1101
1414
|
}
|
|
1102
1415
|
const { resumeBackgroundRun } = await import("./run-manager.js");
|
|
1103
1416
|
const state = await resumeBackgroundRun(grouped.repo, grouped["state-path"]);
|
|
1104
|
-
|
|
1417
|
+
printJsonEnvelope("resume", { status: "resumed", run_id: state.run_id });
|
|
1105
1418
|
break;
|
|
1106
1419
|
}
|
|
1107
1420
|
case "record": {
|
|
@@ -1139,12 +1452,21 @@ const main = async () => {
|
|
|
1139
1452
|
note: grouped.note,
|
|
1140
1453
|
iteration,
|
|
1141
1454
|
score_components: scoreComponents,
|
|
1455
|
+
stage: grouped.stage || "improve",
|
|
1456
|
+
selected_action: grouped["selected-action"],
|
|
1142
1457
|
}, null, 2));
|
|
1143
1458
|
return 0;
|
|
1144
1459
|
}
|
|
1145
1460
|
const { appendIteration } = await import("./run-manager.js");
|
|
1146
|
-
const
|
|
1147
|
-
|
|
1461
|
+
const lineage = {};
|
|
1462
|
+
const stage = grouped.stage;
|
|
1463
|
+
if (stage)
|
|
1464
|
+
lineage.stage = stage;
|
|
1465
|
+
const selectedAction = grouped["selected-action"];
|
|
1466
|
+
if (selectedAction)
|
|
1467
|
+
lineage.selected_action = selectedAction;
|
|
1468
|
+
const state = await appendIteration(grouped.repo, grouped["results-path"], grouped["state-path"], grouped.decision, grouped["metric-value"], grouped["instrument-value"], normalizeResultStatus(vs, "verify_status"), normalizeResultStatus(gs, "guard_status"), grouped.hypothesis, grouped["change-summary"], grouped.labels ? (Array.isArray(grouped.labels) ? grouped.labels : [grouped.labels]) : undefined, grouped.note, iteration, undefined, scorerStatus, scoreComponents, Object.keys(lineage).length > 0 ? lineage : undefined);
|
|
1469
|
+
printJsonEnvelope("record", state);
|
|
1148
1470
|
break;
|
|
1149
1471
|
}
|
|
1150
1472
|
case "digest": {
|
|
@@ -1155,7 +1477,7 @@ const main = async () => {
|
|
|
1155
1477
|
const { buildRunDigest } = await import("./run-manager.js");
|
|
1156
1478
|
const digest = await buildRunDigest(grouped.repo, grouped["results-path"], grouped["state-path"]);
|
|
1157
1479
|
if (useJson) {
|
|
1158
|
-
|
|
1480
|
+
printJsonEnvelope("digest", digest);
|
|
1159
1481
|
}
|
|
1160
1482
|
else {
|
|
1161
1483
|
console.log(`# Auto Research Digest`);
|
|
@@ -1199,7 +1521,7 @@ const main = async () => {
|
|
|
1199
1521
|
if (digest.flags && Object.keys(digest.flags).length > 0) {
|
|
1200
1522
|
console.log(`\n## Flags`);
|
|
1201
1523
|
for (const [key, value] of Object.entries(digest.flags)) {
|
|
1202
|
-
console.log(`- ${key}: ${formatMarkdownField(value)}`);
|
|
1524
|
+
console.log(`- ${formatMarkdownField(key)}: ${formatMarkdownField(value)}`);
|
|
1203
1525
|
}
|
|
1204
1526
|
}
|
|
1205
1527
|
}
|
|
@@ -1225,6 +1547,7 @@ const main = async () => {
|
|
|
1225
1547
|
const installedPath = getInstalledPackagePath(PACKAGE_NAME);
|
|
1226
1548
|
const installedInfo = installedPath ? getInstalledPackageInfo(PACKAGE_NAME) : null;
|
|
1227
1549
|
const updateCache = readUpdateCache();
|
|
1550
|
+
const { skip: updateSkipped, reason: skipReason } = shouldSkipUpdateCheck(process.argv.slice(2));
|
|
1228
1551
|
const updateStatus = {
|
|
1229
1552
|
cache_exists: updateCache !== null,
|
|
1230
1553
|
last_check: updateCache?.last_check || null,
|
|
@@ -1232,9 +1555,13 @@ const main = async () => {
|
|
|
1232
1555
|
latest_version: updateCache?.latest_version || null,
|
|
1233
1556
|
update_available: updateCache?.update_available || false,
|
|
1234
1557
|
update_disabled: process.env.AUTORESEARCH_NO_UPDATE === "1",
|
|
1558
|
+
skipped: updateSkipped,
|
|
1559
|
+
skip_reason: skipReason,
|
|
1235
1560
|
};
|
|
1236
1561
|
if (useJson) {
|
|
1237
|
-
|
|
1562
|
+
const { getWhatsNew } = await import("./whats-new.js");
|
|
1563
|
+
const wn = getWhatsNew(base);
|
|
1564
|
+
printJsonEnvelope("doctor", {
|
|
1238
1565
|
version: VERSION,
|
|
1239
1566
|
skill_name: SKILL_NAME,
|
|
1240
1567
|
runtime: `Node.js ${process.version}`,
|
|
@@ -1249,6 +1576,7 @@ const main = async () => {
|
|
|
1249
1576
|
update: updateStatus,
|
|
1250
1577
|
checks: checks,
|
|
1251
1578
|
checks_passed: checks.filter((c) => !c.ok).length === 0,
|
|
1579
|
+
whats_new: wn ? { features: wn.features, fixes: wn.fixes } : null,
|
|
1252
1580
|
});
|
|
1253
1581
|
break;
|
|
1254
1582
|
}
|
|
@@ -1269,7 +1597,10 @@ const main = async () => {
|
|
|
1269
1597
|
}
|
|
1270
1598
|
console.log("");
|
|
1271
1599
|
console.log("Update:");
|
|
1272
|
-
if (
|
|
1600
|
+
if (updateSkipped) {
|
|
1601
|
+
console.log(` Skipped: yes (${skipReason})`);
|
|
1602
|
+
}
|
|
1603
|
+
else if (updateCache) {
|
|
1273
1604
|
console.log(` Last check: ${updateCache.last_check}`);
|
|
1274
1605
|
console.log(` Current: ${updateCache.current_version}`);
|
|
1275
1606
|
console.log(` Latest: ${updateCache.latest_version}`);
|
|
@@ -1294,6 +1625,20 @@ const main = async () => {
|
|
|
1294
1625
|
return 1;
|
|
1295
1626
|
}
|
|
1296
1627
|
console.log(`\nAll ${checks.length} checks passed.`);
|
|
1628
|
+
const showWhatsNew = grouped["whats-new"] === "true";
|
|
1629
|
+
if (showWhatsNew || useJson) {
|
|
1630
|
+
const { getWhatsNew, formatWhatsNew } = await import("./whats-new.js");
|
|
1631
|
+
const wn = getWhatsNew(base);
|
|
1632
|
+
if (wn) {
|
|
1633
|
+
if (useJson) {
|
|
1634
|
+
// Already displayed in JSON envelope — add it for next re-entry
|
|
1635
|
+
}
|
|
1636
|
+
else {
|
|
1637
|
+
console.log("");
|
|
1638
|
+
console.log(formatWhatsNew(wn));
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1297
1642
|
break;
|
|
1298
1643
|
}
|
|
1299
1644
|
case "goal": {
|
|
@@ -1336,7 +1681,7 @@ const main = async () => {
|
|
|
1336
1681
|
}
|
|
1337
1682
|
const doc = readGoalDoc(goalPath);
|
|
1338
1683
|
if (useJson) {
|
|
1339
|
-
|
|
1684
|
+
printJsonEnvelope("goal", doc);
|
|
1340
1685
|
break;
|
|
1341
1686
|
}
|
|
1342
1687
|
console.log(`Goal: ${formatDisplayValue(doc.goal)}`);
|
|
@@ -1456,7 +1801,7 @@ const main = async () => {
|
|
|
1456
1801
|
const result = buildGoalInitResult(goalPath, config, !hasRequiredFlags && isTTY);
|
|
1457
1802
|
if (isGoalDryRun) {
|
|
1458
1803
|
if (useGoalJson) {
|
|
1459
|
-
|
|
1804
|
+
printJsonEnvelope("goal", { ...result, dry_run: true });
|
|
1460
1805
|
}
|
|
1461
1806
|
else {
|
|
1462
1807
|
console.log("[dry-run] Would write goal document to: " + goalPath);
|
|
@@ -1472,7 +1817,7 @@ const main = async () => {
|
|
|
1472
1817
|
}
|
|
1473
1818
|
atomicWriteTextInRepo(goalGrouped.repo, goalPath, document);
|
|
1474
1819
|
if (useGoalJson) {
|
|
1475
|
-
|
|
1820
|
+
printJsonEnvelope("goal", result);
|
|
1476
1821
|
}
|
|
1477
1822
|
else {
|
|
1478
1823
|
console.log(`✓ Goal definition written to ${goalPath}`);
|
|
@@ -1487,6 +1832,181 @@ const main = async () => {
|
|
|
1487
1832
|
}
|
|
1488
1833
|
break;
|
|
1489
1834
|
}
|
|
1835
|
+
case "queue": {
|
|
1836
|
+
const subCmd = cmdArgs[0] || "list";
|
|
1837
|
+
if (subCmd === "help") {
|
|
1838
|
+
console.error("Usage: autoresearch queue <subcommand> [options]");
|
|
1839
|
+
console.error("");
|
|
1840
|
+
console.error("Subcommands:");
|
|
1841
|
+
console.error(" list List tasks in the queue (default)");
|
|
1842
|
+
console.error(" enqueue Enqueue a new task");
|
|
1843
|
+
console.error(" clean Remove completed and failed tasks");
|
|
1844
|
+
break;
|
|
1845
|
+
}
|
|
1846
|
+
if (subCmd === "enqueue") {
|
|
1847
|
+
if (!grouped.goal || !grouped.metric || !grouped.verify) {
|
|
1848
|
+
console.error("--goal, --metric, and --verify are required for enqueue");
|
|
1849
|
+
return 1;
|
|
1850
|
+
}
|
|
1851
|
+
const { enqueueTasks } = await import("./task-queue.js");
|
|
1852
|
+
const tasks = await enqueueTasks(grouped.repo, [{ goal: grouped.goal, metric: grouped.metric, verify: grouped.verify }]);
|
|
1853
|
+
if (useJson) {
|
|
1854
|
+
printJson({ enqueued: tasks });
|
|
1855
|
+
}
|
|
1856
|
+
else {
|
|
1857
|
+
for (const t of tasks) {
|
|
1858
|
+
console.log(`Enqueued: ${t.id} - ${t.goal}`);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
break;
|
|
1862
|
+
}
|
|
1863
|
+
if (subCmd === "clean") {
|
|
1864
|
+
const { listTasks, writeManifest, resolveQueuePath } = await import("./task-queue.js");
|
|
1865
|
+
const queuePath = resolveQueuePath(grouped.repo);
|
|
1866
|
+
const manifest = await listTasks(grouped.repo);
|
|
1867
|
+
const before = manifest.tasks.length;
|
|
1868
|
+
manifest.tasks = manifest.tasks.filter((t) => t.status === "pending" || t.status === "leased");
|
|
1869
|
+
manifest.updated_at = new Date().toISOString();
|
|
1870
|
+
const removed = before - manifest.tasks.length;
|
|
1871
|
+
await writeManifest(queuePath, manifest);
|
|
1872
|
+
if (useJson) {
|
|
1873
|
+
printJson({ removed });
|
|
1874
|
+
}
|
|
1875
|
+
else {
|
|
1876
|
+
console.log(`Cleaned ${removed} completed/failed tasks. ${manifest.tasks.length} remain.`);
|
|
1877
|
+
}
|
|
1878
|
+
break;
|
|
1879
|
+
}
|
|
1880
|
+
const { listTasks } = await import("./task-queue.js");
|
|
1881
|
+
const manifest = await listTasks(grouped.repo);
|
|
1882
|
+
if (useJson) {
|
|
1883
|
+
printJson(manifest);
|
|
1884
|
+
}
|
|
1885
|
+
else {
|
|
1886
|
+
if (manifest.tasks.length === 0) {
|
|
1887
|
+
console.log("No tasks in queue.");
|
|
1888
|
+
}
|
|
1889
|
+
else {
|
|
1890
|
+
console.log(`Task Queue (${manifest.tasks.length} tasks):`);
|
|
1891
|
+
for (const task of manifest.tasks) {
|
|
1892
|
+
const icon = task.status === "completed" ? "v" : task.status === "failed" ? "x" : task.status === "leased" ? ">" : "*";
|
|
1893
|
+
console.log(` ${icon} ${task.id} [${task.status}] ${task.goal}`);
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
break;
|
|
1898
|
+
}
|
|
1899
|
+
case "pack": {
|
|
1900
|
+
const subCmd = cmdArgs[0] || "help";
|
|
1901
|
+
if (subCmd === "help" || (subCmd !== "export" && subCmd !== "list" && subCmd !== "inspect")) {
|
|
1902
|
+
console.error("Usage: autoresearch pack <subcommand> [options]");
|
|
1903
|
+
console.error("Subcommands:");
|
|
1904
|
+
console.error(" export Export validated run as a strategy pack");
|
|
1905
|
+
console.error(" list List available strategy packs");
|
|
1906
|
+
console.error(" inspect View a specific strategy pack");
|
|
1907
|
+
break;
|
|
1908
|
+
}
|
|
1909
|
+
if (subCmd === "export") {
|
|
1910
|
+
const { exportPack } = await import("./strategy-pack.js");
|
|
1911
|
+
const result = exportPack(grouped.repo, grouped["state-path"], grouped["goal-path"]);
|
|
1912
|
+
if (!result) {
|
|
1913
|
+
console.error("No run state found. Complete a run first.");
|
|
1914
|
+
return 1;
|
|
1915
|
+
}
|
|
1916
|
+
if (useJson) {
|
|
1917
|
+
printJsonEnvelope("pack", { exported: result.path, pack: result.pack });
|
|
1918
|
+
}
|
|
1919
|
+
else {
|
|
1920
|
+
console.log(`Strategy pack exported: ${result.path}`);
|
|
1921
|
+
console.log(` Goal: ${result.pack.goal}`);
|
|
1922
|
+
console.log(` Metric: ${result.pack.metric}`);
|
|
1923
|
+
console.log(` Success: ${result.pack.evidence.success_rate}`);
|
|
1924
|
+
}
|
|
1925
|
+
break;
|
|
1926
|
+
}
|
|
1927
|
+
if (subCmd === "list") {
|
|
1928
|
+
const { listPacks } = await import("./strategy-pack.js");
|
|
1929
|
+
const packs = listPacks(grouped.repo);
|
|
1930
|
+
if (useJson) {
|
|
1931
|
+
printJsonEnvelope("pack", { packs });
|
|
1932
|
+
}
|
|
1933
|
+
else if (packs.length === 0) {
|
|
1934
|
+
console.log("No strategy packs found.");
|
|
1935
|
+
}
|
|
1936
|
+
else {
|
|
1937
|
+
console.log(`Strategy Packs (${packs.length}):`);
|
|
1938
|
+
for (const p of packs)
|
|
1939
|
+
console.log(` ${p.name}`);
|
|
1940
|
+
}
|
|
1941
|
+
break;
|
|
1942
|
+
}
|
|
1943
|
+
if (subCmd === "inspect") {
|
|
1944
|
+
const name = cmdArgs[1];
|
|
1945
|
+
if (!name) {
|
|
1946
|
+
console.error("Usage: autoresearch pack inspect <name>");
|
|
1947
|
+
return 1;
|
|
1948
|
+
}
|
|
1949
|
+
const { readPack } = await import("./strategy-pack.js");
|
|
1950
|
+
const content = readPack(grouped.repo, name);
|
|
1951
|
+
if (!content) {
|
|
1952
|
+
console.error(`Pack not found: ${name}`);
|
|
1953
|
+
return 1;
|
|
1954
|
+
}
|
|
1955
|
+
console.log(content);
|
|
1956
|
+
break;
|
|
1957
|
+
}
|
|
1958
|
+
break;
|
|
1959
|
+
}
|
|
1960
|
+
case "leaderboard": {
|
|
1961
|
+
const { generateLeaderboard, formatLeaderboardMarkdown, formatLeaderboardText } = await import("./leaderboard.js");
|
|
1962
|
+
const { resolveRepo } = await import("./helpers.js");
|
|
1963
|
+
const repo = resolveRepo(grouped.repo);
|
|
1964
|
+
const leaderboard = generateLeaderboard(repo);
|
|
1965
|
+
if (useJson) {
|
|
1966
|
+
printJson(leaderboard);
|
|
1967
|
+
break;
|
|
1968
|
+
}
|
|
1969
|
+
if (leaderboard.entries.length === 0) {
|
|
1970
|
+
console.log("No runs found. Complete some runs to see the leaderboard.");
|
|
1971
|
+
break;
|
|
1972
|
+
}
|
|
1973
|
+
if (grouped.format === "markdown") {
|
|
1974
|
+
console.log(formatLeaderboardMarkdown(leaderboard));
|
|
1975
|
+
}
|
|
1976
|
+
else {
|
|
1977
|
+
console.log(formatLeaderboardText(leaderboard));
|
|
1978
|
+
}
|
|
1979
|
+
break;
|
|
1980
|
+
}
|
|
1981
|
+
case "worker": {
|
|
1982
|
+
const { workerOnce } = await import("./worker.js");
|
|
1983
|
+
const once = grouped["once"] === "true";
|
|
1984
|
+
if (!once) {
|
|
1985
|
+
console.error("worker requires --once flag");
|
|
1986
|
+
console.error("Usage: autoresearch worker --once [--json] [--repo <path>]");
|
|
1987
|
+
return 1;
|
|
1988
|
+
}
|
|
1989
|
+
const result = workerOnce(grouped.repo, grouped["state-path"], grouped["results-path"]);
|
|
1990
|
+
if (useJson) {
|
|
1991
|
+
printJsonEnvelope("worker", result);
|
|
1992
|
+
}
|
|
1993
|
+
else {
|
|
1994
|
+
if (result.ready) {
|
|
1995
|
+
console.log(`✓ Ready for iteration ${result.iteration}`);
|
|
1996
|
+
console.log(` Run ID: ${result.run_id}`);
|
|
1997
|
+
console.log(` Status: ${result.status}`);
|
|
1998
|
+
console.log(` Goal: ${result.goal}`);
|
|
1999
|
+
if (result.metric)
|
|
2000
|
+
console.log(` Metric: ${result.metric}`);
|
|
2001
|
+
}
|
|
2002
|
+
else {
|
|
2003
|
+
console.log(`✗ Not ready: ${result.reason || "unknown"}`);
|
|
2004
|
+
console.log(` Run ID: ${result.run_id}`);
|
|
2005
|
+
console.log(` Iter: ${result.iteration}`);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
return result.ready ? 0 : 1;
|
|
2009
|
+
}
|
|
1490
2010
|
default: {
|
|
1491
2011
|
console.error(`Unknown command: ${cmd}`);
|
|
1492
2012
|
console.error("Run 'autoresearch --help' for usage.");
|
|
@@ -1495,7 +2015,14 @@ const main = async () => {
|
|
|
1495
2015
|
}
|
|
1496
2016
|
}
|
|
1497
2017
|
catch (exc) {
|
|
1498
|
-
|
|
2018
|
+
const { categorizeError, formatStructuredError } = await import("./error-categories.js");
|
|
2019
|
+
const structured = categorizeError(exc);
|
|
2020
|
+
if (useJson) {
|
|
2021
|
+
console.error(formatStructuredError(structured, true));
|
|
2022
|
+
}
|
|
2023
|
+
else {
|
|
2024
|
+
console.error(formatStructuredError(structured, false));
|
|
2025
|
+
}
|
|
1499
2026
|
return 2;
|
|
1500
2027
|
}
|
|
1501
2028
|
return 0;
|