@autonoma-ai/planner 0.1.25 → 0.1.26
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/README.md +6 -3
- package/dist/index.js +685 -406
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -9,11 +9,11 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// ../../node_modules/.pnpm/tsup@8.5.1_@swc+core@1.15.3_jiti@2.6.1_postcss@8.5.
|
|
12
|
+
// ../../node_modules/.pnpm/tsup@8.5.1_@swc+core@1.15.3_jiti@2.6.1_postcss@8.5.25_tsx@4.21.0_typescript@7.0.2_yaml@2.8.3/node_modules/tsup/assets/esm_shims.js
|
|
13
13
|
import path from "path";
|
|
14
14
|
import { fileURLToPath } from "url";
|
|
15
15
|
var init_esm_shims = __esm({
|
|
16
|
-
"../../node_modules/.pnpm/tsup@8.5.1_@swc+core@1.15.3_jiti@2.6.1_postcss@8.5.
|
|
16
|
+
"../../node_modules/.pnpm/tsup@8.5.1_@swc+core@1.15.3_jiti@2.6.1_postcss@8.5.25_tsx@4.21.0_typescript@7.0.2_yaml@2.8.3/node_modules/tsup/assets/esm_shims.js"() {
|
|
17
17
|
"use strict";
|
|
18
18
|
}
|
|
19
19
|
});
|
|
@@ -1019,6 +1019,11 @@ function budgetFor(step, sizes) {
|
|
|
1019
1019
|
}
|
|
1020
1020
|
function runningFraction(step, now, budget) {
|
|
1021
1021
|
if (step.sub && step.sub.total > 0) {
|
|
1022
|
+
if (step.sub.done === 0 && step.sub.startedAt != null) {
|
|
1023
|
+
const elapsed = now - step.sub.startedAt;
|
|
1024
|
+
const unspendable = Math.min(budget, RUNNING_MIN_REMAINING_MS);
|
|
1025
|
+
return clamp01((budget - unspendable) * (elapsed / budget));
|
|
1026
|
+
}
|
|
1022
1027
|
return clamp01(step.sub.done / step.sub.total);
|
|
1023
1028
|
}
|
|
1024
1029
|
if (step.startedAt != null) {
|
|
@@ -1028,8 +1033,9 @@ function runningFraction(step, now, budget) {
|
|
|
1028
1033
|
}
|
|
1029
1034
|
function liveRemainingMs(step, now) {
|
|
1030
1035
|
if (step.sub == null || step.sub.total <= 0 || step.startedAt == null) return void 0;
|
|
1036
|
+
if (step.sub.done < LIVE_RATE_MIN_DONE) return void 0;
|
|
1031
1037
|
const elapsed = now - step.startedAt;
|
|
1032
|
-
if (
|
|
1038
|
+
if (elapsed < LIVE_RATE_MIN_ELAPSED_MS) return void 0;
|
|
1033
1039
|
return elapsed / step.sub.done * Math.max(0, step.sub.total - step.sub.done);
|
|
1034
1040
|
}
|
|
1035
1041
|
function paceRatio(state) {
|
|
@@ -1316,6 +1322,19 @@ var init_prompt = __esm({
|
|
|
1316
1322
|
}
|
|
1317
1323
|
});
|
|
1318
1324
|
|
|
1325
|
+
// src/ui/types.ts
|
|
1326
|
+
function isReviewStatus(status) {
|
|
1327
|
+
return REVIEW_STATUSES.has(status);
|
|
1328
|
+
}
|
|
1329
|
+
var REVIEW_STATUSES;
|
|
1330
|
+
var init_types = __esm({
|
|
1331
|
+
"src/ui/types.ts"() {
|
|
1332
|
+
"use strict";
|
|
1333
|
+
init_esm_shims();
|
|
1334
|
+
REVIEW_STATUSES = /* @__PURE__ */ new Set(["REVIEWING", "REVIEWED", "FIXING"]);
|
|
1335
|
+
}
|
|
1336
|
+
});
|
|
1337
|
+
|
|
1319
1338
|
// src/ui/store.ts
|
|
1320
1339
|
import { join as join5 } from "path";
|
|
1321
1340
|
function initialSteps() {
|
|
@@ -1519,7 +1538,7 @@ function createStore(opts) {
|
|
|
1519
1538
|
if (status === "done") {
|
|
1520
1539
|
for (const id of state.steps[name].artifactIds) {
|
|
1521
1540
|
const art = artifacts[id];
|
|
1522
|
-
if (art) artifacts[id] = { ...art, status: "DONE" };
|
|
1541
|
+
if (art != null && !isReviewStatus(art.status)) artifacts[id] = { ...art, status: "DONE" };
|
|
1523
1542
|
}
|
|
1524
1543
|
}
|
|
1525
1544
|
set({
|
|
@@ -1603,6 +1622,23 @@ function createStore(opts) {
|
|
|
1603
1622
|
setLiveFile(relPath, text2, kind) {
|
|
1604
1623
|
setLiveText(relPath, text2, kind);
|
|
1605
1624
|
},
|
|
1625
|
+
setArtifactReview(path3, status) {
|
|
1626
|
+
const relPath = relOf(path3);
|
|
1627
|
+
const art = state.artifacts[relPath];
|
|
1628
|
+
if (art == null || art.status === status) return;
|
|
1629
|
+
set({ ...state, artifacts: { ...state.artifacts, [relPath]: { ...art, status } } });
|
|
1630
|
+
},
|
|
1631
|
+
settleReviews() {
|
|
1632
|
+
const artifacts = { ...state.artifacts };
|
|
1633
|
+
let changed = false;
|
|
1634
|
+
for (const [id, art] of Object.entries(artifacts)) {
|
|
1635
|
+
if (art.status !== "REVIEWING" && art.status !== "FIXING") continue;
|
|
1636
|
+
artifacts[id] = { ...art, status: "DONE" };
|
|
1637
|
+
changed = true;
|
|
1638
|
+
}
|
|
1639
|
+
if (!changed) return;
|
|
1640
|
+
set({ ...state, artifacts });
|
|
1641
|
+
},
|
|
1606
1642
|
finish(outcome) {
|
|
1607
1643
|
store.stopClock();
|
|
1608
1644
|
set({
|
|
@@ -1709,11 +1745,11 @@ function createStore(opts) {
|
|
|
1709
1745
|
if (state.countdown != null) set({ ...state, countdown: void 0 });
|
|
1710
1746
|
resolve6?.();
|
|
1711
1747
|
},
|
|
1712
|
-
runWelcome({ title, lines, cta }) {
|
|
1748
|
+
runWelcome({ title, lines, cta, eyebrow }) {
|
|
1713
1749
|
return new Promise((resolve6) => {
|
|
1714
1750
|
welcomeResolve = resolve6;
|
|
1715
1751
|
if (process.stdout.isTTY) process.stdout.write(BELL);
|
|
1716
|
-
set({ ...state, welcome: { title, lines, cta } });
|
|
1752
|
+
set({ ...state, welcome: { title, lines, cta, eyebrow } });
|
|
1717
1753
|
});
|
|
1718
1754
|
},
|
|
1719
1755
|
dismissWelcome() {
|
|
@@ -1806,6 +1842,7 @@ var init_store = __esm({
|
|
|
1806
1842
|
init_nav();
|
|
1807
1843
|
init_prompt();
|
|
1808
1844
|
init_steps();
|
|
1845
|
+
init_types();
|
|
1809
1846
|
LOG_CAP = 500;
|
|
1810
1847
|
ACTIVITY_CAP = 200;
|
|
1811
1848
|
EMIT_COALESCE_MS = 16;
|
|
@@ -4702,6 +4739,22 @@ var init_app_links = __esm({
|
|
|
4702
4739
|
}
|
|
4703
4740
|
});
|
|
4704
4741
|
|
|
4742
|
+
// ../../packages/types/src/agent-handoff-links.ts
|
|
4743
|
+
var init_agent_handoff_links = __esm({
|
|
4744
|
+
"../../packages/types/src/agent-handoff-links.ts"() {
|
|
4745
|
+
"use strict";
|
|
4746
|
+
init_esm_shims();
|
|
4747
|
+
}
|
|
4748
|
+
});
|
|
4749
|
+
|
|
4750
|
+
// ../../packages/types/src/sdk-error-signals.ts
|
|
4751
|
+
var init_sdk_error_signals = __esm({
|
|
4752
|
+
"../../packages/types/src/sdk-error-signals.ts"() {
|
|
4753
|
+
"use strict";
|
|
4754
|
+
init_esm_shims();
|
|
4755
|
+
}
|
|
4756
|
+
});
|
|
4757
|
+
|
|
4705
4758
|
// ../../packages/types/src/designated-run.ts
|
|
4706
4759
|
var init_designated_run = __esm({
|
|
4707
4760
|
"../../packages/types/src/designated-run.ts"() {
|
|
@@ -4718,6 +4771,14 @@ var init_deployment_signal_template = __esm({
|
|
|
4718
4771
|
}
|
|
4719
4772
|
});
|
|
4720
4773
|
|
|
4774
|
+
// ../../packages/types/src/constants/analytics-headers.ts
|
|
4775
|
+
var init_analytics_headers = __esm({
|
|
4776
|
+
"../../packages/types/src/constants/analytics-headers.ts"() {
|
|
4777
|
+
"use strict";
|
|
4778
|
+
init_esm_shims();
|
|
4779
|
+
}
|
|
4780
|
+
});
|
|
4781
|
+
|
|
4721
4782
|
// ../../packages/types/src/constants/pipeline-labels.ts
|
|
4722
4783
|
var init_pipeline_labels = __esm({
|
|
4723
4784
|
"../../packages/types/src/constants/pipeline-labels.ts"() {
|
|
@@ -4731,6 +4792,7 @@ var init_constants = __esm({
|
|
|
4731
4792
|
"../../packages/types/src/constants/index.ts"() {
|
|
4732
4793
|
"use strict";
|
|
4733
4794
|
init_esm_shims();
|
|
4795
|
+
init_analytics_headers();
|
|
4734
4796
|
init_billing();
|
|
4735
4797
|
init_pipeline_labels();
|
|
4736
4798
|
}
|
|
@@ -4790,6 +4852,8 @@ var init_src = __esm({
|
|
|
4790
4852
|
init_sensitive_detection();
|
|
4791
4853
|
init_preview_url();
|
|
4792
4854
|
init_app_links();
|
|
4855
|
+
init_agent_handoff_links();
|
|
4856
|
+
init_sdk_error_signals();
|
|
4793
4857
|
init_designated_run();
|
|
4794
4858
|
init_deployment_signal_template();
|
|
4795
4859
|
init_constants();
|
|
@@ -5561,6 +5625,98 @@ var init_autonoma_client = __esm({
|
|
|
5561
5625
|
}
|
|
5562
5626
|
});
|
|
5563
5627
|
|
|
5628
|
+
// src/core/output.ts
|
|
5629
|
+
import { mkdir, rm, writeFile as writeFile2 } from "fs/promises";
|
|
5630
|
+
import { homedir as homedir3 } from "os";
|
|
5631
|
+
import { join as join10 } from "path";
|
|
5632
|
+
function getOutputDir(projectSlug) {
|
|
5633
|
+
return join10(AUTONOMA_HOME3, projectSlug);
|
|
5634
|
+
}
|
|
5635
|
+
async function clearOutputDir(projectSlug) {
|
|
5636
|
+
const dir = getOutputDir(projectSlug);
|
|
5637
|
+
await rm(dir, { recursive: true, force: true });
|
|
5638
|
+
await mkdir(dir, { recursive: true });
|
|
5639
|
+
debugLog("Cleared the run directory for a fresh start", { dir });
|
|
5640
|
+
}
|
|
5641
|
+
function displayPath(absPath) {
|
|
5642
|
+
const home = homedir3();
|
|
5643
|
+
return absPath.startsWith(home) ? `~${absPath.slice(home.length)}` : absPath;
|
|
5644
|
+
}
|
|
5645
|
+
async function ensureOutputDir(projectSlug) {
|
|
5646
|
+
const dir = getOutputDir(projectSlug);
|
|
5647
|
+
await mkdir(dir, { recursive: true });
|
|
5648
|
+
const probe = join10(dir, WRITE_PROBE_FILE);
|
|
5649
|
+
try {
|
|
5650
|
+
await writeFile2(probe, "ok", "utf-8");
|
|
5651
|
+
} catch (err) {
|
|
5652
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
5653
|
+
throw new Error(
|
|
5654
|
+
`Cannot write to ${displayPath(dir)} - the planner keeps every artifact it generates there.
|
|
5655
|
+
${reason}
|
|
5656
|
+
Free up disk space or fix the directory's permissions, then re-run.`
|
|
5657
|
+
);
|
|
5658
|
+
}
|
|
5659
|
+
await rm(probe, { force: true }).catch((err) => {
|
|
5660
|
+
debugLog("Could not remove the write probe", { probe, err });
|
|
5661
|
+
});
|
|
5662
|
+
return dir;
|
|
5663
|
+
}
|
|
5664
|
+
var AUTONOMA_HOME3, WRITE_PROBE_FILE;
|
|
5665
|
+
var init_output = __esm({
|
|
5666
|
+
"src/core/output.ts"() {
|
|
5667
|
+
"use strict";
|
|
5668
|
+
init_esm_shims();
|
|
5669
|
+
init_debug();
|
|
5670
|
+
AUTONOMA_HOME3 = join10(homedir3(), ".autonoma");
|
|
5671
|
+
WRITE_PROBE_FILE = ".write-probe";
|
|
5672
|
+
}
|
|
5673
|
+
});
|
|
5674
|
+
|
|
5675
|
+
// src/core/preferences.ts
|
|
5676
|
+
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
5677
|
+
import { join as join11 } from "path";
|
|
5678
|
+
import { z as z36 } from "zod";
|
|
5679
|
+
async function readPreferences() {
|
|
5680
|
+
try {
|
|
5681
|
+
const raw = await readFile4(PREFERENCES_FILE, "utf-8");
|
|
5682
|
+
const parsed = PreferencesSchema.safeParse(JSON.parse(raw));
|
|
5683
|
+
if (!parsed.success) {
|
|
5684
|
+
debugLog("Ignoring unreadable preferences file", { issues: parsed.error.issues });
|
|
5685
|
+
return {};
|
|
5686
|
+
}
|
|
5687
|
+
return parsed.data;
|
|
5688
|
+
} catch (err) {
|
|
5689
|
+
debugLog("No preferences to read", { err });
|
|
5690
|
+
return {};
|
|
5691
|
+
}
|
|
5692
|
+
}
|
|
5693
|
+
async function updatePreferences(update) {
|
|
5694
|
+
try {
|
|
5695
|
+
const current = await readPreferences();
|
|
5696
|
+
const next = { ...current, ...update };
|
|
5697
|
+
await mkdir2(AUTONOMA_HOME3, { recursive: true });
|
|
5698
|
+
await writeFile3(PREFERENCES_FILE, `${JSON.stringify(next, null, 2)}
|
|
5699
|
+
`, "utf-8");
|
|
5700
|
+
debugLog("Saved preferences", { update });
|
|
5701
|
+
} catch (err) {
|
|
5702
|
+
debugLog("Could not save preferences", { err });
|
|
5703
|
+
}
|
|
5704
|
+
}
|
|
5705
|
+
var PREFERENCES_FILE, PreferencesSchema;
|
|
5706
|
+
var init_preferences = __esm({
|
|
5707
|
+
"src/core/preferences.ts"() {
|
|
5708
|
+
"use strict";
|
|
5709
|
+
init_esm_shims();
|
|
5710
|
+
init_debug();
|
|
5711
|
+
init_output();
|
|
5712
|
+
PREFERENCES_FILE = join11(AUTONOMA_HOME3, "preferences.json");
|
|
5713
|
+
PreferencesSchema = z36.object({
|
|
5714
|
+
/** The coding agent id (`claude`, `codex`) last chosen from the picker. */
|
|
5715
|
+
agentId: z36.string().optional()
|
|
5716
|
+
});
|
|
5717
|
+
}
|
|
5718
|
+
});
|
|
5719
|
+
|
|
5564
5720
|
// src/core/coding-agent.ts
|
|
5565
5721
|
var coding_agent_exports = {};
|
|
5566
5722
|
__export(coding_agent_exports, {
|
|
@@ -5588,10 +5744,14 @@ async function selectLauncher(launchers, presetId, interactive = true) {
|
|
|
5588
5744
|
const availability = await Promise.all(launchers.map((l) => l.isAvailable()));
|
|
5589
5745
|
const available = launchers.filter((_, i) => availability[i]);
|
|
5590
5746
|
debugLog("Detected available agents", { available: available.map((l) => l.id), presetId, interactive });
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5747
|
+
const remembered = presetId ?? (await readPreferences()).agentId;
|
|
5748
|
+
if (remembered != null) {
|
|
5749
|
+
const preset = available.find((l) => l.id === remembered);
|
|
5750
|
+
if (preset != null) {
|
|
5751
|
+
if (presetId == null) log.info(`Using ${preset.label} - the agent you picked last time.`);
|
|
5752
|
+
return preset;
|
|
5753
|
+
}
|
|
5754
|
+
if (presetId != null) log.warn(`Requested agent "${presetId}" is not installed or not supported.`);
|
|
5595
5755
|
}
|
|
5596
5756
|
if (available.length === 0) return void 0;
|
|
5597
5757
|
if (available.length === 1) {
|
|
@@ -5611,6 +5771,7 @@ async function selectLauncher(launchers, presetId, interactive = true) {
|
|
|
5611
5771
|
options: available.map((l) => ({ value: l.id, label: l.label }))
|
|
5612
5772
|
});
|
|
5613
5773
|
if (isCancel(selected)) throw new Error("Agent selection cancelled");
|
|
5774
|
+
await updatePreferences({ agentId: selected });
|
|
5614
5775
|
return available.find((l) => l.id === selected);
|
|
5615
5776
|
}
|
|
5616
5777
|
async function selectPermissionMode(preset) {
|
|
@@ -5641,6 +5802,7 @@ var init_coding_agent = __esm({
|
|
|
5641
5802
|
init_esm_shims();
|
|
5642
5803
|
init_prompts();
|
|
5643
5804
|
init_debug();
|
|
5805
|
+
init_preferences();
|
|
5644
5806
|
MCP_COMMAND_TIMEOUT_MS = 6e4;
|
|
5645
5807
|
SPAWNED_BY_PLANNER_ENV = "AUTONOMA_PLANNER_SPAWNED_AGENT";
|
|
5646
5808
|
CODEX_BEARER_TOKEN_ENV = "AUTONOMA_MCP_TOKEN";
|
|
@@ -6385,16 +6547,16 @@ var init_notify = __esm({
|
|
|
6385
6547
|
});
|
|
6386
6548
|
|
|
6387
6549
|
// src/core/project-map.ts
|
|
6388
|
-
import { readFile as
|
|
6389
|
-
import { join as
|
|
6390
|
-
import { z as
|
|
6550
|
+
import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
6551
|
+
import { join as join13 } from "path";
|
|
6552
|
+
import { z as z37 } from "zod";
|
|
6391
6553
|
async function saveProjectMap(outputDir, map) {
|
|
6392
|
-
await
|
|
6554
|
+
await writeFile5(join13(outputDir, PROJECT_MAP_FILE), JSON.stringify(map, null, 2), "utf-8");
|
|
6393
6555
|
}
|
|
6394
6556
|
async function loadProjectMap(outputDir) {
|
|
6395
|
-
const path3 =
|
|
6557
|
+
const path3 = join13(outputDir, PROJECT_MAP_FILE);
|
|
6396
6558
|
try {
|
|
6397
|
-
const raw = await
|
|
6559
|
+
const raw = await readFile6(path3, "utf-8");
|
|
6398
6560
|
const parsed = ProjectMapSchema.safeParse(JSON.parse(raw));
|
|
6399
6561
|
if (parsed.success) return parsed.data;
|
|
6400
6562
|
debugLog("project-map.json failed schema validation, ignoring it", { path: path3, issues: parsed.error.issues });
|
|
@@ -6496,34 +6658,34 @@ var init_project_map = __esm({
|
|
|
6496
6658
|
"use strict";
|
|
6497
6659
|
init_esm_shims();
|
|
6498
6660
|
init_debug();
|
|
6499
|
-
FrontendEntry =
|
|
6500
|
-
path:
|
|
6501
|
-
framework:
|
|
6502
|
-
dependsOn:
|
|
6661
|
+
FrontendEntry = z37.object({
|
|
6662
|
+
path: z37.string().min(1).describe("Repo-relative path to the frontend app/directory (the UI surface)."),
|
|
6663
|
+
framework: z37.string().describe("Detected UI framework/stack, or 'unknown' if not obvious (e.g. next, react, vue, svelte)."),
|
|
6664
|
+
dependsOn: z37.array(z37.string()).describe(
|
|
6503
6665
|
"Repo-relative paths (each matching a backend in this map) that THIS frontend needs in order to function - the API/service(s) it calls and the data layer(s) that own the records it renders. These are pre-selected when the user picks this frontend, so only list backends this frontend actually depends on. Empty if it needs none."
|
|
6504
6666
|
),
|
|
6505
|
-
why:
|
|
6506
|
-
});
|
|
6507
|
-
BackendEntry =
|
|
6508
|
-
path:
|
|
6509
|
-
language:
|
|
6510
|
-
framework:
|
|
6511
|
-
dataLayer:
|
|
6512
|
-
kind:
|
|
6513
|
-
schemaPath:
|
|
6667
|
+
why: z37.string().min(1).describe("One line: the evidence that made you classify this as a frontend.")
|
|
6668
|
+
});
|
|
6669
|
+
BackendEntry = z37.object({
|
|
6670
|
+
path: z37.string().min(1).describe("Repo-relative path to the backend/API/service or the package that owns the data layer."),
|
|
6671
|
+
language: z37.string().describe("Primary language, or 'unknown' (e.g. typescript, python, go, rust)."),
|
|
6672
|
+
framework: z37.string().describe("Web/service framework or ORM stack, or 'unknown' (e.g. express, hono, fastapi, rails)."),
|
|
6673
|
+
dataLayer: z37.object({
|
|
6674
|
+
kind: z37.string().describe("How models are defined (e.g. prisma, drizzle, sqlalchemy, typeorm, raw-sql, unknown)."),
|
|
6675
|
+
schemaPath: z37.string().optional().describe("Repo-relative path to the schema/models definition, if found.")
|
|
6514
6676
|
}).optional().describe("Where this backend's database models live. Omit if this backend owns no data layer."),
|
|
6515
|
-
why:
|
|
6677
|
+
why: z37.string().min(1).describe("One line: the evidence that made you classify this as a backend.")
|
|
6516
6678
|
});
|
|
6517
|
-
IgnoreEntry =
|
|
6518
|
-
path:
|
|
6519
|
-
why:
|
|
6679
|
+
IgnoreEntry = z37.object({
|
|
6680
|
+
path: z37.string().min(1).describe("Repo-relative path to a directory that is NOT relevant to testing this app."),
|
|
6681
|
+
why: z37.string().min(1).describe("One line: why it is irrelevant (e.g. docs site, infra, tooling, examples, unrelated app).")
|
|
6520
6682
|
});
|
|
6521
|
-
ProjectMapSchema =
|
|
6522
|
-
frontends:
|
|
6523
|
-
backends:
|
|
6683
|
+
ProjectMapSchema = z37.object({
|
|
6684
|
+
frontends: z37.array(FrontendEntry).describe("The UI surface(s) whose pages/flows get tested. Usually 1; may be more."),
|
|
6685
|
+
backends: z37.array(BackendEntry).describe(
|
|
6524
6686
|
"The API/service(s) and data layer(s) that own the models we seed. May be 0 (frontend-only), 1, or many."
|
|
6525
6687
|
),
|
|
6526
|
-
ignore:
|
|
6688
|
+
ignore: z37.array(IgnoreEntry).describe("Directories judged irrelevant to this app's tests, so later steps skip them.")
|
|
6527
6689
|
});
|
|
6528
6690
|
PROJECT_MAP_FILE = "project-map.json";
|
|
6529
6691
|
}
|
|
@@ -6534,11 +6696,11 @@ var parse_entity_audit_exports = {};
|
|
|
6534
6696
|
__export(parse_entity_audit_exports, {
|
|
6535
6697
|
parseEntityNames: () => parseEntityNames
|
|
6536
6698
|
});
|
|
6537
|
-
import { readFile as
|
|
6538
|
-
import { join as
|
|
6699
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
6700
|
+
import { join as join15 } from "path";
|
|
6539
6701
|
async function parseEntityNames(outputDir) {
|
|
6540
6702
|
try {
|
|
6541
|
-
const content = await
|
|
6703
|
+
const content = await readFile8(join15(outputDir, "entity-audit.md"), "utf-8");
|
|
6542
6704
|
const names = [];
|
|
6543
6705
|
for (const line of content.split("\n")) {
|
|
6544
6706
|
const match = line.match(/^\s+-\s+name:\s+(.+)$/);
|
|
@@ -6573,7 +6735,7 @@ var init_count_generated_tests = __esm({
|
|
|
6573
6735
|
});
|
|
6574
6736
|
|
|
6575
6737
|
// src/core/display.ts
|
|
6576
|
-
import { join as
|
|
6738
|
+
import { join as join16 } from "path";
|
|
6577
6739
|
function formatArgs(input, keys) {
|
|
6578
6740
|
const parts = [];
|
|
6579
6741
|
for (const key of keys) {
|
|
@@ -6665,7 +6827,7 @@ function logToStore(store, agentId, info, stats) {
|
|
|
6665
6827
|
const folder = String(tc.input.folder ?? "");
|
|
6666
6828
|
const filename = String(tc.input.filename ?? "");
|
|
6667
6829
|
if (folder && filename) {
|
|
6668
|
-
store.noteWrite(
|
|
6830
|
+
store.noteWrite(join16(TESTS_DIR, folder, normalizeTestFilename(filename)));
|
|
6669
6831
|
}
|
|
6670
6832
|
}
|
|
6671
6833
|
if (tc.name === "bash") {
|
|
@@ -6917,7 +7079,7 @@ async function runAgent(config, prompt, extractResult) {
|
|
|
6917
7079
|
agent: config.id,
|
|
6918
7080
|
nudge: nudges
|
|
6919
7081
|
});
|
|
6920
|
-
messages.push(...generation.
|
|
7082
|
+
messages.push(...generation.responseMessages);
|
|
6921
7083
|
messages.push({ role: "user", content: NUDGE_PROMPT });
|
|
6922
7084
|
generation = await agent.generate({ messages, ...callOptions });
|
|
6923
7085
|
}
|
|
@@ -6973,8 +7135,8 @@ var init_agent = __esm({
|
|
|
6973
7135
|
});
|
|
6974
7136
|
|
|
6975
7137
|
// src/core/gitignore.ts
|
|
6976
|
-
import { readFile as
|
|
6977
|
-
import { join as
|
|
7138
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
7139
|
+
import { join as join17, relative as relative2 } from "path";
|
|
6978
7140
|
import { glob as glob2 } from "glob";
|
|
6979
7141
|
async function loadGitignorePatterns(projectRoot) {
|
|
6980
7142
|
const patterns = [
|
|
@@ -6994,10 +7156,10 @@ async function loadGitignorePatterns(projectRoot) {
|
|
|
6994
7156
|
];
|
|
6995
7157
|
const matches = await glob2("**/.gitignore", { cwd: projectRoot, dot: true });
|
|
6996
7158
|
for (const match of matches) {
|
|
6997
|
-
const fullPath =
|
|
7159
|
+
const fullPath = join17(projectRoot, match);
|
|
6998
7160
|
try {
|
|
6999
|
-
const content = await
|
|
7000
|
-
const prefix = relative2(projectRoot,
|
|
7161
|
+
const content = await readFile9(fullPath, "utf-8");
|
|
7162
|
+
const prefix = relative2(projectRoot, join17(projectRoot, match, ".."));
|
|
7001
7163
|
const parsed = parseGitignore(content, prefix);
|
|
7002
7164
|
patterns.push(...parsed);
|
|
7003
7165
|
} catch (err) {
|
|
@@ -7060,7 +7222,7 @@ var init_exec_error = __esm({
|
|
|
7060
7222
|
import { execFile as execFile3 } from "child_process";
|
|
7061
7223
|
import { promisify as promisify2 } from "util";
|
|
7062
7224
|
import { tool } from "ai";
|
|
7063
|
-
import { z as
|
|
7225
|
+
import { z as z38 } from "zod";
|
|
7064
7226
|
function validateCommand(command, allowed) {
|
|
7065
7227
|
const trimmed = command.trim();
|
|
7066
7228
|
if (trimmed.length === 0) return "Empty command";
|
|
@@ -7126,8 +7288,8 @@ var init_bash = __esm({
|
|
|
7126
7288
|
DEFAULT_ALLOWED = /* @__PURE__ */ new Set(["git", "wc", "sort", "head", "tail", "cat", "ls", "find", "diff", "echo"]);
|
|
7127
7289
|
TIMEOUT_MS = 3e4;
|
|
7128
7290
|
MAX_OUTPUT_BYTES = 1024 * 512;
|
|
7129
|
-
inputSchema =
|
|
7130
|
-
command:
|
|
7291
|
+
inputSchema = z38.object({
|
|
7292
|
+
command: z38.string().describe("Shell command to execute")
|
|
7131
7293
|
});
|
|
7132
7294
|
}
|
|
7133
7295
|
});
|
|
@@ -7135,7 +7297,7 @@ var init_bash = __esm({
|
|
|
7135
7297
|
// src/tools/glob.ts
|
|
7136
7298
|
import { tool as tool2 } from "ai";
|
|
7137
7299
|
import { glob as glob3 } from "glob";
|
|
7138
|
-
import { z as
|
|
7300
|
+
import { z as z39 } from "zod";
|
|
7139
7301
|
async function executeGlob(pattern, cwd, ignorePatterns = DEFAULT_IGNORE) {
|
|
7140
7302
|
try {
|
|
7141
7303
|
const matches = await glob3(pattern, {
|
|
@@ -7169,9 +7331,9 @@ var init_glob = __esm({
|
|
|
7169
7331
|
"src/tools/glob.ts"() {
|
|
7170
7332
|
"use strict";
|
|
7171
7333
|
init_esm_shims();
|
|
7172
|
-
inputSchema2 =
|
|
7173
|
-
pattern:
|
|
7174
|
-
cwd:
|
|
7334
|
+
inputSchema2 = z39.object({
|
|
7335
|
+
pattern: z39.string().describe("Glob pattern to match files (e.g. '**/*.ts', 'src/**/*.py')"),
|
|
7336
|
+
cwd: z39.string().optional().describe("Directory to search in. Defaults to working directory.")
|
|
7175
7337
|
});
|
|
7176
7338
|
DEFAULT_IGNORE = [
|
|
7177
7339
|
"**/node_modules/**",
|
|
@@ -7189,7 +7351,7 @@ var init_glob = __esm({
|
|
|
7189
7351
|
import { execFile as execFile4 } from "child_process";
|
|
7190
7352
|
import { promisify as promisify3 } from "util";
|
|
7191
7353
|
import { tool as tool3 } from "ai";
|
|
7192
|
-
import { z as
|
|
7354
|
+
import { z as z40 } from "zod";
|
|
7193
7355
|
function buildGrepTool(workingDirectory) {
|
|
7194
7356
|
return tool3({
|
|
7195
7357
|
description: "Search file contents with ripgrep. Returns matching lines with file paths and line numbers.",
|
|
@@ -7233,10 +7395,10 @@ var init_grep = __esm({
|
|
|
7233
7395
|
init_esm_shims();
|
|
7234
7396
|
init_exec_error();
|
|
7235
7397
|
execFileAsync3 = promisify3(execFile4);
|
|
7236
|
-
inputSchema3 =
|
|
7237
|
-
pattern:
|
|
7238
|
-
glob:
|
|
7239
|
-
path:
|
|
7398
|
+
inputSchema3 = z40.object({
|
|
7399
|
+
pattern: z40.string().describe("Regex pattern to search for in file contents"),
|
|
7400
|
+
glob: z40.string().optional().describe("Glob to filter files (e.g. '*.ts')"),
|
|
7401
|
+
path: z40.string().optional().describe("File or directory to search in")
|
|
7240
7402
|
});
|
|
7241
7403
|
}
|
|
7242
7404
|
});
|
|
@@ -7244,10 +7406,10 @@ var init_grep = __esm({
|
|
|
7244
7406
|
// src/tools/list-directory.ts
|
|
7245
7407
|
import { readdir } from "fs/promises";
|
|
7246
7408
|
import { stat } from "fs/promises";
|
|
7247
|
-
import { join as
|
|
7409
|
+
import { join as join18, relative as relative3 } from "path";
|
|
7248
7410
|
import { tool as tool4 } from "ai";
|
|
7249
7411
|
import { minimatch } from "minimatch";
|
|
7250
|
-
import { z as
|
|
7412
|
+
import { z as z41 } from "zod";
|
|
7251
7413
|
function buildMatcher(patterns) {
|
|
7252
7414
|
const positive = patterns.filter((p) => !p.startsWith("!"));
|
|
7253
7415
|
const negative = patterns.filter((p) => p.startsWith("!")).map((p) => p.slice(1));
|
|
@@ -7269,7 +7431,7 @@ async function buildTree(dirPath, maxDepth, currentDepth, isIgnored, relativeBas
|
|
|
7269
7431
|
const withTypes = [];
|
|
7270
7432
|
for (const name of rawEntries) {
|
|
7271
7433
|
try {
|
|
7272
|
-
const s = await stat(
|
|
7434
|
+
const s = await stat(join18(dirPath, name));
|
|
7273
7435
|
withTypes.push({ name, isDir: s.isDirectory() });
|
|
7274
7436
|
} catch {
|
|
7275
7437
|
withTypes.push({ name, isDir: false });
|
|
@@ -7289,7 +7451,7 @@ async function buildTree(dirPath, maxDepth, currentDepth, isIgnored, relativeBas
|
|
|
7289
7451
|
}
|
|
7290
7452
|
if (entry.isDir) {
|
|
7291
7453
|
const children = await buildTree(
|
|
7292
|
-
|
|
7454
|
+
join18(dirPath, entry.name),
|
|
7293
7455
|
maxDepth,
|
|
7294
7456
|
currentDepth + 1,
|
|
7295
7457
|
isIgnored,
|
|
@@ -7327,10 +7489,10 @@ async function buildListDirectoryTool(workingDirectory) {
|
|
|
7327
7489
|
const isIgnored = buildMatcher(patterns);
|
|
7328
7490
|
return tool4({
|
|
7329
7491
|
description: "List directory structure as a tree. Use this for an overview of the project layout. Start at the root (path='.') with depth 3, then increase depth or narrow path if needed. Do NOT call this on every subdirectory - use glob to find specific files instead. Returns cached result if the same path+depth was already requested.",
|
|
7330
|
-
inputSchema:
|
|
7331
|
-
path:
|
|
7332
|
-
depth:
|
|
7333
|
-
gitignore:
|
|
7492
|
+
inputSchema: z41.object({
|
|
7493
|
+
path: z41.string().default(".").describe("Directory path relative to project root. Defaults to root."),
|
|
7494
|
+
depth: z41.number().min(1).max(15).default(10).describe("Max depth to traverse (1-15). Default 10."),
|
|
7495
|
+
gitignore: z41.boolean().describe(
|
|
7334
7496
|
"Whether to respect the gitignore or to ignore it. true will respect it. false will ignore it. Default true"
|
|
7335
7497
|
).default(true)
|
|
7336
7498
|
}),
|
|
@@ -7342,7 +7504,7 @@ async function buildListDirectoryTool(workingDirectory) {
|
|
|
7342
7504
|
};
|
|
7343
7505
|
}
|
|
7344
7506
|
seen.add(cacheKey);
|
|
7345
|
-
const targetDir = input.path === "." ? workingDirectory :
|
|
7507
|
+
const targetDir = input.path === "." ? workingDirectory : join18(workingDirectory, input.path);
|
|
7346
7508
|
try {
|
|
7347
7509
|
const s = await stat(targetDir);
|
|
7348
7510
|
if (!s.isDirectory()) {
|
|
@@ -7378,10 +7540,10 @@ var init_list_directory = __esm({
|
|
|
7378
7540
|
});
|
|
7379
7541
|
|
|
7380
7542
|
// src/tools/read-file.ts
|
|
7381
|
-
import { readFile as
|
|
7543
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
7382
7544
|
import { relative as relative4, resolve as resolve2 } from "path";
|
|
7383
7545
|
import { tool as tool5 } from "ai";
|
|
7384
|
-
import { z as
|
|
7546
|
+
import { z as z42 } from "zod";
|
|
7385
7547
|
function resolveSandboxedPath(workingDirectory, filePath) {
|
|
7386
7548
|
const absolutePath = resolve2(workingDirectory, filePath);
|
|
7387
7549
|
const relativePath = relative4(workingDirectory, absolutePath);
|
|
@@ -7406,7 +7568,7 @@ async function executeReadFile(workingDirectory, filePath, offset, limit) {
|
|
|
7406
7568
|
const resolved = resolveSandboxedPath(workingDirectory, filePath);
|
|
7407
7569
|
if ("error" in resolved) return resolved;
|
|
7408
7570
|
try {
|
|
7409
|
-
const content = await
|
|
7571
|
+
const content = await readFile10(resolved.absolutePath, "utf-8");
|
|
7410
7572
|
const sliced = sliceLines(content, offset ?? 0, limit ?? MAX_LINES);
|
|
7411
7573
|
if (sliced.numbered.length > MAX_OUTPUT_BYTES2) {
|
|
7412
7574
|
sliced.numbered = sliced.numbered.slice(0, MAX_OUTPUT_BYTES2) + `
|
|
@@ -7439,10 +7601,10 @@ var init_read_file = __esm({
|
|
|
7439
7601
|
init_esm_shims();
|
|
7440
7602
|
MAX_LINES = 2e3;
|
|
7441
7603
|
MAX_OUTPUT_BYTES2 = 256 * 1024;
|
|
7442
|
-
inputSchema4 =
|
|
7443
|
-
filePath:
|
|
7444
|
-
offset:
|
|
7445
|
-
limit:
|
|
7604
|
+
inputSchema4 = z42.object({
|
|
7605
|
+
filePath: z42.string().describe("Path to the file (absolute or relative to working directory)"),
|
|
7606
|
+
offset: z42.number().int().min(0).optional().describe("Line number to start reading from (0-based)"),
|
|
7607
|
+
limit: z42.number().int().min(1).optional().describe("Maximum number of lines to read")
|
|
7446
7608
|
});
|
|
7447
7609
|
}
|
|
7448
7610
|
});
|
|
@@ -7465,10 +7627,10 @@ var init_pick_string = __esm({
|
|
|
7465
7627
|
|
|
7466
7628
|
// src/tools/subagent.ts
|
|
7467
7629
|
import { ToolLoopAgent as ToolLoopAgent2, hasToolCall as hasToolCall2, stepCountIs as stepCountIs2, tool as tool6 } from "ai";
|
|
7468
|
-
import { z as
|
|
7630
|
+
import { z as z43 } from "zod";
|
|
7469
7631
|
function buildSubagentTools(workingDirectory, onFileRead) {
|
|
7470
7632
|
const baseReadFile = buildReadFileTool(workingDirectory);
|
|
7471
|
-
const
|
|
7633
|
+
const readFile26 = onFileRead ? tool6({
|
|
7472
7634
|
description: baseReadFile.description,
|
|
7473
7635
|
inputSchema: baseReadFile.inputSchema,
|
|
7474
7636
|
execute: async (input, options) => {
|
|
@@ -7481,7 +7643,7 @@ function buildSubagentTools(workingDirectory, onFileRead) {
|
|
|
7481
7643
|
bash: buildBashTool(workingDirectory),
|
|
7482
7644
|
glob: buildGlobTool(workingDirectory),
|
|
7483
7645
|
grep: buildGrepTool(workingDirectory),
|
|
7484
|
-
read_file:
|
|
7646
|
+
read_file: readFile26
|
|
7485
7647
|
};
|
|
7486
7648
|
}
|
|
7487
7649
|
function buildSubagentTool(model, workingDirectory, onHeartbeat, onFileRead) {
|
|
@@ -7538,11 +7700,11 @@ var init_subagent = __esm({
|
|
|
7538
7700
|
init_glob();
|
|
7539
7701
|
init_grep();
|
|
7540
7702
|
init_read_file();
|
|
7541
|
-
inputSchema5 =
|
|
7542
|
-
instruction:
|
|
7703
|
+
inputSchema5 = z43.object({
|
|
7704
|
+
instruction: z43.string().describe("Focused task for the subagent. Be specific about files and patterns to investigate.")
|
|
7543
7705
|
});
|
|
7544
|
-
resultSchema =
|
|
7545
|
-
findings:
|
|
7706
|
+
resultSchema = z43.object({
|
|
7707
|
+
findings: z43.string().describe("Summary of what was found")
|
|
7546
7708
|
});
|
|
7547
7709
|
SYSTEM_PROMPT = `You are a code research assistant. You have tools to explore a codebase: bash (shell commands, mainly git), glob (find files), grep (search content), and read_file (read files).
|
|
7548
7710
|
|
|
@@ -7553,10 +7715,10 @@ Be thorough but focused - only investigate what's relevant to your instruction.`
|
|
|
7553
7715
|
});
|
|
7554
7716
|
|
|
7555
7717
|
// src/tools/write-file.ts
|
|
7556
|
-
import { writeFile as
|
|
7718
|
+
import { writeFile as writeFile6, mkdir as mkdir3 } from "fs/promises";
|
|
7557
7719
|
import { dirname as dirname2, relative as relative5, resolve as resolve3 } from "path";
|
|
7558
7720
|
import { tool as tool7 } from "ai";
|
|
7559
|
-
import { z as
|
|
7721
|
+
import { z as z44 } from "zod";
|
|
7560
7722
|
async function executeWriteFile(outputDirectory, filePath, content) {
|
|
7561
7723
|
const cleaned = filePath.replace(/^autonoma\//, "");
|
|
7562
7724
|
const absolutePath = resolve3(outputDirectory, cleaned);
|
|
@@ -7565,8 +7727,8 @@ async function executeWriteFile(outputDirectory, filePath, content) {
|
|
|
7565
7727
|
return { error: "Cannot write files outside the output directory" };
|
|
7566
7728
|
}
|
|
7567
7729
|
try {
|
|
7568
|
-
await
|
|
7569
|
-
await
|
|
7730
|
+
await mkdir3(dirname2(absolutePath), { recursive: true });
|
|
7731
|
+
await writeFile6(absolutePath, content, "utf-8");
|
|
7570
7732
|
return { path: relativePath, bytesWritten: content.length };
|
|
7571
7733
|
} catch (err) {
|
|
7572
7734
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -7585,21 +7747,21 @@ var init_write_file = __esm({
|
|
|
7585
7747
|
"src/tools/write-file.ts"() {
|
|
7586
7748
|
"use strict";
|
|
7587
7749
|
init_esm_shims();
|
|
7588
|
-
inputSchema6 =
|
|
7589
|
-
filePath:
|
|
7590
|
-
content:
|
|
7750
|
+
inputSchema6 = z44.object({
|
|
7751
|
+
filePath: z44.string().describe("Path to write (absolute or relative to output directory)"),
|
|
7752
|
+
content: z44.string().describe("File content to write")
|
|
7591
7753
|
});
|
|
7592
7754
|
}
|
|
7593
7755
|
});
|
|
7594
7756
|
|
|
7595
7757
|
// src/tools/ask-user.ts
|
|
7596
7758
|
import { tool as tool8 } from "ai";
|
|
7597
|
-
import { z as
|
|
7759
|
+
import { z as z45 } from "zod";
|
|
7598
7760
|
function buildAskUserTool() {
|
|
7599
7761
|
return tool8({
|
|
7600
7762
|
description: "Ask the user a question ONLY when the answer is truly unknowable from the codebase. Valid reasons: untyped JSON/JSONB field schemas, business rules not in code, config values not in source. NEVER ask about: field names (read the schema), field types (read the ORM model), enum values (read the code), relationships (read foreign keys), numeric values (read the seed data or defaults). If you can find it by reading a file, DO NOT ask - read the file instead.",
|
|
7601
|
-
inputSchema:
|
|
7602
|
-
question:
|
|
7763
|
+
inputSchema: z45.object({
|
|
7764
|
+
question: z45.string().describe(
|
|
7603
7765
|
"A clear, plain-language question. State exactly what you need to know and why you can't find it in code. BAD: 'What are the decimal values for checking_balance?' - GOOD: 'Your Account model has a metadata JSON column with no type definition. What fields go inside it?'"
|
|
7604
7766
|
)
|
|
7605
7767
|
}),
|
|
@@ -7659,7 +7821,7 @@ var init_tools = __esm({
|
|
|
7659
7821
|
|
|
7660
7822
|
// src/agents/00-project-mapper/coverage.ts
|
|
7661
7823
|
import { readdir as readdir2 } from "fs/promises";
|
|
7662
|
-
import { join as
|
|
7824
|
+
import { join as join19 } from "path";
|
|
7663
7825
|
async function findUncoveredDirs(projectRoot, map) {
|
|
7664
7826
|
const entries = [...map.frontends, ...map.backends, ...map.ignore].map(
|
|
7665
7827
|
(e) => e.path.replace(/^\.\//, "").replace(/\/+$/, "")
|
|
@@ -7683,7 +7845,7 @@ async function findUncoveredDirs(projectRoot, map) {
|
|
|
7683
7845
|
const childRel = rel === "" ? child.name : `${rel}/${child.name}`;
|
|
7684
7846
|
if (underEntry(childRel)) continue;
|
|
7685
7847
|
if (hasEntryWithin(childRel)) {
|
|
7686
|
-
if (depth + 1 < MAX_DEPTH) await walk(
|
|
7848
|
+
if (depth + 1 < MAX_DEPTH) await walk(join19(abs, child.name), childRel, depth + 1);
|
|
7687
7849
|
} else {
|
|
7688
7850
|
out.push(childRel);
|
|
7689
7851
|
}
|
|
@@ -7710,7 +7872,7 @@ __export(project_mapper_exports, {
|
|
|
7710
7872
|
runProjectMapper: () => runProjectMapper
|
|
7711
7873
|
});
|
|
7712
7874
|
import { tool as tool9 } from "ai";
|
|
7713
|
-
import { z as
|
|
7875
|
+
import { z as z46 } from "zod";
|
|
7714
7876
|
async function runProjectMapper(input) {
|
|
7715
7877
|
const model = getModel(input.modelId);
|
|
7716
7878
|
const { logger, onStepFinish } = buildDefaultStepLogger("project-map", MAX_STEPS);
|
|
@@ -7744,7 +7906,7 @@ First build the full inventory (workspace/package manifests + top-level director
|
|
|
7744
7906
|
}),
|
|
7745
7907
|
finish: tool9({
|
|
7746
7908
|
description: "End this step. Call once set_project_map has recorded a complete map.",
|
|
7747
|
-
inputSchema:
|
|
7909
|
+
inputSchema: z46.object({}),
|
|
7748
7910
|
execute: () => {
|
|
7749
7911
|
if (captured == null) {
|
|
7750
7912
|
return { error: "Cannot finish: no map recorded - call set_project_map first." };
|
|
@@ -7788,7 +7950,7 @@ __export(pages_finder_exports, {
|
|
|
7788
7950
|
import { existsSync } from "fs";
|
|
7789
7951
|
import * as path2 from "path";
|
|
7790
7952
|
import { tool as tool10 } from "ai";
|
|
7791
|
-
import { z as
|
|
7953
|
+
import { z as z47 } from "zod";
|
|
7792
7954
|
async function runPageFinder(input) {
|
|
7793
7955
|
const model = getModel(input.modelId);
|
|
7794
7956
|
const pageCollector = new PageCollector();
|
|
@@ -7823,12 +7985,12 @@ ${input.extraMessage}`;
|
|
|
7823
7985
|
}),
|
|
7824
7986
|
view_pages: tool10({
|
|
7825
7987
|
description: "use this tool to view all the pages that you already added",
|
|
7826
|
-
inputSchema:
|
|
7988
|
+
inputSchema: z47.object(),
|
|
7827
7989
|
execute: () => pageCollector.viewPages()
|
|
7828
7990
|
}),
|
|
7829
7991
|
finish: tool10({
|
|
7830
7992
|
description: "End this step. Call once every page in the codebase has been added via add_page.",
|
|
7831
|
-
inputSchema:
|
|
7993
|
+
inputSchema: z47.object({}),
|
|
7832
7994
|
execute: () => {
|
|
7833
7995
|
if (pageCollector.pages.size === 0) {
|
|
7834
7996
|
return {
|
|
@@ -7855,10 +8017,10 @@ var init_pages_finder = __esm({
|
|
|
7855
8017
|
init_agent();
|
|
7856
8018
|
init_model();
|
|
7857
8019
|
init_tools();
|
|
7858
|
-
Page =
|
|
7859
|
-
route:
|
|
7860
|
-
path:
|
|
7861
|
-
description:
|
|
8020
|
+
Page = z47.object({
|
|
8021
|
+
route: z47.string().min(1),
|
|
8022
|
+
path: z47.string().min(1),
|
|
8023
|
+
description: z47.string().min(10)
|
|
7862
8024
|
});
|
|
7863
8025
|
PageCollector = class {
|
|
7864
8026
|
// the key is the path
|
|
@@ -7887,14 +8049,20 @@ var init_pages_finder = __esm({
|
|
|
7887
8049
|
});
|
|
7888
8050
|
|
|
7889
8051
|
// src/core/progress.ts
|
|
7890
|
-
function
|
|
8052
|
+
function activeAgentNow() {
|
|
8053
|
+
const store = getActiveStore();
|
|
8054
|
+
if (store == null) return void 0;
|
|
8055
|
+
return agentNow(store.getState());
|
|
8056
|
+
}
|
|
8057
|
+
function reportSubProgress(step, done, total, unit, note2, startedAt) {
|
|
7891
8058
|
if (total <= 0) return;
|
|
7892
|
-
getActiveStore()?.setSubProgress(step, { done: Math.min(done, total), total, unit, note: note2 });
|
|
8059
|
+
getActiveStore()?.setSubProgress(step, { done: Math.min(done, total), total, unit, note: note2, startedAt });
|
|
7893
8060
|
}
|
|
7894
8061
|
var init_progress = __esm({
|
|
7895
8062
|
"src/core/progress.ts"() {
|
|
7896
8063
|
"use strict";
|
|
7897
8064
|
init_esm_shims();
|
|
8065
|
+
init_eta();
|
|
7898
8066
|
init_store();
|
|
7899
8067
|
}
|
|
7900
8068
|
});
|
|
@@ -8048,15 +8216,15 @@ var kb_generator_exports = {};
|
|
|
8048
8216
|
__export(kb_generator_exports, {
|
|
8049
8217
|
runKBGenerator: () => runKBGenerator
|
|
8050
8218
|
});
|
|
8051
|
-
import { readFile as
|
|
8052
|
-
import { join as
|
|
8219
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
8220
|
+
import { join as join20, resolve as resolve5 } from "path";
|
|
8053
8221
|
import { tool as tool11 } from "ai";
|
|
8054
|
-
import { z as
|
|
8222
|
+
import { z as z48 } from "zod";
|
|
8055
8223
|
function buildRegisterPagesTool(tracker) {
|
|
8056
8224
|
return tool11({
|
|
8057
8225
|
description: "Register ALL page/route files discovered via glob. Call this ONCE after globbing for page files. The system will track which ones you've read and block finish until all are covered.",
|
|
8058
|
-
inputSchema:
|
|
8059
|
-
pages:
|
|
8226
|
+
inputSchema: z48.object({
|
|
8227
|
+
pages: z48.array(z48.string()).describe("All page file paths found by glob")
|
|
8060
8228
|
}),
|
|
8061
8229
|
execute: async (input) => {
|
|
8062
8230
|
tracker.register(input.pages);
|
|
@@ -8070,7 +8238,7 @@ function buildRegisterPagesTool(tracker) {
|
|
|
8070
8238
|
function buildPageCoverageTool(tracker) {
|
|
8071
8239
|
return tool11({
|
|
8072
8240
|
description: "Check how many registered pages you've read vs how many remain.",
|
|
8073
|
-
inputSchema:
|
|
8241
|
+
inputSchema: z48.object({}),
|
|
8074
8242
|
execute: async () => tracker.coverage()
|
|
8075
8243
|
});
|
|
8076
8244
|
}
|
|
@@ -8081,9 +8249,9 @@ function requiredReads(total) {
|
|
|
8081
8249
|
function buildFinishTool(tracker, onFinish) {
|
|
8082
8250
|
return tool11({
|
|
8083
8251
|
description: "Call when you have finished generating the knowledge base. BLOCKED until you have read enough of the registered routes (every route on a small app; a strong majority on a large one) - call page_coverage first to check how many remain.",
|
|
8084
|
-
inputSchema:
|
|
8085
|
-
summary:
|
|
8086
|
-
artifacts:
|
|
8252
|
+
inputSchema: z48.object({
|
|
8253
|
+
summary: z48.string().describe("Summary of what was generated"),
|
|
8254
|
+
artifacts: z48.array(z48.string()).describe("List of files written")
|
|
8087
8255
|
}),
|
|
8088
8256
|
execute: async (input) => {
|
|
8089
8257
|
const cov = tracker.coverage();
|
|
@@ -8162,8 +8330,8 @@ Output files:
|
|
|
8162
8330
|
const agentConfig = buildKbAgentConfig(tracker, model, input, onStepFinish, setResult);
|
|
8163
8331
|
await runAgent(agentConfig, prompt, () => result);
|
|
8164
8332
|
logger.summary();
|
|
8165
|
-
const autonomaPath =
|
|
8166
|
-
const autonomaExists = await
|
|
8333
|
+
const autonomaPath = join20(input.outputDir, "AUTONOMA.md");
|
|
8334
|
+
const autonomaExists = await readFile11(autonomaPath, "utf-8").then(() => true).catch((err) => {
|
|
8167
8335
|
debugLog("AUTONOMA.md not found while checking step completion", { err });
|
|
8168
8336
|
return false;
|
|
8169
8337
|
});
|
|
@@ -8349,17 +8517,17 @@ var entity_audit_exports = {};
|
|
|
8349
8517
|
__export(entity_audit_exports, {
|
|
8350
8518
|
runEntityAudit: () => runEntityAudit
|
|
8351
8519
|
});
|
|
8352
|
-
import { readFile as
|
|
8353
|
-
import { join as
|
|
8520
|
+
import { readFile as readFile12, writeFile as writeFile7 } from "fs/promises";
|
|
8521
|
+
import { join as join21 } from "path";
|
|
8354
8522
|
import { tool as tool12 } from "ai";
|
|
8355
8523
|
import { glob as glob4 } from "glob";
|
|
8356
|
-
import { z as
|
|
8524
|
+
import { z as z49 } from "zod";
|
|
8357
8525
|
function buildRegisterModelsTool(tracker) {
|
|
8358
8526
|
return tool12({
|
|
8359
8527
|
description: "Register ALL database models discovered via grep. Call this ONCE after grepping for model definitions. After registering, use next_model to process them one at a time.",
|
|
8360
|
-
inputSchema:
|
|
8361
|
-
models:
|
|
8362
|
-
framework:
|
|
8528
|
+
inputSchema: z49.object({
|
|
8529
|
+
models: z49.array(z49.string()).describe("All model/table names found by grep"),
|
|
8530
|
+
framework: z49.string().describe("Database framework detected (e.g. 'sqlalchemy', 'prisma', 'drizzle')")
|
|
8363
8531
|
}),
|
|
8364
8532
|
execute: async (input) => {
|
|
8365
8533
|
tracker.register(input.models);
|
|
@@ -8375,7 +8543,7 @@ function buildRegisterModelsTool(tracker) {
|
|
|
8375
8543
|
function buildNextModelTool(tracker) {
|
|
8376
8544
|
return tool12({
|
|
8377
8545
|
description: "Get the next model to audit from the queue. If you called next_model before without calling mark_model_audited, the previous model is auto-skipped (marked as no creation path found). Returns done:true when all models are processed.",
|
|
8378
|
-
inputSchema:
|
|
8546
|
+
inputSchema: z49.object({}),
|
|
8379
8547
|
execute: async () => {
|
|
8380
8548
|
const next = tracker.nextModel();
|
|
8381
8549
|
if (!next) {
|
|
@@ -8395,17 +8563,17 @@ function buildNextModelTool(tracker) {
|
|
|
8395
8563
|
function buildMarkModelAuditedTool(tracker) {
|
|
8396
8564
|
return tool12({
|
|
8397
8565
|
description: "Mark a model as audited after you have determined its creation paths. Call this for EACH model after reading its creation code and determining independently_created + created_by. Include creation_function (e.g. 'UserService.create'), side_effects (list of things the creation does beyond the model itself), and for each created_by entry include owner, via (function name), and why (one sentence explaining the relationship).",
|
|
8398
|
-
inputSchema:
|
|
8399
|
-
model:
|
|
8400
|
-
independently_created:
|
|
8401
|
-
creation_file:
|
|
8402
|
-
creation_function:
|
|
8403
|
-
side_effects:
|
|
8404
|
-
created_by:
|
|
8405
|
-
|
|
8406
|
-
owner:
|
|
8407
|
-
via:
|
|
8408
|
-
why:
|
|
8566
|
+
inputSchema: z49.object({
|
|
8567
|
+
model: z49.string().describe("Model name"),
|
|
8568
|
+
independently_created: z49.boolean(),
|
|
8569
|
+
creation_file: z49.string().optional().describe("File containing the creation function"),
|
|
8570
|
+
creation_function: z49.string().optional().describe("Function/method name (e.g. 'UserService.create' or 'create_user')"),
|
|
8571
|
+
side_effects: z49.array(z49.string()).optional().describe("Side effects of creation (e.g. 'creates default Settings row', 'hashes password')"),
|
|
8572
|
+
created_by: z49.array(
|
|
8573
|
+
z49.object({
|
|
8574
|
+
owner: z49.string().describe("Owner model name"),
|
|
8575
|
+
via: z49.string().optional().describe("Function that creates this model (e.g. 'OrganizationService.create')"),
|
|
8576
|
+
why: z49.string().optional().describe("One sentence explaining why this model is created as a side effect")
|
|
8409
8577
|
})
|
|
8410
8578
|
).describe("List of owner models that create this as a side effect, empty array if none")
|
|
8411
8579
|
}),
|
|
@@ -8444,16 +8612,16 @@ function buildMarkModelAuditedTool(tracker) {
|
|
|
8444
8612
|
function buildModelCoverageTool(tracker) {
|
|
8445
8613
|
return tool12({
|
|
8446
8614
|
description: "Check how many registered models you've audited vs how many remain.",
|
|
8447
|
-
inputSchema:
|
|
8615
|
+
inputSchema: z49.object({}),
|
|
8448
8616
|
execute: async () => tracker.coverage()
|
|
8449
8617
|
});
|
|
8450
8618
|
}
|
|
8451
8619
|
function buildFinishTool2(tracker, onFinish) {
|
|
8452
8620
|
return tool12({
|
|
8453
8621
|
description: "Call when entity audit is complete. BLOCKED if there are unaudited models - call model_coverage first to check.",
|
|
8454
|
-
inputSchema:
|
|
8455
|
-
summary:
|
|
8456
|
-
artifacts:
|
|
8622
|
+
inputSchema: z49.object({
|
|
8623
|
+
summary: z49.string().describe("Summary of the audit"),
|
|
8624
|
+
artifacts: z49.array(z49.string()).describe("Files written")
|
|
8457
8625
|
}),
|
|
8458
8626
|
execute: async (input) => {
|
|
8459
8627
|
const cov = tracker.coverage();
|
|
@@ -8481,7 +8649,7 @@ async function findPrismaSchema(projectRoot) {
|
|
|
8481
8649
|
return candidates[0] ?? void 0;
|
|
8482
8650
|
}
|
|
8483
8651
|
async function extractPrismaModels(schemaPath) {
|
|
8484
|
-
const content = await
|
|
8652
|
+
const content = await readFile12(schemaPath, "utf-8");
|
|
8485
8653
|
return content.split("\n").filter((line) => line.startsWith("model ")).map((line) => line.split(/\s+/)[1]).filter((name) => name != null);
|
|
8486
8654
|
}
|
|
8487
8655
|
async function detectFrameworkAndModels(projectRoot) {
|
|
@@ -8568,8 +8736,8 @@ ${formatException(err)}`);
|
|
|
8568
8736
|
logger.summary();
|
|
8569
8737
|
const writeCanonicalAudit = async () => {
|
|
8570
8738
|
if (tracker.auditedModels.size === 0) return void 0;
|
|
8571
|
-
const auditPath =
|
|
8572
|
-
await
|
|
8739
|
+
const auditPath = join21(input.outputDir, "entity-audit.md");
|
|
8740
|
+
await writeFile7(auditPath, tracker.generateAuditMarkdown(), "utf-8");
|
|
8573
8741
|
return auditPath;
|
|
8574
8742
|
};
|
|
8575
8743
|
const canonicalPath = await writeCanonicalAudit();
|
|
@@ -8583,9 +8751,9 @@ ${formatException(err)}`);
|
|
|
8583
8751
|
}
|
|
8584
8752
|
const reviewed = result;
|
|
8585
8753
|
if (!reviewed) {
|
|
8586
|
-
const auditPath =
|
|
8754
|
+
const auditPath = join21(input.outputDir, "entity-audit.md");
|
|
8587
8755
|
try {
|
|
8588
|
-
await
|
|
8756
|
+
await readFile12(auditPath, "utf-8");
|
|
8589
8757
|
return {
|
|
8590
8758
|
success: true,
|
|
8591
8759
|
artifacts: ["entity-audit.md"],
|
|
@@ -8795,8 +8963,8 @@ on a unique constraint. Everything else stays concrete and static, and no other
|
|
|
8795
8963
|
});
|
|
8796
8964
|
|
|
8797
8965
|
// src/agents/03-scenario-recipe/scenario-table.ts
|
|
8798
|
-
import { readFile as
|
|
8799
|
-
import { join as
|
|
8966
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
8967
|
+
import { join as join22 } from "path";
|
|
8800
8968
|
import matter2 from "gray-matter";
|
|
8801
8969
|
function validateScenarioIsConcrete(content) {
|
|
8802
8970
|
const errors = findUnknownRecipeTokens(content).map(
|
|
@@ -8831,22 +8999,22 @@ __export(scenario_recipe_exports, {
|
|
|
8831
8999
|
feedbackToScenario: () => feedbackToScenario,
|
|
8832
9000
|
runScenarioRecipe: () => runScenarioRecipe
|
|
8833
9001
|
});
|
|
8834
|
-
import { readFile as
|
|
8835
|
-
import { join as
|
|
9002
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
9003
|
+
import { join as join23 } from "path";
|
|
8836
9004
|
import { tool as tool13 } from "ai";
|
|
8837
|
-
import { z as
|
|
9005
|
+
import { z as z50 } from "zod";
|
|
8838
9006
|
function buildFinishTool3(requiredEntities, outputDir, onFinish) {
|
|
8839
9007
|
return tool13({
|
|
8840
9008
|
description: "Call when scenario design is complete and scenarios.md is written. BLOCKED if any required entities are missing from the scenario.",
|
|
8841
|
-
inputSchema:
|
|
8842
|
-
summary:
|
|
8843
|
-
entityCount:
|
|
8844
|
-
artifacts:
|
|
9009
|
+
inputSchema: z50.object({
|
|
9010
|
+
summary: z50.string().describe("Summary of the scenario"),
|
|
9011
|
+
entityCount: z50.number().describe("Number of entity types in the scenario"),
|
|
9012
|
+
artifacts: z50.array(z50.string()).describe("Files written")
|
|
8845
9013
|
}),
|
|
8846
9014
|
execute: async (input) => {
|
|
8847
9015
|
let content;
|
|
8848
9016
|
try {
|
|
8849
|
-
content = await
|
|
9017
|
+
content = await readFile14(join23(outputDir, "scenarios.md"), "utf-8");
|
|
8850
9018
|
} catch {
|
|
8851
9019
|
return { error: "Cannot finish: scenarios.md not found. Write it first." };
|
|
8852
9020
|
}
|
|
@@ -8925,9 +9093,9 @@ When done, call finish.`;
|
|
|
8925
9093
|
logger.summary();
|
|
8926
9094
|
const reviewed = result;
|
|
8927
9095
|
if (!reviewed) {
|
|
8928
|
-
const scenariosPath =
|
|
9096
|
+
const scenariosPath = join23(input.outputDir, "scenarios.md");
|
|
8929
9097
|
try {
|
|
8930
|
-
await
|
|
9098
|
+
await readFile14(scenariosPath, "utf-8");
|
|
8931
9099
|
return {
|
|
8932
9100
|
success: true,
|
|
8933
9101
|
artifacts: ["scenarios.md"],
|
|
@@ -8986,12 +9154,12 @@ var init_scenario_recipe = __esm({
|
|
|
8986
9154
|
});
|
|
8987
9155
|
|
|
8988
9156
|
// src/agents/04-recipe-builder/entity-order.ts
|
|
8989
|
-
import { readFile as
|
|
8990
|
-
import { join as
|
|
9157
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
9158
|
+
import { join as join24 } from "path";
|
|
8991
9159
|
import matter3 from "gray-matter";
|
|
8992
|
-
import { z as
|
|
9160
|
+
import { z as z51 } from "zod";
|
|
8993
9161
|
async function parseEntityAudit(outputDir) {
|
|
8994
|
-
const raw = await
|
|
9162
|
+
const raw = await readFile15(join24(outputDir, "entity-audit.md"), "utf-8");
|
|
8995
9163
|
try {
|
|
8996
9164
|
const parsed = frontmatterSchema.safeParse(matter3(raw).data);
|
|
8997
9165
|
if (parsed.success && parsed.data.models.length > 0) {
|
|
@@ -9152,34 +9320,34 @@ var init_entity_order = __esm({
|
|
|
9152
9320
|
"src/agents/04-recipe-builder/entity-order.ts"() {
|
|
9153
9321
|
"use strict";
|
|
9154
9322
|
init_esm_shims();
|
|
9155
|
-
createdBySchema =
|
|
9156
|
-
owner:
|
|
9157
|
-
via:
|
|
9158
|
-
why:
|
|
9323
|
+
createdBySchema = z51.object({
|
|
9324
|
+
owner: z51.string(),
|
|
9325
|
+
via: z51.string().optional(),
|
|
9326
|
+
why: z51.string().optional()
|
|
9159
9327
|
});
|
|
9160
|
-
auditedModelSchema =
|
|
9161
|
-
name:
|
|
9162
|
-
independently_created:
|
|
9163
|
-
creation_file:
|
|
9164
|
-
creation_function:
|
|
9165
|
-
side_effects:
|
|
9328
|
+
auditedModelSchema = z51.object({
|
|
9329
|
+
name: z51.string(),
|
|
9330
|
+
independently_created: z51.coerce.boolean().default(false),
|
|
9331
|
+
creation_file: z51.string().optional(),
|
|
9332
|
+
creation_function: z51.string().optional(),
|
|
9333
|
+
side_effects: z51.array(z51.string()).optional(),
|
|
9166
9334
|
// Tolerate a stray `created_by:` with no entries (parsed as null by YAML).
|
|
9167
|
-
created_by:
|
|
9335
|
+
created_by: z51.array(createdBySchema).nullish().transform((v) => v ?? [])
|
|
9168
9336
|
});
|
|
9169
|
-
frontmatterSchema =
|
|
9170
|
-
models:
|
|
9337
|
+
frontmatterSchema = z51.object({
|
|
9338
|
+
models: z51.array(auditedModelSchema).nullish().transform((v) => v ?? [])
|
|
9171
9339
|
});
|
|
9172
9340
|
}
|
|
9173
9341
|
});
|
|
9174
9342
|
|
|
9175
9343
|
// src/agents/04-recipe-builder/completion.ts
|
|
9176
|
-
import { readFile as
|
|
9177
|
-
import { join as
|
|
9178
|
-
import { z as
|
|
9344
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
9345
|
+
import { join as join25 } from "path";
|
|
9346
|
+
import { z as z52 } from "zod";
|
|
9179
9347
|
async function readCompletion(outputDir) {
|
|
9180
9348
|
let raw;
|
|
9181
9349
|
try {
|
|
9182
|
-
raw = await
|
|
9350
|
+
raw = await readFile16(join25(outputDir, COMPLETION_MARKER_FILE), "utf-8");
|
|
9183
9351
|
} catch (err) {
|
|
9184
9352
|
debugLog("No completion marker yet", { err });
|
|
9185
9353
|
return false;
|
|
@@ -9205,7 +9373,7 @@ var init_completion = __esm({
|
|
|
9205
9373
|
init_esm_shims();
|
|
9206
9374
|
init_debug();
|
|
9207
9375
|
COMPLETION_MARKER_FILE = ".sdk-integration-complete";
|
|
9208
|
-
completionSchema =
|
|
9376
|
+
completionSchema = z52.object({ complete: z52.literal(true) });
|
|
9209
9377
|
}
|
|
9210
9378
|
});
|
|
9211
9379
|
|
|
@@ -9253,8 +9421,8 @@ var init_completion_watch = __esm({
|
|
|
9253
9421
|
});
|
|
9254
9422
|
|
|
9255
9423
|
// src/agents/04-recipe-builder/integration-prompt.ts
|
|
9256
|
-
import { writeFile as
|
|
9257
|
-
import { join as
|
|
9424
|
+
import { writeFile as writeFile8 } from "fs/promises";
|
|
9425
|
+
import { join as join26 } from "path";
|
|
9258
9426
|
function renderIntegrationPrompt(params) {
|
|
9259
9427
|
const priorFailureSection = params.priorFailure != null ? `
|
|
9260
9428
|
\u2550\u2550\u2550 A PRIOR SESSION DID NOT COMPLETE - READ THIS FIRST \u2550\u2550\u2550
|
|
@@ -9543,7 +9711,7 @@ Write the completion marker once ALL of these hold:
|
|
|
9543
9711
|
A step you documented as genuinely blocked NEVER justifies withholding the marker; a
|
|
9544
9712
|
checklist item you simply have not finished always does. The marker is how the CLI knows
|
|
9545
9713
|
the session is done and can upload the recipe:
|
|
9546
|
-
${
|
|
9714
|
+
${join26(params.outputDir, COMPLETION_MARKER_FILE)}
|
|
9547
9715
|
Its contents MUST be exactly:
|
|
9548
9716
|
{ "complete": true }
|
|
9549
9717
|
Writing it is not optional: the planner watches for this marker and takes the terminal
|
|
@@ -9555,8 +9723,8 @@ or the commit you left behind and what blocked the push - and then says:
|
|
|
9555
9723
|
Nothing after that message - no further questions, summaries, or work.`;
|
|
9556
9724
|
}
|
|
9557
9725
|
async function writeIntegrationPrompt(params) {
|
|
9558
|
-
const path3 =
|
|
9559
|
-
await
|
|
9726
|
+
const path3 = join26(params.outputDir, INTEGRATION_PROMPT_FILE);
|
|
9727
|
+
await writeFile8(path3, renderIntegrationPrompt(params), "utf-8");
|
|
9560
9728
|
return path3;
|
|
9561
9729
|
}
|
|
9562
9730
|
var INTEGRATION_PROMPT_VERSION, INTEGRATION_PROMPT_FILE, DEFAULT_ENDPOINT_PATH, INTEGRATION_BRANCH;
|
|
@@ -9574,8 +9742,8 @@ var init_integration_prompt = __esm({
|
|
|
9574
9742
|
});
|
|
9575
9743
|
|
|
9576
9744
|
// src/agents/04-recipe-builder/state.ts
|
|
9577
|
-
import { readFile as
|
|
9578
|
-
import { join as
|
|
9745
|
+
import { readFile as readFile17, writeFile as writeFile9 } from "fs/promises";
|
|
9746
|
+
import { join as join27 } from "path";
|
|
9579
9747
|
function initialRecipeState() {
|
|
9580
9748
|
return {
|
|
9581
9749
|
phase: "tech-detect",
|
|
@@ -9585,7 +9753,7 @@ function initialRecipeState() {
|
|
|
9585
9753
|
}
|
|
9586
9754
|
async function loadRecipeState(outputDir) {
|
|
9587
9755
|
try {
|
|
9588
|
-
const raw = await
|
|
9756
|
+
const raw = await readFile17(join27(outputDir, STATE_FILE2), "utf-8");
|
|
9589
9757
|
const parsed = JSON.parse(raw);
|
|
9590
9758
|
return parsed;
|
|
9591
9759
|
} catch {
|
|
@@ -9593,7 +9761,7 @@ async function loadRecipeState(outputDir) {
|
|
|
9593
9761
|
}
|
|
9594
9762
|
}
|
|
9595
9763
|
async function saveRecipeState(outputDir, state) {
|
|
9596
|
-
await
|
|
9764
|
+
await writeFile9(join27(outputDir, STATE_FILE2), JSON.stringify(state, null, 2), "utf-8");
|
|
9597
9765
|
}
|
|
9598
9766
|
var STATE_FILE2;
|
|
9599
9767
|
var init_state2 = __esm({
|
|
@@ -9606,22 +9774,19 @@ var init_state2 = __esm({
|
|
|
9606
9774
|
|
|
9607
9775
|
// src/agents/04-recipe-builder/phases/handoff.ts
|
|
9608
9776
|
import { rm as rm2 } from "fs/promises";
|
|
9609
|
-
import { join as
|
|
9777
|
+
import { join as join28 } from "path";
|
|
9610
9778
|
function launchMessage(promptFile) {
|
|
9611
9779
|
return `Read the file ${promptFile} and follow its instructions exactly to integrate Autonoma into this application. It is your complete spec. Do not stop until every item in it is done and you have written the completion marker it describes.`;
|
|
9612
9780
|
}
|
|
9613
9781
|
async function runHandoffPhase(state, deps, outputDir) {
|
|
9614
|
-
const recipePath = state.recipePath ??
|
|
9782
|
+
const recipePath = state.recipePath ?? join28(outputDir, RECIPE_FILE);
|
|
9615
9783
|
state.recipePath = recipePath;
|
|
9616
9784
|
const launcher = await selectLauncher(deps.launchers, state.agentId ?? deps.presetAgentId, deps.interactive);
|
|
9617
9785
|
if (launcher == null) {
|
|
9618
9786
|
if (!deps.interactive) {
|
|
9619
|
-
return {
|
|
9620
|
-
kind: "handback",
|
|
9621
|
-
summary: "--non-interactive SDK implementation requires an installed, supported coding agent, but none was resolved on your PATH (name one with --agent if more than one is present). Install one, or run interactively."
|
|
9622
|
-
};
|
|
9787
|
+
return { kind: "handback", summary: NO_AGENT_FOUND_HEADLESS };
|
|
9623
9788
|
}
|
|
9624
|
-
return manualFallback(outputDir, recipePath, deps.cliCommand,
|
|
9789
|
+
return manualFallback(outputDir, recipePath, deps.cliCommand, NO_AGENT_FOUND);
|
|
9625
9790
|
}
|
|
9626
9791
|
const permissionMode = deps.interactive ? await selectPermissionMode(state.permissionMode ?? deps.presetPermissionMode) : state.permissionMode ?? deps.presetPermissionMode ?? DEFAULT_PERMISSION_MODE;
|
|
9627
9792
|
state.agentId = launcher.id;
|
|
@@ -9640,7 +9805,7 @@ async function runHandoffPhase(state, deps, outputDir) {
|
|
|
9640
9805
|
return { kind: "advance" };
|
|
9641
9806
|
}
|
|
9642
9807
|
async function runCompletionPhase(state, deps, outputDir) {
|
|
9643
|
-
const recipePath = state.recipePath ??
|
|
9808
|
+
const recipePath = state.recipePath ?? join28(outputDir, RECIPE_FILE);
|
|
9644
9809
|
while (true) {
|
|
9645
9810
|
const [complete, read] = await Promise.all([readCompletion(outputDir), loadRecipe(outputDir)]);
|
|
9646
9811
|
const uploadProblems = read.status === "ok" ? findRecipeUploadProblems(read.recipe) : [];
|
|
@@ -9649,8 +9814,13 @@ async function runCompletionPhase(state, deps, outputDir) {
|
|
|
9649
9814
|
log.success("The agent reported the integration complete and validated.");
|
|
9650
9815
|
return { kind: "advance" };
|
|
9651
9816
|
}
|
|
9652
|
-
const failure = describeIncompleteRecipe(read, uploadProblems, recipePath, deps.cliCommand);
|
|
9653
9817
|
const launcher = await selectLauncher(deps.launchers, state.agentId ?? deps.presetAgentId, deps.interactive);
|
|
9818
|
+
const nothingEverRan = read.status === "absent" && launcher == null && (state.launchAttempts ?? 0) === 0;
|
|
9819
|
+
if (nothingEverRan) {
|
|
9820
|
+
if (!deps.interactive) return { kind: "handback", summary: NO_AGENT_FOUND_HEADLESS };
|
|
9821
|
+
return await manualFallback(outputDir, recipePath, deps.cliCommand, NO_AGENT_FOUND);
|
|
9822
|
+
}
|
|
9823
|
+
const failure = describeIncompleteRecipe(read, uploadProblems, recipePath, deps.cliCommand);
|
|
9654
9824
|
if (launcher != null && (state.launchAttempts ?? 0) < MAX_LAUNCH_ATTEMPTS) {
|
|
9655
9825
|
log.warn("The integration isn't complete yet. Launching the agent to finish it...");
|
|
9656
9826
|
state.agentId = launcher.id;
|
|
@@ -9674,7 +9844,7 @@ async function runCompletionPhase(state, deps, outputDir) {
|
|
|
9674
9844
|
function handbackSummary(failure, outputDir) {
|
|
9675
9845
|
return `${failure}
|
|
9676
9846
|
|
|
9677
|
-
Finish the integration (see the prompt at ${
|
|
9847
|
+
Finish the integration (see the prompt at ${join28(outputDir, INTEGRATION_PROMPT_FILE)}), then re-run with --resume.`;
|
|
9678
9848
|
}
|
|
9679
9849
|
async function launchAgent(launcher, permissionMode, target) {
|
|
9680
9850
|
const promptFile = await writeIntegrationPrompt({
|
|
@@ -9684,7 +9854,7 @@ async function launchAgent(launcher, permissionMode, target) {
|
|
|
9684
9854
|
priorFailure: target.priorFailure,
|
|
9685
9855
|
userGuidance: target.userGuidance
|
|
9686
9856
|
});
|
|
9687
|
-
await rm2(
|
|
9857
|
+
await rm2(join28(target.outputDir, COMPLETION_MARKER_FILE), { force: true }).catch((err) => {
|
|
9688
9858
|
debugLog("Could not clear a stale completion marker", { err });
|
|
9689
9859
|
});
|
|
9690
9860
|
log.info(`Launching ${launcher.label}. It will implement the integration - watch and steer it as it works.`);
|
|
@@ -9736,19 +9906,24 @@ ${recheck}`;
|
|
|
9736
9906
|
}
|
|
9737
9907
|
async function manualFallback(outputDir, recipePath, cliCommand, why) {
|
|
9738
9908
|
const promptFile = await writeIntegrationPrompt({ outputDir, recipePath, cliCommand });
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9909
|
+
await welcome({
|
|
9910
|
+
eyebrow: "NO CODING AGENT FOUND",
|
|
9911
|
+
title: "Finish this step in the coding agent you already use",
|
|
9912
|
+
lines: [
|
|
9913
|
+
why,
|
|
9914
|
+
"Any coding agent can do this step just as well - Cursor, Windsurf, Copilot, Zed, or whatever you have open. Point yours at this project and paste it this:",
|
|
9915
|
+
`Read ${promptFile} and follow its instructions exactly.`,
|
|
9916
|
+
"That file is the complete spec: the endpoint to implement, the factories to register, and the completion marker it must write when it is done.",
|
|
9917
|
+
`When your agent reports it finished, come back here and run: ${cliCommand} --resume`
|
|
9918
|
+
],
|
|
9919
|
+
cta: "Press enter once you have handed it over"
|
|
9920
|
+
});
|
|
9921
|
+
return {
|
|
9922
|
+
kind: "pause",
|
|
9923
|
+
summary: `${why} Integration handed off for manual implementation - resume when it's ready.`
|
|
9924
|
+
};
|
|
9750
9925
|
}
|
|
9751
|
-
var MAX_LAUNCH_ATTEMPTS, HANDOFF_COUNTDOWN_SECONDS;
|
|
9926
|
+
var MAX_LAUNCH_ATTEMPTS, HANDOFF_COUNTDOWN_SECONDS, NO_AGENT_FOUND, NO_AGENT_FOUND_HEADLESS;
|
|
9752
9927
|
var init_handoff = __esm({
|
|
9753
9928
|
"src/agents/04-recipe-builder/phases/handoff.ts"() {
|
|
9754
9929
|
"use strict";
|
|
@@ -9765,6 +9940,8 @@ var init_handoff = __esm({
|
|
|
9765
9940
|
init_state2();
|
|
9766
9941
|
MAX_LAUNCH_ATTEMPTS = 2;
|
|
9767
9942
|
HANDOFF_COUNTDOWN_SECONDS = 10;
|
|
9943
|
+
NO_AGENT_FOUND = "Autonoma can launch Claude Code or Codex for you, but neither is on your PATH.";
|
|
9944
|
+
NO_AGENT_FOUND_HEADLESS = "--non-interactive SDK implementation requires an installed, supported coding agent, but none was resolved on your PATH (name one with --agent if more than one is present). Install one, or run interactively.";
|
|
9768
9945
|
}
|
|
9769
9946
|
});
|
|
9770
9947
|
|
|
@@ -9875,17 +10052,17 @@ var init_recipe_builder = __esm({
|
|
|
9875
10052
|
});
|
|
9876
10053
|
|
|
9877
10054
|
// src/agents/00b-feature-discovery/index.ts
|
|
9878
|
-
import { readFile as
|
|
9879
|
-
import { join as
|
|
10055
|
+
import { readFile as readFile18, writeFile as writeFile10 } from "fs/promises";
|
|
10056
|
+
import { join as join29 } from "path";
|
|
9880
10057
|
import { tool as tool14 } from "ai";
|
|
9881
|
-
import { z as
|
|
10058
|
+
import { z as z53 } from "zod";
|
|
9882
10059
|
async function saveFeatures(outputDir, features) {
|
|
9883
10060
|
const obj = Object.fromEntries(features);
|
|
9884
|
-
await
|
|
10061
|
+
await writeFile10(join29(outputDir, FEATURES_FILE), JSON.stringify(obj, null, 2), "utf-8");
|
|
9885
10062
|
}
|
|
9886
10063
|
async function loadFeatures(outputDir) {
|
|
9887
10064
|
try {
|
|
9888
|
-
const raw = await
|
|
10065
|
+
const raw = await readFile18(join29(outputDir, FEATURES_FILE), "utf-8");
|
|
9889
10066
|
const obj = JSON.parse(raw);
|
|
9890
10067
|
return new Map(Object.entries(obj));
|
|
9891
10068
|
} catch {
|
|
@@ -9919,7 +10096,7 @@ Process every page. Call add_feature for each sub-feature you discover. When don
|
|
|
9919
10096
|
add_feature: tool14({
|
|
9920
10097
|
description: "Add a discovered sub-feature",
|
|
9921
10098
|
inputSchema: Feature.extend({
|
|
9922
|
-
id:
|
|
10099
|
+
id: z53.string().min(1).describe("Unique kebab-case ID (e.g. 'settings-notifications-tab')")
|
|
9923
10100
|
}),
|
|
9924
10101
|
execute: (featureInput) => {
|
|
9925
10102
|
const { id, ...rest } = featureInput;
|
|
@@ -9933,17 +10110,17 @@ Process every page. Call add_feature for each sub-feature you discover. When don
|
|
|
9933
10110
|
}),
|
|
9934
10111
|
view_features: tool14({
|
|
9935
10112
|
description: "View all discovered features so far",
|
|
9936
|
-
inputSchema:
|
|
10113
|
+
inputSchema: z53.object({}),
|
|
9937
10114
|
execute: () => collector.viewFeatures()
|
|
9938
10115
|
}),
|
|
9939
10116
|
view_pages: tool14({
|
|
9940
10117
|
description: "View the pages list to know what to analyze",
|
|
9941
|
-
inputSchema:
|
|
10118
|
+
inputSchema: z53.object({}),
|
|
9942
10119
|
execute: () => pagesDescription
|
|
9943
10120
|
}),
|
|
9944
10121
|
finish: tool14({
|
|
9945
10122
|
description: "Signal that feature discovery is complete",
|
|
9946
|
-
inputSchema:
|
|
10123
|
+
inputSchema: z53.object({ summary: z53.string() }),
|
|
9947
10124
|
execute: async (finishInput) => {
|
|
9948
10125
|
result = {
|
|
9949
10126
|
success: true,
|
|
@@ -9974,13 +10151,13 @@ var init_b_feature_discovery = __esm({
|
|
|
9974
10151
|
init_model();
|
|
9975
10152
|
init_tools();
|
|
9976
10153
|
FEATURES_FILE = "features.json";
|
|
9977
|
-
Feature =
|
|
9978
|
-
name:
|
|
9979
|
-
type:
|
|
9980
|
-
parentPagePath:
|
|
9981
|
-
sourceFiles:
|
|
9982
|
-
interactiveElements:
|
|
9983
|
-
description:
|
|
10154
|
+
Feature = z53.object({
|
|
10155
|
+
name: z53.string().min(1).describe("Human-readable name (e.g. 'Settings > Notifications Tab', 'Create Project Modal')"),
|
|
10156
|
+
type: z53.enum(["tab", "modal", "form", "table", "wizard", "nested-route", "complex-component"]),
|
|
10157
|
+
parentPagePath: z53.string().min(1).describe("The page path this feature belongs to (from the pages list)"),
|
|
10158
|
+
sourceFiles: z53.array(z53.string()).min(1).describe("Relative paths to the source files for this sub-feature"),
|
|
10159
|
+
interactiveElements: z53.number().int().min(0).describe("Count of interactive elements found (buttons, inputs, toggles, etc.)"),
|
|
10160
|
+
description: z53.string().min(10).describe("What this sub-feature does")
|
|
9984
10161
|
});
|
|
9985
10162
|
FeatureCollector = class {
|
|
9986
10163
|
features = /* @__PURE__ */ new Map();
|
|
@@ -10063,21 +10240,21 @@ Use kebab-case IDs that indicate the parent page and feature type:
|
|
|
10063
10240
|
});
|
|
10064
10241
|
|
|
10065
10242
|
// src/agents/05-test-generator/graph.ts
|
|
10066
|
-
import { readFile as
|
|
10067
|
-
import { join as
|
|
10243
|
+
import { readFile as readFile19, writeFile as writeFile11 } from "fs/promises";
|
|
10244
|
+
import { join as join30 } from "path";
|
|
10068
10245
|
function estimateExpectedTests(written, processed, totalNodes) {
|
|
10069
10246
|
if (totalNodes <= 0) return written;
|
|
10070
10247
|
const rate = written > 0 && processed >= LIVE_RATE_MIN_PROCESSED ? written / processed : TESTS_PER_NODE_PRIOR;
|
|
10071
10248
|
return Math.max(written, Math.round(rate * totalNodes));
|
|
10072
10249
|
}
|
|
10073
10250
|
async function saveBfsState(outputDir, state) {
|
|
10074
|
-
const path3 =
|
|
10075
|
-
await
|
|
10251
|
+
const path3 = join30(outputDir, state.stateFile);
|
|
10252
|
+
await writeFile11(path3, JSON.stringify(state.serialize(), null, 2), "utf-8");
|
|
10076
10253
|
}
|
|
10077
10254
|
async function loadBfsState(outputDir) {
|
|
10078
|
-
const path3 =
|
|
10255
|
+
const path3 = join30(outputDir, BFS_STATE_FILE);
|
|
10079
10256
|
try {
|
|
10080
|
-
const raw = await
|
|
10257
|
+
const raw = await readFile19(path3, "utf-8");
|
|
10081
10258
|
return CoverageState.deserialize(JSON.parse(raw));
|
|
10082
10259
|
} catch {
|
|
10083
10260
|
return void 0;
|
|
@@ -10089,6 +10266,7 @@ var init_graph = __esm({
|
|
|
10089
10266
|
"use strict";
|
|
10090
10267
|
init_esm_shims();
|
|
10091
10268
|
init_progress();
|
|
10269
|
+
init_store();
|
|
10092
10270
|
BFS_STATE_FILE = ".bfs-state.json";
|
|
10093
10271
|
JOURNEY_STATE_FILE = ".journey-state.json";
|
|
10094
10272
|
TESTS_PER_NODE_PRIOR = 3;
|
|
@@ -10205,6 +10383,29 @@ var init_graph = __esm({
|
|
|
10205
10383
|
this.phase = phase;
|
|
10206
10384
|
this.reportProgress();
|
|
10207
10385
|
}
|
|
10386
|
+
/**
|
|
10387
|
+
* Report the review pass as its own sub-progress, distinct from node
|
|
10388
|
+
* exploration. The step strip already shows "13/13 nodes" once generation
|
|
10389
|
+
* finishes; this replaces it with "Review 4/10" so the run does not look
|
|
10390
|
+
* done while the longest silent phase is still ahead.
|
|
10391
|
+
*
|
|
10392
|
+
* `done`/`total` count the tests reviewed this cycle. `startedAt` lets the
|
|
10393
|
+
* ETA bridge the gap before the first completion arrives.
|
|
10394
|
+
*/
|
|
10395
|
+
setReviewProgress(done, total, startedAt) {
|
|
10396
|
+
if (!this.reportsProgress) return;
|
|
10397
|
+
const note2 = this.phase;
|
|
10398
|
+
reportSubProgress("testGenerator", done, total, "reviewed", note2, startedAt);
|
|
10399
|
+
}
|
|
10400
|
+
/**
|
|
10401
|
+
* The review pass is over: restore the node-exploration sub-progress on the
|
|
10402
|
+
* strip, and settle the per-test rows so nothing is left claiming to be
|
|
10403
|
+
* under review or awaiting a fix that the budget cut off.
|
|
10404
|
+
*/
|
|
10405
|
+
clearReviewProgress() {
|
|
10406
|
+
this.reportProgress();
|
|
10407
|
+
getActiveStore()?.settleReviews();
|
|
10408
|
+
}
|
|
10208
10409
|
/** Processed nodes (tested or skipped) over the known graph size, labelled
|
|
10209
10410
|
* with the running phase or - while exploring - a live estimate of the final
|
|
10210
10411
|
* test count, which isn't known upfront (each node's test count is decided as
|
|
@@ -10653,13 +10854,13 @@ When you finish, all dimensions are reported so the user knows what's covered an
|
|
|
10653
10854
|
});
|
|
10654
10855
|
|
|
10655
10856
|
// src/agents/05-test-generator/recipe-context.ts
|
|
10656
|
-
import { readFile as
|
|
10657
|
-
import { join as
|
|
10658
|
-
import { z as
|
|
10857
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
10858
|
+
import { join as join31 } from "path";
|
|
10859
|
+
import { z as z54 } from "zod";
|
|
10659
10860
|
async function loadRecipeContext(outputDir) {
|
|
10660
10861
|
let raw;
|
|
10661
10862
|
try {
|
|
10662
|
-
raw = await
|
|
10863
|
+
raw = await readFile20(join31(outputDir, "recipe.json"), "utf-8");
|
|
10663
10864
|
} catch (err) {
|
|
10664
10865
|
debugLog("recipe.json not present; test generation falls back to scenarios.md alone", { err });
|
|
10665
10866
|
return "";
|
|
@@ -10717,13 +10918,13 @@ var init_recipe_context = __esm({
|
|
|
10717
10918
|
init_debug();
|
|
10718
10919
|
STRUCTURAL_KEYS = /* @__PURE__ */ new Set(["_alias", "_ref"]);
|
|
10719
10920
|
TEMPLATE_TOKEN = /\{\{\s*\w+\s*\}\}/;
|
|
10720
|
-
recipeRowSchema =
|
|
10721
|
-
recipeFileSchema =
|
|
10722
|
-
recipes:
|
|
10723
|
-
|
|
10724
|
-
name:
|
|
10725
|
-
description:
|
|
10726
|
-
create:
|
|
10921
|
+
recipeRowSchema = z54.record(z54.string(), z54.unknown());
|
|
10922
|
+
recipeFileSchema = z54.object({
|
|
10923
|
+
recipes: z54.array(
|
|
10924
|
+
z54.object({
|
|
10925
|
+
name: z54.string(),
|
|
10926
|
+
description: z54.string().optional(),
|
|
10927
|
+
create: z54.record(z54.string(), z54.array(recipeRowSchema)).optional()
|
|
10727
10928
|
})
|
|
10728
10929
|
)
|
|
10729
10930
|
});
|
|
@@ -10731,7 +10932,7 @@ var init_recipe_context = __esm({
|
|
|
10731
10932
|
});
|
|
10732
10933
|
|
|
10733
10934
|
// src/agents/05-test-generator/restore-deleted-test.ts
|
|
10734
|
-
import { access, mkdir as
|
|
10935
|
+
import { access, mkdir as mkdir4, writeFile as writeFile12 } from "fs/promises";
|
|
10735
10936
|
import { dirname as dirname3 } from "path";
|
|
10736
10937
|
async function restoreDeletedTest(testPath, originalContent) {
|
|
10737
10938
|
try {
|
|
@@ -10741,8 +10942,8 @@ async function restoreDeletedTest(testPath, originalContent) {
|
|
|
10741
10942
|
debugLog("Test absent after its fix pass - restoring the pre-review content", { testPath, err });
|
|
10742
10943
|
}
|
|
10743
10944
|
try {
|
|
10744
|
-
await
|
|
10745
|
-
await
|
|
10945
|
+
await mkdir4(dirname3(testPath), { recursive: true });
|
|
10946
|
+
await writeFile12(testPath, originalContent, "utf-8");
|
|
10746
10947
|
return true;
|
|
10747
10948
|
} catch (err) {
|
|
10748
10949
|
console.warn(` [fix] Could not restore ${testPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -10764,19 +10965,25 @@ var init_restore_deleted_test = __esm({
|
|
|
10764
10965
|
});
|
|
10765
10966
|
|
|
10766
10967
|
// src/core/pool.ts
|
|
10767
|
-
async function runPool(items, { limit, shouldContinue }, work) {
|
|
10968
|
+
async function runPool(items, { limit, shouldContinue, onProgress, onStart }, work) {
|
|
10768
10969
|
const completed = [];
|
|
10769
10970
|
const failed = [];
|
|
10770
10971
|
const skipped = [];
|
|
10771
10972
|
const inFlight2 = /* @__PURE__ */ new Set();
|
|
10772
10973
|
let next = 0;
|
|
10974
|
+
const total = items.length;
|
|
10975
|
+
const settled = { n: 0 };
|
|
10976
|
+
const fireProgress = () => onProgress?.(settled.n, total);
|
|
10773
10977
|
const dispatch = (item, index) => {
|
|
10978
|
+
onStart?.(item, index);
|
|
10774
10979
|
const promise = work(item, index).then((result) => {
|
|
10775
10980
|
completed.push({ item, index, result });
|
|
10776
10981
|
}).catch((error) => {
|
|
10777
10982
|
failed.push({ item, index, error });
|
|
10778
10983
|
}).finally(() => {
|
|
10779
10984
|
inFlight2.delete(promise);
|
|
10985
|
+
settled.n++;
|
|
10986
|
+
fireProgress();
|
|
10780
10987
|
});
|
|
10781
10988
|
inFlight2.add(promise);
|
|
10782
10989
|
};
|
|
@@ -10803,21 +11010,21 @@ var init_pool = __esm({
|
|
|
10803
11010
|
});
|
|
10804
11011
|
|
|
10805
11012
|
// src/agents/05-test-generator/rubrics.ts
|
|
10806
|
-
import { z as
|
|
11013
|
+
import { z as z55 } from "zod";
|
|
10807
11014
|
function reviewResultSchema(shape) {
|
|
10808
|
-
return
|
|
11015
|
+
return z55.object(shape);
|
|
10809
11016
|
}
|
|
10810
11017
|
var dimensionResultSchema, reviewResultRecordSchema, structuralIntentRubric, flowCompletenessRubric, uiTextRubric, dataAccuracyRubric, ALL_RUBRICS;
|
|
10811
11018
|
var init_rubrics = __esm({
|
|
10812
11019
|
"src/agents/05-test-generator/rubrics.ts"() {
|
|
10813
11020
|
"use strict";
|
|
10814
11021
|
init_esm_shims();
|
|
10815
|
-
dimensionResultSchema =
|
|
10816
|
-
pass:
|
|
10817
|
-
evidence:
|
|
10818
|
-
suggestion:
|
|
11022
|
+
dimensionResultSchema = z55.object({
|
|
11023
|
+
pass: z55.boolean(),
|
|
11024
|
+
evidence: z55.string().describe("What you checked and found - cite file paths, line content, or specific strings"),
|
|
11025
|
+
suggestion: z55.string().optional().describe("What the planner agent should fix, if failing")
|
|
10819
11026
|
});
|
|
10820
|
-
reviewResultRecordSchema =
|
|
11027
|
+
reviewResultRecordSchema = z55.record(z55.string(), dimensionResultSchema);
|
|
10821
11028
|
structuralIntentRubric = {
|
|
10822
11029
|
name: "structural-intent",
|
|
10823
11030
|
maxSteps: 8,
|
|
@@ -11063,10 +11270,13 @@ var init_review_pass = __esm({
|
|
|
11063
11270
|
});
|
|
11064
11271
|
|
|
11065
11272
|
// src/agents/05-test-generator/review.ts
|
|
11066
|
-
import { readFile as
|
|
11067
|
-
import { join as
|
|
11273
|
+
import { readFile as readFile21 } from "fs/promises";
|
|
11274
|
+
import { join as join32, relative as relative7, basename as basename4 } from "path";
|
|
11068
11275
|
import "ai";
|
|
11069
11276
|
import { glob as glob5 } from "glob";
|
|
11277
|
+
function markReview(relativePath, status) {
|
|
11278
|
+
getActiveStore()?.setArtifactReview(join32(TESTS_DIR, relativePath), status);
|
|
11279
|
+
}
|
|
11070
11280
|
function mergeRubricResults(reported) {
|
|
11071
11281
|
const merged = {};
|
|
11072
11282
|
for (const rubric of ALL_RUBRICS) {
|
|
@@ -11086,6 +11296,7 @@ async function reviewOneTest({
|
|
|
11086
11296
|
test,
|
|
11087
11297
|
dataContract
|
|
11088
11298
|
}) {
|
|
11299
|
+
markReview(test.relativePath, "REVIEWING");
|
|
11089
11300
|
const reported = /* @__PURE__ */ new Map();
|
|
11090
11301
|
await Promise.all(
|
|
11091
11302
|
ALL_RUBRICS.map(async (rubric) => {
|
|
@@ -11106,35 +11317,37 @@ async function reviewOneTest({
|
|
|
11106
11317
|
})
|
|
11107
11318
|
);
|
|
11108
11319
|
const dimensions = mergeRubricResults(reported);
|
|
11320
|
+
const failedDimensions = Object.entries(dimensions).filter(([, dim]) => !dim.pass).map(([key]) => key);
|
|
11321
|
+
markReview(test.relativePath, failedDimensions.length === 0 ? "REVIEWED" : "FIXING");
|
|
11109
11322
|
return {
|
|
11110
11323
|
relativePath: test.relativePath,
|
|
11111
11324
|
content: test.content,
|
|
11112
11325
|
dimensions,
|
|
11113
|
-
failedDimensions
|
|
11326
|
+
failedDimensions
|
|
11114
11327
|
};
|
|
11115
11328
|
}
|
|
11116
11329
|
async function readDataContract(outputDir) {
|
|
11117
11330
|
const recipe = await loadRecipeContext(outputDir);
|
|
11118
11331
|
if (recipe !== "") return recipe;
|
|
11119
11332
|
try {
|
|
11120
|
-
return await
|
|
11333
|
+
return await readFile21(join32(outputDir, "scenarios.md"), "utf-8");
|
|
11121
11334
|
} catch (err) {
|
|
11122
11335
|
debugLog("No data contract available for review", { err });
|
|
11123
11336
|
return void 0;
|
|
11124
11337
|
}
|
|
11125
11338
|
}
|
|
11126
|
-
async function runConsolidatedReview(outputDir, projectRoot, model, deadline, settled = /* @__PURE__ */ new Set(), precomputed = []) {
|
|
11127
|
-
const testsDir =
|
|
11339
|
+
async function runConsolidatedReview(outputDir, projectRoot, model, deadline, settled = /* @__PURE__ */ new Set(), precomputed = [], onProgress) {
|
|
11340
|
+
const testsDir = join32(outputDir, TESTS_DIR);
|
|
11128
11341
|
const logger = createStepLogger("review", 5);
|
|
11129
11342
|
const scenarioData = await readDataContract(outputDir);
|
|
11130
|
-
const testFiles = await glob5(
|
|
11343
|
+
const testFiles = await glob5(join32(testsDir, TEST_FILE_GLOB));
|
|
11131
11344
|
const tests = [];
|
|
11132
11345
|
for (const testPath of testFiles) {
|
|
11133
11346
|
if (!isTestFile(testPath)) continue;
|
|
11134
11347
|
if (testPath.includes("/_invalid/")) continue;
|
|
11135
11348
|
const relativePath = relative7(testsDir, testPath);
|
|
11136
11349
|
if (settled.has(relativePath)) continue;
|
|
11137
|
-
const content = await
|
|
11350
|
+
const content = await readFile21(testPath, "utf-8");
|
|
11138
11351
|
const flowMatch = content.match(/^---\n[\s\S]*?flow:\s*["']?([^"'\n]+)["']?\s*\n[\s\S]*?---/m);
|
|
11139
11352
|
tests.push({ path: testPath, relativePath, content, flow: flowMatch?.[1]?.trim() ?? "unknown" });
|
|
11140
11353
|
}
|
|
@@ -11153,15 +11366,24 @@ async function runConsolidatedReview(outputDir, projectRoot, model, deadline, se
|
|
|
11153
11366
|
toolErrors: [],
|
|
11154
11367
|
writtenFiles: []
|
|
11155
11368
|
});
|
|
11369
|
+
getActiveStore()?.pushActivity({ call: "review", arg: `${toReview.length} tests \xD7 ${ALL_RUBRICS.length} rubrics` });
|
|
11156
11370
|
let passed = 0;
|
|
11157
11371
|
let failed = 0;
|
|
11158
11372
|
const feedback = [];
|
|
11159
11373
|
const passedPaths = [];
|
|
11160
11374
|
const byTest = /* @__PURE__ */ new Map();
|
|
11161
11375
|
let reviewed = 0;
|
|
11376
|
+
onProgress?.(0, jobs.length);
|
|
11162
11377
|
const outcome = await runPool(
|
|
11163
11378
|
jobs,
|
|
11164
|
-
{
|
|
11379
|
+
{
|
|
11380
|
+
limit: REVIEW_CONCURRENCY,
|
|
11381
|
+
shouldContinue: () => Date.now() <= deadline,
|
|
11382
|
+
onProgress,
|
|
11383
|
+
// Jobs are (test, rubric) pairs, so a test's row lights up on its
|
|
11384
|
+
// first dispatched rubric; the store ignores the repeats.
|
|
11385
|
+
onStart: (job) => markReview(job.test.relativePath, "REVIEWING")
|
|
11386
|
+
},
|
|
11165
11387
|
(job) => runReviewPass(job.test.content, job.test.relativePath, job.rubric, projectRoot, model, scenarioData)
|
|
11166
11388
|
);
|
|
11167
11389
|
for (const { item, error } of outcome.failed) {
|
|
@@ -11193,8 +11415,10 @@ async function runConsolidatedReview(outputDir, projectRoot, model, deadline, se
|
|
|
11193
11415
|
if (failedDimensions.length === 0) {
|
|
11194
11416
|
passed++;
|
|
11195
11417
|
passedPaths.push(test.relativePath);
|
|
11418
|
+
markReview(test.relativePath, "REVIEWED");
|
|
11196
11419
|
} else {
|
|
11197
11420
|
failed++;
|
|
11421
|
+
markReview(test.relativePath, "FIXING");
|
|
11198
11422
|
feedback.push({
|
|
11199
11423
|
testPath: test.path,
|
|
11200
11424
|
relativePath: test.relativePath,
|
|
@@ -11236,6 +11460,7 @@ var init_review = __esm({
|
|
|
11236
11460
|
init_logs();
|
|
11237
11461
|
init_pool();
|
|
11238
11462
|
init_test_files();
|
|
11463
|
+
init_store();
|
|
11239
11464
|
init_recipe_context();
|
|
11240
11465
|
init_review_pass();
|
|
11241
11466
|
init_rubrics();
|
|
@@ -11381,7 +11606,7 @@ var init_validation = __esm({
|
|
|
11381
11606
|
});
|
|
11382
11607
|
|
|
11383
11608
|
// src/agents/05-test-generator/test-spec.ts
|
|
11384
|
-
import { z as
|
|
11609
|
+
import { z as z56 } from "zod";
|
|
11385
11610
|
function hasPlaceholder(text2) {
|
|
11386
11611
|
if (text2 == null) return false;
|
|
11387
11612
|
return PLACEHOLDER_PATTERNS.some(({ pattern }) => pattern.test(text2));
|
|
@@ -11432,12 +11657,12 @@ var init_test_spec = __esm({
|
|
|
11432
11657
|
{ pattern: /(?<!\{)\{[a-z][a-zA-Z]*\}(?!\})/, name: "bare {variable}" },
|
|
11433
11658
|
{ pattern: /\be\.g\./i, name: '"e.g." example' }
|
|
11434
11659
|
];
|
|
11435
|
-
stepSchema =
|
|
11436
|
-
verb:
|
|
11437
|
-
description:
|
|
11660
|
+
stepSchema = z56.object({
|
|
11661
|
+
verb: z56.enum(STEP_VERBS).describe("The action. Only these verbs exist."),
|
|
11662
|
+
description: z56.string().min(1).describe(
|
|
11438
11663
|
`What to do or check, naming the exact visible text. For assert, the thing expected on screen - e.g. 'text "Transfer Successful"'.`
|
|
11439
11664
|
),
|
|
11440
|
-
location:
|
|
11665
|
+
location: z56.string().optional().describe(
|
|
11441
11666
|
'WHERE on screen the target is - "in the modal", "in the toast notification", "on the Sony WH-1000XM5 product card", "in the dashboard header", "as a page heading". REQUIRED for click, type and assert: the same label routinely appears more than once (a header button and the modal button it opens, "Buy" on every card in a list), and acting on the wrong one fails confusingly. Not needed for scroll or refresh, which act on the page.'
|
|
11442
11667
|
)
|
|
11443
11668
|
}).refine((step) => !requiresLocation(step.verb) || (step.location?.trim() ?? "") !== "", {
|
|
@@ -11447,27 +11672,27 @@ var init_test_spec = __esm({
|
|
|
11447
11672
|
message: "steps carry no variables - use the exact value from the test data, not a placeholder or example",
|
|
11448
11673
|
path: ["description"]
|
|
11449
11674
|
});
|
|
11450
|
-
testSpecSchema =
|
|
11451
|
-
title:
|
|
11452
|
-
description:
|
|
11453
|
-
intent:
|
|
11675
|
+
testSpecSchema = z56.object({
|
|
11676
|
+
title: z56.string().min(1).describe("Short, descriptive test name."),
|
|
11677
|
+
description: z56.string().min(1).describe("One sentence explaining what the test verifies."),
|
|
11678
|
+
intent: z56.string().min(30).describe(
|
|
11454
11679
|
"A specific, falsifiable claim derived from the node's mission: what the user does, what the feature produces, why it matters. Not the steps, not 'the page displays correctly'."
|
|
11455
11680
|
),
|
|
11456
|
-
criticality:
|
|
11457
|
-
scenario:
|
|
11458
|
-
flow:
|
|
11459
|
-
verification:
|
|
11681
|
+
criticality: z56.enum(CRITICALITY_LEVELS),
|
|
11682
|
+
scenario: z56.string().min(1).describe('Which scenario this test uses (usually "standard").'),
|
|
11683
|
+
flow: z56.string().min(1).describe("Which feature/flow this belongs to (must match a flow from AUTONOMA.md)."),
|
|
11684
|
+
verification: z56.string().min(20).describe(
|
|
11460
11685
|
"WHERE to navigate and WHAT to assert to prove the mutation worked. Must name the source of truth - a toast, a confirmation dialog or an inline success indicator is an acknowledgment, not proof."
|
|
11461
11686
|
),
|
|
11462
|
-
setup:
|
|
11687
|
+
setup: z56.string().min(1).describe(
|
|
11463
11688
|
"Which page the user starts on and how they got there. Never authentication - the user is already signed in."
|
|
11464
11689
|
),
|
|
11465
|
-
steps:
|
|
11466
|
-
verificationSteps:
|
|
11690
|
+
steps: z56.array(stepSchema).min(1).describe("The action sequence, in order."),
|
|
11691
|
+
verificationSteps: z56.array(stepSchema).describe(
|
|
11467
11692
|
"Steps that navigate to the source of truth and assert the mutation landed. Implements the `verification` field above."
|
|
11468
11693
|
),
|
|
11469
|
-
expectedResult:
|
|
11470
|
-
notes:
|
|
11694
|
+
expectedResult: z56.string().min(1).describe("What should be true when the test passes."),
|
|
11695
|
+
notes: z56.string().optional().describe(
|
|
11471
11696
|
"Optional free text for anything you could not resolve or want to flag - an ambiguous default state, a element you could not find in the source, an assumption you had to make. Recorded for humans and NEVER written into the test file, so it will not affect execution. Use it instead of guessing silently."
|
|
11472
11697
|
)
|
|
11473
11698
|
}).refine((spec) => spec.steps.filter((step) => isInteractionVerb(step.verb)).length >= MIN_INTERACTIONS, {
|
|
@@ -11478,25 +11703,25 @@ var init_test_spec = __esm({
|
|
|
11478
11703
|
});
|
|
11479
11704
|
|
|
11480
11705
|
// src/agents/05-test-generator/tools.ts
|
|
11481
|
-
import { mkdir as
|
|
11482
|
-
import { dirname as dirname4, join as
|
|
11706
|
+
import { mkdir as mkdir5, writeFile as writeFile13 } from "fs/promises";
|
|
11707
|
+
import { dirname as dirname4, join as join33 } from "path";
|
|
11483
11708
|
import { hasToolCall as hasToolCall3, stepCountIs as stepCountIs3, tool as tool16, ToolLoopAgent as ToolLoopAgent3 } from "ai";
|
|
11484
|
-
import { z as
|
|
11709
|
+
import { z as z57 } from "zod";
|
|
11485
11710
|
function buildWriteTestTool(state, outputDir, onWritten) {
|
|
11486
11711
|
return tool16({
|
|
11487
11712
|
description: "Write one test to qa-tests/{folder}/{filename}.md. You supply the test's parts; the file is rendered for you, so do not write markdown or frontmatter yourself.",
|
|
11488
|
-
inputSchema:
|
|
11489
|
-
folder:
|
|
11490
|
-
filename:
|
|
11491
|
-
nodeId:
|
|
11713
|
+
inputSchema: z57.object({
|
|
11714
|
+
folder: z57.string().describe("Subfolder name under qa-tests/"),
|
|
11715
|
+
filename: z57.string().describe(`File name ending in ${TEST_FILE_EXT} (e.g. login-valid-credentials${TEST_FILE_EXT})`),
|
|
11716
|
+
nodeId: z57.string().describe(
|
|
11492
11717
|
"The id next_node returned for this feature, copied verbatim. Not a re-slugged version of it, not a folder path, not the test filename."
|
|
11493
11718
|
),
|
|
11494
11719
|
test: testSpecSchema
|
|
11495
11720
|
}),
|
|
11496
11721
|
execute: async (input) => {
|
|
11497
11722
|
const content = renderTestMarkdown(input.test);
|
|
11498
|
-
const relPath =
|
|
11499
|
-
const absPath =
|
|
11723
|
+
const relPath = join33(TESTS_DIR, input.folder, normalizeTestFilename(input.filename));
|
|
11724
|
+
const absPath = join33(outputDir, relPath);
|
|
11500
11725
|
const nodeId = state.resolveNodeId(input.nodeId, relPath);
|
|
11501
11726
|
if (nodeId == null) {
|
|
11502
11727
|
const stats = state.summary();
|
|
@@ -11514,8 +11739,8 @@ function buildWriteTestTool(state, outputDir, onWritten) {
|
|
|
11514
11739
|
};
|
|
11515
11740
|
}
|
|
11516
11741
|
try {
|
|
11517
|
-
await
|
|
11518
|
-
await
|
|
11742
|
+
await mkdir5(dirname4(absPath), { recursive: true });
|
|
11743
|
+
await writeFile13(absPath, content, "utf-8");
|
|
11519
11744
|
state.markTested(nodeId, [relPath]);
|
|
11520
11745
|
await saveBfsState(outputDir, state);
|
|
11521
11746
|
const notes = input.test.notes?.trim();
|
|
@@ -11528,7 +11753,7 @@ function buildWriteTestTool(state, outputDir, onWritten) {
|
|
|
11528
11753
|
});
|
|
11529
11754
|
}
|
|
11530
11755
|
onWritten?.({
|
|
11531
|
-
relativePath:
|
|
11756
|
+
relativePath: join33(input.folder, normalizeTestFilename(input.filename)),
|
|
11532
11757
|
content,
|
|
11533
11758
|
flow: input.test.flow
|
|
11534
11759
|
});
|
|
@@ -11566,14 +11791,14 @@ function buildWriteTestTool(state, outputDir, onWritten) {
|
|
|
11566
11791
|
function buildCreateFolderTool(outputDir) {
|
|
11567
11792
|
return tool16({
|
|
11568
11793
|
description: "Create a folder under qa-tests/ for organizing tests.",
|
|
11569
|
-
inputSchema:
|
|
11570
|
-
folder:
|
|
11794
|
+
inputSchema: z57.object({
|
|
11795
|
+
folder: z57.string().describe("Folder name (kebab-case)")
|
|
11571
11796
|
}),
|
|
11572
11797
|
execute: async (input) => {
|
|
11573
|
-
const absPath =
|
|
11798
|
+
const absPath = join33(outputDir, TESTS_DIR, input.folder);
|
|
11574
11799
|
try {
|
|
11575
|
-
await
|
|
11576
|
-
return { path:
|
|
11800
|
+
await mkdir5(absPath, { recursive: true });
|
|
11801
|
+
return { path: join33(TESTS_DIR, input.folder) };
|
|
11577
11802
|
} catch (err) {
|
|
11578
11803
|
const message = err instanceof Error ? err.message : String(err);
|
|
11579
11804
|
return { error: `Failed to create folder: ${message}` };
|
|
@@ -11584,7 +11809,7 @@ function buildCreateFolderTool(outputDir) {
|
|
|
11584
11809
|
function buildNextNodeTool(state, outputDir) {
|
|
11585
11810
|
return tool16({
|
|
11586
11811
|
description: "Get the next node to write tests for. If you called next_node before without writing any tests (via write_test), the previous node is auto-skipped. Returns done:true when all nodes are processed.",
|
|
11587
|
-
inputSchema:
|
|
11812
|
+
inputSchema: z57.object({}),
|
|
11588
11813
|
execute: async () => {
|
|
11589
11814
|
const next = state.nextNode();
|
|
11590
11815
|
await saveBfsState(outputDir, state);
|
|
@@ -11618,7 +11843,7 @@ function buildNextNodeTool(state, outputDir) {
|
|
|
11618
11843
|
function buildGetProgressTool(state) {
|
|
11619
11844
|
return tool16({
|
|
11620
11845
|
description: "Check how many nodes have been tested vs how many remain.",
|
|
11621
|
-
inputSchema:
|
|
11846
|
+
inputSchema: z57.object({}),
|
|
11622
11847
|
execute: async () => {
|
|
11623
11848
|
const stats = state.summary();
|
|
11624
11849
|
const nodes = [...state.nodes.values()].map((n) => ({
|
|
@@ -11634,12 +11859,12 @@ function buildGetProgressTool(state) {
|
|
|
11634
11859
|
function buildSpawnResearcherTool(model, workingDirectory, onHeartbeat) {
|
|
11635
11860
|
return tool16({
|
|
11636
11861
|
description: "Spawn a research subagent to read and analyze source files without polluting your context. Use for complex sub-features where you don't want to read 20 files yourself.",
|
|
11637
|
-
inputSchema:
|
|
11638
|
-
instruction:
|
|
11862
|
+
inputSchema: z57.object({
|
|
11863
|
+
instruction: z57.string().describe("What to research - be specific about files and what to look for")
|
|
11639
11864
|
}),
|
|
11640
11865
|
execute: async (input) => {
|
|
11641
|
-
const resultSchema2 =
|
|
11642
|
-
findings:
|
|
11866
|
+
const resultSchema2 = z57.object({
|
|
11867
|
+
findings: z57.string().describe("Summary of what was found")
|
|
11643
11868
|
});
|
|
11644
11869
|
let result;
|
|
11645
11870
|
const subagent = new ToolLoopAgent3({
|
|
@@ -11692,11 +11917,11 @@ var init_tools2 = __esm({
|
|
|
11692
11917
|
});
|
|
11693
11918
|
|
|
11694
11919
|
// src/agents/05-test-generator/write-index.ts
|
|
11695
|
-
import { readFile as
|
|
11696
|
-
import { join as
|
|
11920
|
+
import { readFile as readFile22, writeFile as writeFile14 } from "fs/promises";
|
|
11921
|
+
import { join as join34 } from "path";
|
|
11697
11922
|
async function scanTest(outputDir, path3) {
|
|
11698
11923
|
try {
|
|
11699
|
-
const content = await
|
|
11924
|
+
const content = await readFile22(join34(outputDir, path3), "utf-8");
|
|
11700
11925
|
return {
|
|
11701
11926
|
criticality: content.match(/criticality:\s*(\w+)/)?.[1] ?? "",
|
|
11702
11927
|
steps: content.match(STEP_VERBS2)?.length ?? 0,
|
|
@@ -11767,7 +11992,7 @@ ${folders.map((f) => `| ${f.name} | ${f.test_count} |`).join("\n")}
|
|
|
11767
11992
|
|
|
11768
11993
|
${[...testsByFolder.entries()].flatMap(([_folder, tests]) => tests.map((t) => `- \`${t}\``)).join("\n")}
|
|
11769
11994
|
${renderSection("Features with no tests", "the run walked these and produced no test for them - re-run the planner to cover them", untested)}${renderSection("Tests lost in review", "the review cycle removed these and nothing could put them back - re-run the planner to regenerate them", lost)}`;
|
|
11770
|
-
await
|
|
11995
|
+
await writeFile14(join34(outputDir, TESTS_DIR, TEST_INDEX_FILE), content, "utf-8");
|
|
11771
11996
|
}
|
|
11772
11997
|
function untestedFeatures(state) {
|
|
11773
11998
|
const withTests = new Set(state.testsWritten.keys());
|
|
@@ -11802,11 +12027,11 @@ __export(test_generator_exports, {
|
|
|
11802
12027
|
runTestGenerator: () => runTestGenerator
|
|
11803
12028
|
});
|
|
11804
12029
|
import { existsSync as existsSync2 } from "fs";
|
|
11805
|
-
import { mkdir as
|
|
11806
|
-
import { basename as basename5, join as
|
|
12030
|
+
import { mkdir as mkdir6, readFile as readFile23, rmdir, unlink, writeFile as writeFile15 } from "fs/promises";
|
|
12031
|
+
import { basename as basename5, join as join35 } from "path";
|
|
11807
12032
|
import { tool as tool17 } from "ai";
|
|
11808
12033
|
import { glob as glob6 } from "glob";
|
|
11809
|
-
import { z as
|
|
12034
|
+
import { z as z58 } from "zod";
|
|
11810
12035
|
async function preseedQueue(state, projectRoot, pages, features) {
|
|
11811
12036
|
let seeded = 0;
|
|
11812
12037
|
const pageIdByPath = /* @__PURE__ */ new Map();
|
|
@@ -11858,8 +12083,8 @@ async function runTestGenerator(input) {
|
|
|
11858
12083
|
let result;
|
|
11859
12084
|
const finishTool = tool17({
|
|
11860
12085
|
description: "Call when the BFS queue is empty and all routes have been explored.",
|
|
11861
|
-
inputSchema:
|
|
11862
|
-
summary:
|
|
12086
|
+
inputSchema: z58.object({
|
|
12087
|
+
summary: z58.string().describe("Coverage summary")
|
|
11863
12088
|
}),
|
|
11864
12089
|
execute: async (finishInput) => {
|
|
11865
12090
|
const stats = state.summary();
|
|
@@ -11887,7 +12112,7 @@ async function runTestGenerator(input) {
|
|
|
11887
12112
|
});
|
|
11888
12113
|
let kbContext = "";
|
|
11889
12114
|
try {
|
|
11890
|
-
const autonomaMd = await
|
|
12115
|
+
const autonomaMd = await readFile23(join35(input.outputDir, "AUTONOMA.md"), "utf-8");
|
|
11891
12116
|
kbContext += `
|
|
11892
12117
|
## Knowledge Base (AUTONOMA.md)
|
|
11893
12118
|
|
|
@@ -11900,7 +12125,7 @@ ${autonomaMd}
|
|
|
11900
12125
|
kbContext += recipeContext;
|
|
11901
12126
|
} else {
|
|
11902
12127
|
try {
|
|
11903
|
-
const scenariosMd = await
|
|
12128
|
+
const scenariosMd = await readFile23(join35(input.outputDir, "scenarios.md"), "utf-8");
|
|
11904
12129
|
kbContext += `
|
|
11905
12130
|
## Scenarios
|
|
11906
12131
|
|
|
@@ -12092,13 +12317,15 @@ IMPORTANT: Do NOT try to finish early. Process every node via next_node until it
|
|
|
12092
12317
|
console.log(` Review cycle ${cycle + 1}/${MAX_REVIEW_CYCLES}`);
|
|
12093
12318
|
state.setPhase(`review cycle ${cycle + 1}/${MAX_REVIEW_CYCLES}`);
|
|
12094
12319
|
const scanDeadline = Date.now() + Math.floor((reviewDeadline - Date.now()) * REVIEW_SCAN_SHARE);
|
|
12320
|
+
const reviewStartedAt = activeAgentNow() ?? Date.now();
|
|
12095
12321
|
const reviewResult = await runConsolidatedReview(
|
|
12096
12322
|
input.outputDir,
|
|
12097
12323
|
input.projectRoot,
|
|
12098
12324
|
model,
|
|
12099
12325
|
scanDeadline,
|
|
12100
12326
|
settled,
|
|
12101
|
-
cycle === 0 ? pipelined : []
|
|
12327
|
+
cycle === 0 ? pipelined : [],
|
|
12328
|
+
(done, total) => state.setReviewProgress(done, total, reviewStartedAt)
|
|
12102
12329
|
);
|
|
12103
12330
|
for (const path3 of reviewResult.passedPaths) settled.add(path3);
|
|
12104
12331
|
console.log(
|
|
@@ -12194,21 +12421,22 @@ IMPORTANT: Do NOT try to finish early. Process every node via next_node until it
|
|
|
12194
12421
|
console.log(` Fix pass complete`);
|
|
12195
12422
|
if (scanCutShort) break;
|
|
12196
12423
|
}
|
|
12424
|
+
state.clearReviewProgress();
|
|
12197
12425
|
state.setPhase("checking every test");
|
|
12198
|
-
const allTestFiles = await glob6(
|
|
12426
|
+
const allTestFiles = await glob6(join35(input.outputDir, TESTS_DIR, TEST_FILE_GLOB));
|
|
12199
12427
|
let markedInvalid = 0;
|
|
12200
12428
|
for (const testPath of allTestFiles) {
|
|
12201
12429
|
if (!isTestFile(testPath)) continue;
|
|
12202
12430
|
if (testPath.includes(`/${INVALID_DIR}/`)) continue;
|
|
12203
|
-
const content = await
|
|
12431
|
+
const content = await readFile23(testPath, "utf-8");
|
|
12204
12432
|
const validation = validateTestContent(content);
|
|
12205
12433
|
if (!validation.valid) {
|
|
12206
|
-
const invalidDir =
|
|
12207
|
-
await
|
|
12208
|
-
const dest =
|
|
12434
|
+
const invalidDir = join35(input.outputDir, TESTS_DIR, INVALID_DIR);
|
|
12435
|
+
await mkdir6(invalidDir, { recursive: true });
|
|
12436
|
+
const dest = join35(invalidDir, basename5(testPath));
|
|
12209
12437
|
const annotated = `<!-- VALIDATION ERRORS: ${validation.errors.join("; ")} -->
|
|
12210
12438
|
${content}`;
|
|
12211
|
-
await
|
|
12439
|
+
await writeFile15(dest, annotated, "utf-8");
|
|
12212
12440
|
await unlink(testPath);
|
|
12213
12441
|
markedInvalid++;
|
|
12214
12442
|
}
|
|
@@ -12216,7 +12444,7 @@ ${content}`;
|
|
|
12216
12444
|
if (markedInvalid > 0) {
|
|
12217
12445
|
console.log(` ${markedInvalid} tests still invalid after review cycles - moved to _invalid/`);
|
|
12218
12446
|
}
|
|
12219
|
-
const dirs = await glob6(
|
|
12447
|
+
const dirs = await glob6(join35(input.outputDir, TESTS_DIR, "**/"), {
|
|
12220
12448
|
dot: false
|
|
12221
12449
|
});
|
|
12222
12450
|
for (const dir of dirs.sort((a, b) => b.length - a.length)) {
|
|
@@ -12269,21 +12497,21 @@ async function generateJourneyTests(outputDir, model, projectRoot) {
|
|
|
12269
12497
|
let autonomaMd = "";
|
|
12270
12498
|
let scenariosMd = "";
|
|
12271
12499
|
try {
|
|
12272
|
-
autonomaMd = await
|
|
12500
|
+
autonomaMd = await readFile23(join35(outputDir, "AUTONOMA.md"), "utf-8");
|
|
12273
12501
|
} catch (err) {
|
|
12274
12502
|
debugLog("AUTONOMA.md not present for journey generation", { err });
|
|
12275
12503
|
}
|
|
12276
12504
|
try {
|
|
12277
|
-
scenariosMd = await
|
|
12505
|
+
scenariosMd = await readFile23(join35(outputDir, "scenarios.md"), "utf-8");
|
|
12278
12506
|
} catch (err) {
|
|
12279
12507
|
debugLog("scenarios.md not present for journey generation", { err });
|
|
12280
12508
|
}
|
|
12281
12509
|
if (!autonomaMd) return 0;
|
|
12282
|
-
const existingTests = await glob6(
|
|
12510
|
+
const existingTests = await glob6(join35(outputDir, TESTS_DIR, TEST_FILE_GLOB));
|
|
12283
12511
|
const existingTitles = [];
|
|
12284
12512
|
for (const t of existingTests) {
|
|
12285
12513
|
if (!isTestFile(t)) continue;
|
|
12286
|
-
const content = await
|
|
12514
|
+
const content = await readFile23(t, "utf-8");
|
|
12287
12515
|
const titleMatch = content.match(/title:\s*"([^"]+)"/);
|
|
12288
12516
|
if (titleMatch) existingTitles.push(titleMatch[1]);
|
|
12289
12517
|
}
|
|
@@ -12329,7 +12557,7 @@ Write 5-8 journey tests using the write_test tool with folder "${JOURNEY_NODE_ID
|
|
|
12329
12557
|
let journeyResult;
|
|
12330
12558
|
const journeyFinish = tool17({
|
|
12331
12559
|
description: "Signal journey generation is complete.",
|
|
12332
|
-
inputSchema:
|
|
12560
|
+
inputSchema: z58.object({ summary: z58.string() }),
|
|
12333
12561
|
execute: async (finishInput) => {
|
|
12334
12562
|
journeyResult = {
|
|
12335
12563
|
success: true,
|
|
@@ -12377,6 +12605,7 @@ var init_test_generator = __esm({
|
|
|
12377
12605
|
init_gitignore();
|
|
12378
12606
|
init_logs();
|
|
12379
12607
|
init_model();
|
|
12608
|
+
init_progress();
|
|
12380
12609
|
init_test_files();
|
|
12381
12610
|
init_tools();
|
|
12382
12611
|
init_b_feature_discovery();
|
|
@@ -12488,7 +12717,7 @@ var init_ansi_to_rows = __esm({
|
|
|
12488
12717
|
|
|
12489
12718
|
// src/replay/types.ts
|
|
12490
12719
|
var NODE_TYPE;
|
|
12491
|
-
var
|
|
12720
|
+
var init_types2 = __esm({
|
|
12492
12721
|
"src/replay/types.ts"() {
|
|
12493
12722
|
"use strict";
|
|
12494
12723
|
init_esm_shims();
|
|
@@ -12572,7 +12801,7 @@ var init_frame_differ = __esm({
|
|
|
12572
12801
|
"use strict";
|
|
12573
12802
|
init_esm_shims();
|
|
12574
12803
|
init_ansi_to_rows();
|
|
12575
|
-
|
|
12804
|
+
init_types2();
|
|
12576
12805
|
DOCUMENT_ID = 1;
|
|
12577
12806
|
DOCTYPE_ID = 2;
|
|
12578
12807
|
HTML_ID = 3;
|
|
@@ -13199,7 +13428,7 @@ function drawWelcomeModal(g, state) {
|
|
|
13199
13428
|
for (let yy = y; yy < y + h; yy++) g.fillBg(x, yy, w, MODAL_FILL5);
|
|
13200
13429
|
const bg = MODAL_FILL5;
|
|
13201
13430
|
let cy = y + 1;
|
|
13202
|
-
g.text(x + 4, cy,
|
|
13431
|
+
g.text(x + 4, cy, `\u25C6 ${wel.eyebrow ?? DEFAULT_EYEBROW}`, { color: theme.accent, bold: true, bg });
|
|
13203
13432
|
cy += 2;
|
|
13204
13433
|
for (const line of titleLines) {
|
|
13205
13434
|
g.text(x + 4, cy, line, { color: theme.text, bold: true, bg });
|
|
@@ -13213,7 +13442,7 @@ function drawWelcomeModal(g, state) {
|
|
|
13213
13442
|
const ly = y + h - 2;
|
|
13214
13443
|
g.text(x + 4, ly, wel.cta, { color: theme.accent, bold: true, bg });
|
|
13215
13444
|
}
|
|
13216
|
-
var MODAL_MAX_W5, MODAL_FILL5;
|
|
13445
|
+
var MODAL_MAX_W5, MODAL_FILL5, DEFAULT_EYEBROW;
|
|
13217
13446
|
var init_welcome = __esm({
|
|
13218
13447
|
"src/ui/draw/welcome.ts"() {
|
|
13219
13448
|
"use strict";
|
|
@@ -13222,6 +13451,7 @@ var init_welcome = __esm({
|
|
|
13222
13451
|
init_wrap();
|
|
13223
13452
|
MODAL_MAX_W5 = 84;
|
|
13224
13453
|
MODAL_FILL5 = "#141414";
|
|
13454
|
+
DEFAULT_EYEBROW = "WELCOME TO AUTONOMA";
|
|
13225
13455
|
}
|
|
13226
13456
|
});
|
|
13227
13457
|
|
|
@@ -13307,6 +13537,14 @@ function artifactStatusCell(a, now) {
|
|
|
13307
13537
|
const frame = SPINNER_FRAMES2[Math.floor(now / WRITING_SPINNER_MS) % SPINNER_FRAMES2.length];
|
|
13308
13538
|
return { t: `${frame} WRITING`, color: theme.sky };
|
|
13309
13539
|
}
|
|
13540
|
+
// Deliberately no spinner on REVIEWING: 16 review agents run at once, so
|
|
13541
|
+
// a spinner per row would set most of the list twitching at 5fps.
|
|
13542
|
+
case "REVIEWING":
|
|
13543
|
+
return { t: "REVIEWING", color: theme.violet };
|
|
13544
|
+
case "REVIEWED":
|
|
13545
|
+
return { t: "\u2713 REVIEWED", color: theme.green };
|
|
13546
|
+
case "FIXING":
|
|
13547
|
+
return { t: "FIXING", color: theme.amber };
|
|
13310
13548
|
default:
|
|
13311
13549
|
return { t: "PENDING", color: theme.faint };
|
|
13312
13550
|
}
|
|
@@ -13337,7 +13575,8 @@ function drawFiles(g, geo, state, base, bottom) {
|
|
|
13337
13575
|
g.text(2, row, icon, { color: theme.tertiary, bg });
|
|
13338
13576
|
const label = a.title ?? a.name;
|
|
13339
13577
|
const detail = a.title != null ? [a.name, a.description].filter((s) => s != null).join(" \xB7 ") : a.description;
|
|
13340
|
-
const
|
|
13578
|
+
const written = a.status !== "PENDING";
|
|
13579
|
+
const nameColor = highlighted ? theme.accent : written ? theme.text : theme.secondary;
|
|
13341
13580
|
const nameMax = geo.div - 2 - STATUS_CELL_W - 2 - 5;
|
|
13342
13581
|
const shown = label.length > nameMax ? label.slice(0, Math.max(1, nameMax - 1)) + "\u2026" : label;
|
|
13343
13582
|
g.text(5, row, shown, { color: nameColor, bold: highlighted || active2, bg });
|
|
@@ -13461,6 +13700,7 @@ function callColor(call) {
|
|
|
13461
13700
|
if (call === "done" || call === "success") return theme.green;
|
|
13462
13701
|
if (call === "warn" || call === "checkpoint") return theme.amber;
|
|
13463
13702
|
if (call === "error") return theme.red;
|
|
13703
|
+
if (call === "review") return theme.violet;
|
|
13464
13704
|
return theme.secondary;
|
|
13465
13705
|
}
|
|
13466
13706
|
function drawActivity(g, state, top, h) {
|
|
@@ -13502,7 +13742,8 @@ function drawDashboard(g, state) {
|
|
|
13502
13742
|
drawActivity(g, state, panelBottom, activityRows);
|
|
13503
13743
|
const exitHint = state.ctrlCArmed ? { k: "Ctrl+C", label: "again to exit (progress saved)", color: theme.red } : { k: "^C ^C", label: "exit", color: theme.red };
|
|
13504
13744
|
if (state.welcome != null) {
|
|
13505
|
-
|
|
13745
|
+
const label = state.welcome.eyebrow == null ? "begin" : "continue";
|
|
13746
|
+
drawHints(g, H - 1, [{ k: "enter", label, color: theme.accent }], [exitHint]);
|
|
13506
13747
|
drawWelcomeModal(g, state);
|
|
13507
13748
|
return;
|
|
13508
13749
|
}
|
|
@@ -13658,6 +13899,27 @@ var init_grid = __esm({
|
|
|
13658
13899
|
this.h = h;
|
|
13659
13900
|
this.cells = Array.from({ length: h }, () => Array.from({ length: w }, () => ({ ch: " " })));
|
|
13660
13901
|
}
|
|
13902
|
+
/**
|
|
13903
|
+
* Reset every cell to a blank space, reusing the existing `cells` arrays.
|
|
13904
|
+
*
|
|
13905
|
+
* Allocation is the leak: a 100x30 terminal allocates 3000 Cell objects
|
|
13906
|
+
* per render, and at 10 renders/second (the clock tick) that is
|
|
13907
|
+
* ~30k objects/second of short-lived garbage. V8's GC runs constantly
|
|
13908
|
+
* to reclaim it, and the GC pauses block the event loop, starving the
|
|
13909
|
+
* pipeline. Clearing in place avoids the allocation entirely.
|
|
13910
|
+
*/
|
|
13911
|
+
clear() {
|
|
13912
|
+
for (let y = 0; y < this.h; y++) {
|
|
13913
|
+
const row = this.cells[y];
|
|
13914
|
+
for (let x = 0; x < this.w; x++) {
|
|
13915
|
+
const c = row[x];
|
|
13916
|
+
c.ch = " ";
|
|
13917
|
+
c.color = void 0;
|
|
13918
|
+
c.bg = void 0;
|
|
13919
|
+
c.bold = void 0;
|
|
13920
|
+
}
|
|
13921
|
+
}
|
|
13922
|
+
}
|
|
13661
13923
|
set(x, y, ch, st = {}) {
|
|
13662
13924
|
if (y < 0 || y >= this.h || x < 0 || x >= this.w) return;
|
|
13663
13925
|
const safe = ch < " " ? " " : ch;
|
|
@@ -13731,23 +13993,50 @@ var init_grid = __esm({
|
|
|
13731
13993
|
* Flatten into per-row ANSI strings (24-bit color escapes). One string per
|
|
13732
13994
|
* row keeps the Ink tree tiny - rendering thousands of nested <Text> spans
|
|
13733
13995
|
* per frame is what makes large dashboards crawl.
|
|
13996
|
+
*
|
|
13997
|
+
* Builds the string directly from cells, skipping the intermediate Seg[][]
|
|
13998
|
+
* that `rows()` produces. The Seg array was allocated per render and
|
|
13999
|
+
* immediately discarded - avoiding it cuts another ~600 short-lived objects
|
|
14000
|
+
* per frame.
|
|
13734
14001
|
*/
|
|
13735
14002
|
ansiRows() {
|
|
13736
|
-
|
|
13737
|
-
|
|
13738
|
-
|
|
13739
|
-
|
|
13740
|
-
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
|
|
13744
|
-
|
|
14003
|
+
const out = [];
|
|
14004
|
+
for (let y = 0; y < this.h; y++) {
|
|
14005
|
+
const row = this.cells[y];
|
|
14006
|
+
let line = "";
|
|
14007
|
+
let curCh = "";
|
|
14008
|
+
let curColor;
|
|
14009
|
+
let curBg;
|
|
14010
|
+
let curBold = false;
|
|
14011
|
+
let hasStyle = false;
|
|
14012
|
+
const flush2 = () => {
|
|
14013
|
+
if (!hasStyle) {
|
|
14014
|
+
line += curCh;
|
|
13745
14015
|
} else {
|
|
13746
|
-
|
|
14016
|
+
const codes = [];
|
|
14017
|
+
if (curBold) codes.push("1");
|
|
14018
|
+
if (curColor != null) codes.push(fgCode(curColor));
|
|
14019
|
+
if (curBg != null) codes.push(bgCode(curBg));
|
|
14020
|
+
line += `\x1B[${codes.join(";")}m${curCh}\x1B[0m`;
|
|
14021
|
+
}
|
|
14022
|
+
curCh = "";
|
|
14023
|
+
};
|
|
14024
|
+
for (let x = 0; x < this.w; x++) {
|
|
14025
|
+
const c = row[x];
|
|
14026
|
+
const cBold = !!c.bold;
|
|
14027
|
+
if (c.color !== curColor || c.bg !== curBg || cBold !== curBold) {
|
|
14028
|
+
if (curCh !== "") flush2();
|
|
14029
|
+
curColor = c.color;
|
|
14030
|
+
curBg = c.bg;
|
|
14031
|
+
curBold = cBold;
|
|
14032
|
+
hasStyle = curColor != null || curBg != null || curBold;
|
|
13747
14033
|
}
|
|
14034
|
+
curCh += c.ch;
|
|
13748
14035
|
}
|
|
13749
|
-
|
|
13750
|
-
|
|
14036
|
+
if (curCh !== "") flush2();
|
|
14037
|
+
out.push(line);
|
|
14038
|
+
}
|
|
14039
|
+
return out;
|
|
13751
14040
|
}
|
|
13752
14041
|
/** Flatten into per-row spans, merging adjacent cells with identical style. */
|
|
13753
14042
|
rows() {
|
|
@@ -13775,9 +14064,15 @@ var init_grid = __esm({
|
|
|
13775
14064
|
|
|
13776
14065
|
// src/ui/screens/Dashboard.tsx
|
|
13777
14066
|
import { Box, Text } from "ink";
|
|
14067
|
+
import { useRef } from "react";
|
|
13778
14068
|
import { jsx } from "react/jsx-runtime";
|
|
13779
14069
|
function Dashboard({ state, width, rows }) {
|
|
13780
|
-
const
|
|
14070
|
+
const gridRef = useRef(void 0);
|
|
14071
|
+
if (gridRef.current == null || gridRef.current.w !== width || gridRef.current.h !== rows) {
|
|
14072
|
+
gridRef.current = new Grid(width, rows);
|
|
14073
|
+
}
|
|
14074
|
+
const g = gridRef.current;
|
|
14075
|
+
g.clear();
|
|
13781
14076
|
drawDashboard(g, state);
|
|
13782
14077
|
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", width, height: rows, children: g.ansiRows().map((row, y) => /* @__PURE__ */ jsx(Text, { children: row }, y)) });
|
|
13783
14078
|
}
|
|
@@ -14109,14 +14404,14 @@ var init_session_recorder = __esm({
|
|
|
14109
14404
|
});
|
|
14110
14405
|
|
|
14111
14406
|
// src/ui/artifacts/reader.ts
|
|
14112
|
-
import { open, readFile as
|
|
14407
|
+
import { open, readFile as readFile24, stat as stat2 } from "fs/promises";
|
|
14113
14408
|
async function readForLive(absPath) {
|
|
14114
14409
|
try {
|
|
14115
14410
|
const info = await stat2(absPath);
|
|
14116
14411
|
if (!info.isFile()) return void 0;
|
|
14117
14412
|
const kind = kindOf(absPath);
|
|
14118
14413
|
if (info.size <= LARGE_BYTES) {
|
|
14119
|
-
const text2 = await
|
|
14414
|
+
const text2 = await readFile24(absPath, "utf-8");
|
|
14120
14415
|
return { text: text2, kind, truncated: false };
|
|
14121
14416
|
}
|
|
14122
14417
|
const fh = await open(absPath, "r");
|
|
@@ -14410,8 +14705,8 @@ ensureSupportedNode();
|
|
|
14410
14705
|
|
|
14411
14706
|
// src/index.ts
|
|
14412
14707
|
init_submit();
|
|
14413
|
-
import { readFile as
|
|
14414
|
-
import { join as
|
|
14708
|
+
import { readFile as readFile25, writeFile as writeFile16 } from "fs/promises";
|
|
14709
|
+
import { join as join36 } from "path";
|
|
14415
14710
|
|
|
14416
14711
|
// src/agents/04-recipe-builder/sdk-command.ts
|
|
14417
14712
|
init_esm_shims();
|
|
@@ -15499,49 +15794,7 @@ function wrap(text2, width) {
|
|
|
15499
15794
|
init_interrupt();
|
|
15500
15795
|
init_logs();
|
|
15501
15796
|
init_model();
|
|
15502
|
-
|
|
15503
|
-
// src/core/output.ts
|
|
15504
|
-
init_esm_shims();
|
|
15505
|
-
init_debug();
|
|
15506
|
-
import { mkdir, rm, writeFile as writeFile2 } from "fs/promises";
|
|
15507
|
-
import { homedir as homedir3 } from "os";
|
|
15508
|
-
import { join as join10 } from "path";
|
|
15509
|
-
var AUTONOMA_HOME3 = join10(homedir3(), ".autonoma");
|
|
15510
|
-
var WRITE_PROBE_FILE = ".write-probe";
|
|
15511
|
-
function getOutputDir(projectSlug) {
|
|
15512
|
-
return join10(AUTONOMA_HOME3, projectSlug);
|
|
15513
|
-
}
|
|
15514
|
-
async function clearOutputDir(projectSlug) {
|
|
15515
|
-
const dir = getOutputDir(projectSlug);
|
|
15516
|
-
await rm(dir, { recursive: true, force: true });
|
|
15517
|
-
await mkdir(dir, { recursive: true });
|
|
15518
|
-
debugLog("Cleared the run directory for a fresh start", { dir });
|
|
15519
|
-
}
|
|
15520
|
-
function displayPath(absPath) {
|
|
15521
|
-
const home = homedir3();
|
|
15522
|
-
return absPath.startsWith(home) ? `~${absPath.slice(home.length)}` : absPath;
|
|
15523
|
-
}
|
|
15524
|
-
async function ensureOutputDir(projectSlug) {
|
|
15525
|
-
const dir = getOutputDir(projectSlug);
|
|
15526
|
-
await mkdir(dir, { recursive: true });
|
|
15527
|
-
const probe = join10(dir, WRITE_PROBE_FILE);
|
|
15528
|
-
try {
|
|
15529
|
-
await writeFile2(probe, "ok", "utf-8");
|
|
15530
|
-
} catch (err) {
|
|
15531
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
15532
|
-
throw new Error(
|
|
15533
|
-
`Cannot write to ${displayPath(dir)} - the planner keeps every artifact it generates there.
|
|
15534
|
-
${reason}
|
|
15535
|
-
Free up disk space or fix the directory's permissions, then re-run.`
|
|
15536
|
-
);
|
|
15537
|
-
}
|
|
15538
|
-
await rm(probe, { force: true }).catch((err) => {
|
|
15539
|
-
debugLog("Could not remove the write probe", { probe, err });
|
|
15540
|
-
});
|
|
15541
|
-
return dir;
|
|
15542
|
-
}
|
|
15543
|
-
|
|
15544
|
-
// src/index.ts
|
|
15797
|
+
init_output();
|
|
15545
15798
|
init_session();
|
|
15546
15799
|
init_ui_lifecycle();
|
|
15547
15800
|
init_env();
|
|
@@ -15550,8 +15803,8 @@ init_prompts();
|
|
|
15550
15803
|
// src/core/git.ts
|
|
15551
15804
|
init_esm_shims();
|
|
15552
15805
|
import { execFile } from "child_process";
|
|
15553
|
-
import { readFile as
|
|
15554
|
-
import { join as
|
|
15806
|
+
import { readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
15807
|
+
import { join as join12 } from "path";
|
|
15555
15808
|
import { promisify } from "util";
|
|
15556
15809
|
var execFileAsync = promisify(execFile);
|
|
15557
15810
|
var GIT_INFO_FILE = ".git-info.json";
|
|
@@ -15575,11 +15828,11 @@ async function readGitInfo(projectRoot) {
|
|
|
15575
15828
|
};
|
|
15576
15829
|
}
|
|
15577
15830
|
async function saveGitInfo(outputDir, info) {
|
|
15578
|
-
await
|
|
15831
|
+
await writeFile4(join12(outputDir, GIT_INFO_FILE), JSON.stringify(info, null, 2), "utf-8");
|
|
15579
15832
|
}
|
|
15580
15833
|
async function loadGitInfo(outputDir) {
|
|
15581
15834
|
try {
|
|
15582
|
-
const raw = await
|
|
15835
|
+
const raw = await readFile5(join12(outputDir, GIT_INFO_FILE), "utf-8");
|
|
15583
15836
|
const parsed = JSON.parse(raw);
|
|
15584
15837
|
if (typeof parsed === "object" && parsed != null && "sha" in parsed && typeof parsed.sha === "string") {
|
|
15585
15838
|
const branch = "branch" in parsed && typeof parsed.branch === "string" ? parsed.branch : void 0;
|
|
@@ -15601,15 +15854,15 @@ init_state();
|
|
|
15601
15854
|
init_esm_shims();
|
|
15602
15855
|
init_prompts();
|
|
15603
15856
|
init_debug();
|
|
15604
|
-
import { readFile as
|
|
15605
|
-
import { join as
|
|
15857
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
15858
|
+
import { join as join14 } from "path";
|
|
15606
15859
|
init_test_files();
|
|
15607
15860
|
var ARTIFACT_FILES = ["AUTONOMA.md", "scenarios.md", "entity-audit.md"];
|
|
15608
15861
|
async function readArtifacts(outputDir) {
|
|
15609
15862
|
const files = await Promise.all(
|
|
15610
15863
|
ARTIFACT_FILES.map(async (name) => {
|
|
15611
15864
|
try {
|
|
15612
|
-
return { name, content: await
|
|
15865
|
+
return { name, content: await readFile7(join14(outputDir, name), "utf-8") };
|
|
15613
15866
|
} catch (err) {
|
|
15614
15867
|
debugLog(`Artifact ${name} not on disk; skipping upload`, { err });
|
|
15615
15868
|
return void 0;
|
|
@@ -15625,7 +15878,7 @@ async function readTestCases(outputDir) {
|
|
|
15625
15878
|
const segments = testPath.split("/").slice(1);
|
|
15626
15879
|
const name = segments[segments.length - 1];
|
|
15627
15880
|
const folder = segments.slice(0, -1).join("/");
|
|
15628
|
-
const content = await
|
|
15881
|
+
const content = await readFile7(join14(outputDir, testPath), "utf-8");
|
|
15629
15882
|
return { name, content, folder: folder.length > 0 ? folder : void 0 };
|
|
15630
15883
|
})
|
|
15631
15884
|
);
|
|
@@ -15690,11 +15943,11 @@ process.setSourceMapsEnabled(true);
|
|
|
15690
15943
|
var PAGES_FILE = "pages.json";
|
|
15691
15944
|
async function savePages(outputDir, pages) {
|
|
15692
15945
|
const obj = Object.fromEntries(pages);
|
|
15693
|
-
await
|
|
15946
|
+
await writeFile16(join36(outputDir, PAGES_FILE), JSON.stringify(obj, null, 2), "utf-8");
|
|
15694
15947
|
}
|
|
15695
15948
|
async function loadPages(outputDir) {
|
|
15696
15949
|
try {
|
|
15697
|
-
const raw = await
|
|
15950
|
+
const raw = await readFile25(join36(outputDir, PAGES_FILE), "utf-8");
|
|
15698
15951
|
const obj = JSON.parse(raw);
|
|
15699
15952
|
return new Map(Object.entries(obj));
|
|
15700
15953
|
} catch {
|
|
@@ -15745,14 +15998,32 @@ async function resolveScopeSelection(map, config, nonInteractive) {
|
|
|
15745
15998
|
return pickDefaultSelection(map);
|
|
15746
15999
|
}
|
|
15747
16000
|
async function promptScopeSelection(map) {
|
|
16001
|
+
const onlyFrontend = map.frontends.length === 1 ? map.frontends[0] : void 0;
|
|
16002
|
+
const onlyBackend = map.backends.length === 1 ? map.backends[0] : void 0;
|
|
16003
|
+
if (onlyFrontend != null) {
|
|
16004
|
+
log.info(`Planning tests for ${onlyFrontend.path} [${onlyFrontend.framework}] - the only frontend found.`);
|
|
16005
|
+
if (map.backends.length === 0) return { frontend: onlyFrontend.path, backends: [] };
|
|
16006
|
+
if (onlyBackend != null) {
|
|
16007
|
+
log.info(`Using ${onlyBackend.path} - the only backend found.`);
|
|
16008
|
+
return { frontend: onlyFrontend.path, backends: [onlyBackend.path] };
|
|
16009
|
+
}
|
|
16010
|
+
}
|
|
15748
16011
|
while (true) {
|
|
15749
|
-
const frontend = await select({
|
|
16012
|
+
const frontend = onlyFrontend?.path ?? await select({
|
|
15750
16013
|
message: "Which frontend do you want to plan tests for?",
|
|
15751
16014
|
detail: "One frontend per run - the whole test suite is planned against it.",
|
|
15752
|
-
options: map.frontends.map((f) => ({
|
|
16015
|
+
options: map.frontends.map((f) => ({
|
|
16016
|
+
value: f.path,
|
|
16017
|
+
label: `${f.path} [${f.framework}]`,
|
|
16018
|
+
hint: f.why
|
|
16019
|
+
}))
|
|
15753
16020
|
});
|
|
15754
16021
|
if (isCancel(frontend)) continue;
|
|
15755
16022
|
if (map.backends.length === 0) return { frontend, backends: [] };
|
|
16023
|
+
if (onlyBackend != null) {
|
|
16024
|
+
log.info(`Using ${onlyBackend.path} - the only backend found.`);
|
|
16025
|
+
return { frontend, backends: [onlyBackend.path] };
|
|
16026
|
+
}
|
|
15756
16027
|
const needed = defaultBackendsFor(map, frontend);
|
|
15757
16028
|
const backends = await multiselect({
|
|
15758
16029
|
message: "Which backends does this frontend talk to?",
|
|
@@ -16117,6 +16388,7 @@ async function main() {
|
|
|
16117
16388
|
const projectArg = args.value("project");
|
|
16118
16389
|
const resumeCommand = `autonoma-planner --resume` + (projectArg != null ? ` --project ${projectArg}` : "");
|
|
16119
16390
|
let mountedUi;
|
|
16391
|
+
let stoppedAt;
|
|
16120
16392
|
installInterruptHandler({
|
|
16121
16393
|
// exitCode defaults to 0 for a user-initiated Ctrl+C (progress saved, a clean stop);
|
|
16122
16394
|
// an external SIGTERM/SIGHUP passes the conventional 143/129 so the flushed exit still
|
|
@@ -16290,9 +16562,11 @@ All of it runs from this one command - there is nothing to launch step by step.`
|
|
|
16290
16562
|
const step = steps[i];
|
|
16291
16563
|
state = await runStepWithRecovery(step, outputDir, state, config, nonInteractive);
|
|
16292
16564
|
if (state.steps[step] === "paused") {
|
|
16565
|
+
stoppedAt = { label: STEP_LABELS[step], kind: "paused" };
|
|
16293
16566
|
break;
|
|
16294
16567
|
}
|
|
16295
16568
|
if (state.steps[step] === "failed") {
|
|
16569
|
+
stoppedAt = { label: STEP_LABELS[step], kind: "failed" };
|
|
16296
16570
|
log.error("Pipeline stopped due to failure.");
|
|
16297
16571
|
log.warn(`Your progress is saved. To retry this step, run:
|
|
16298
16572
|
${resumeCommand}`);
|
|
@@ -16352,6 +16626,11 @@ All of it runs from this one command - there is nothing to launch step by step.`
|
|
|
16352
16626
|
log.info(`Saved in ${displayPath(outputDir)}`);
|
|
16353
16627
|
if (finish != null) for (const line of describeFinishPhase(finish)) log.info(line);
|
|
16354
16628
|
log.info(nextStepLine(finish, nonInteractive));
|
|
16629
|
+
} else if (stoppedAt != null) {
|
|
16630
|
+
const verb = stoppedAt.kind === "paused" ? "paused at" : "stopped at";
|
|
16631
|
+
log.warn(`The run ${verb} "${stoppedAt.label}" and did not finish.`);
|
|
16632
|
+
log.info(`Your progress is saved. Continue with:
|
|
16633
|
+
${resumeCommand}`);
|
|
16355
16634
|
}
|
|
16356
16635
|
outro("Done");
|
|
16357
16636
|
}
|