builder-assistant-engineer 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +250 -0
- package/dist/analyst/prompt.js +19 -0
- package/dist/analyst/retry.js +38 -0
- package/dist/artifacts/diff.js +47 -0
- package/dist/artifacts/lessons.js +32 -0
- package/dist/artifacts/merge.js +70 -0
- package/dist/artifacts/write.js +44 -0
- package/dist/backends/agent-cli.js +174 -0
- package/dist/backends/api.js +171 -0
- package/dist/backends/index.js +10 -0
- package/dist/backends/manual.js +52 -0
- package/dist/backends/sse.js +24 -0
- package/dist/backends/types.js +1 -0
- package/dist/bin.js +3 -0
- package/dist/cli.js +104 -0
- package/dist/commands/context.js +12 -0
- package/dist/commands/init.js +199 -0
- package/dist/commands/next.js +103 -0
- package/dist/commands/plan.js +71 -0
- package/dist/commands/replan.js +37 -0
- package/dist/commands/review.js +37 -0
- package/dist/commands/shared.js +40 -0
- package/dist/commands/status.js +95 -0
- package/dist/config/commands.js +85 -0
- package/dist/config/schema.js +43 -0
- package/dist/config/settings.js +8 -0
- package/dist/config/store.js +38 -0
- package/dist/core/bash.js +105 -0
- package/dist/core/errors.js +14 -0
- package/dist/core/fs.js +35 -0
- package/dist/core/git.js +104 -0
- package/dist/core/gitignore.js +14 -0
- package/dist/core/json.js +13 -0
- package/dist/core/paths.js +14 -0
- package/dist/core/process.js +56 -0
- package/dist/core/prompt-loader.js +27 -0
- package/dist/core/state.js +27 -0
- package/dist/core/which.js +24 -0
- package/dist/detect/agents.js +17 -0
- package/dist/detect/mode.js +11 -0
- package/dist/digest/baseline.js +192 -0
- package/dist/digest/budget.js +31 -0
- package/dist/digest/files.js +167 -0
- package/dist/digest/format.js +18 -0
- package/dist/digest/git.js +19 -0
- package/dist/digest/index.js +100 -0
- package/dist/digest/manifests.js +133 -0
- package/dist/digest/read.js +28 -0
- package/dist/digest/redact.js +37 -0
- package/dist/digest/render.js +138 -0
- package/dist/digest/select.js +116 -0
- package/dist/digest/todos.js +16 -0
- package/dist/digest/tree.js +55 -0
- package/dist/digest/walk.js +72 -0
- package/dist/gates/capture.js +145 -0
- package/dist/gates/contract.js +339 -0
- package/dist/gates/enforce.js +143 -0
- package/dist/gates/findings.js +17 -0
- package/dist/gates/gate.js +315 -0
- package/dist/gates/ignore-rules.js +104 -0
- package/dist/gates/protected-files.js +311 -0
- package/dist/gates/regression.js +219 -0
- package/dist/gates/reports.js +349 -0
- package/dist/gates/results.js +295 -0
- package/dist/gates/runners.js +189 -0
- package/dist/gates/state-guard.js +56 -0
- package/dist/i18n/en.js +288 -0
- package/dist/i18n/es.js +288 -0
- package/dist/i18n/index.js +18 -0
- package/dist/interview/adaptive.js +69 -0
- package/dist/interview/brief.js +27 -0
- package/dist/interview/questions.js +18 -0
- package/dist/interview/render.js +19 -0
- package/dist/interview/reply.js +34 -0
- package/dist/next/attempts.js +151 -0
- package/dist/next/start.js +179 -0
- package/dist/plan/continuation.js +40 -0
- package/dist/plan/evidence.js +239 -0
- package/dist/plan/filter.js +20 -0
- package/dist/plan/parser.js +283 -0
- package/dist/plan/prior.js +32 -0
- package/dist/plan/questions.js +48 -0
- package/dist/plan/repair.js +33 -0
- package/dist/plan/report.js +37 -0
- package/dist/plan/run.js +191 -0
- package/dist/prompts/analyst.md +101 -0
- package/dist/prompts/continue.md +11 -0
- package/dist/prompts/fix-format.md +13 -0
- package/dist/prompts/fix-paths.md +23 -0
- package/dist/prompts/lesson.md +18 -0
- package/dist/prompts/retry.md +9 -0
- package/dist/prompts/review.md +36 -0
- package/dist/prompts/task.md +9 -0
- package/dist/review/changes.js +177 -0
- package/dist/review/diff.js +115 -0
- package/dist/review/integrity.js +150 -0
- package/dist/review/mechanical.js +62 -0
- package/dist/review/parse.js +40 -0
- package/dist/review/reviewer.js +23 -0
- package/dist/review/run.js +134 -0
- package/dist/review/scope.js +55 -0
- package/dist/review/secrets.js +163 -0
- package/dist/review/snapshot.js +38 -0
- package/dist/review/tests.js +140 -0
- package/dist/tasks/attempts.js +71 -0
- package/dist/tasks/checks.js +181 -0
- package/dist/tasks/frontmatter.js +58 -0
- package/dist/tasks/graph.js +27 -0
- package/dist/tasks/handoff.js +62 -0
- package/dist/tasks/learn.js +128 -0
- package/dist/tasks/load.js +37 -0
- package/dist/tasks/metrics.js +40 -0
- package/dist/tasks/runs.js +12 -0
- package/dist/tasks/schema.js +96 -0
- package/dist/tasks/select.js +15 -0
- package/dist/tasks/shell-words.js +176 -0
- package/dist/tasks/status.js +13 -0
- package/dist/tasks/verify.js +181 -0
- package/dist/ui/clack.js +46 -0
- package/dist/ui/prompter.js +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { readTextIfExists } from "../core/fs.js";
|
|
3
|
+
import { asRecord, parseObject } from "../core/json.js";
|
|
4
|
+
import { parseLine, program } from "../tasks/shell-words.js";
|
|
5
|
+
import { baseName, execTarget, mainScript, positional } from "./protected-files.js";
|
|
6
|
+
import { runnerHint } from "./results.js";
|
|
7
|
+
export const TEST_RUNNERS = new Set([
|
|
8
|
+
"vitest",
|
|
9
|
+
"jest",
|
|
10
|
+
"mocha",
|
|
11
|
+
"node",
|
|
12
|
+
"pytest",
|
|
13
|
+
"go",
|
|
14
|
+
"cargo",
|
|
15
|
+
"rspec",
|
|
16
|
+
"minitest",
|
|
17
|
+
"phpunit",
|
|
18
|
+
"dotnet",
|
|
19
|
+
"surefire",
|
|
20
|
+
"bun",
|
|
21
|
+
"deno",
|
|
22
|
+
"unittest",
|
|
23
|
+
"playwright",
|
|
24
|
+
]);
|
|
25
|
+
const MAKE = /^(g?make|\$[({]MAKE[)}])$/;
|
|
26
|
+
const MAKEFILES = ["GNUmakefile", "makefile", "Makefile"];
|
|
27
|
+
const PYTHONS = new Set(["python", "python3", "py"]);
|
|
28
|
+
const SCRIPT_HOSTS = new Set(["node", "tsx", "bun"]);
|
|
29
|
+
const MANAGERS = new Set(["pnpm", "yarn", "bun"]);
|
|
30
|
+
const MAX_DEPTH = 5;
|
|
31
|
+
export async function readSources(cwd) {
|
|
32
|
+
const scripts = asRecord(parseObject((await readTextIfExists(join(cwd, "package.json"))) ?? "")?.scripts);
|
|
33
|
+
for (const name of MAKEFILES) {
|
|
34
|
+
const makefile = await readTextIfExists(join(cwd, name));
|
|
35
|
+
if (makefile !== undefined)
|
|
36
|
+
return { scripts, makefile };
|
|
37
|
+
}
|
|
38
|
+
return { scripts };
|
|
39
|
+
}
|
|
40
|
+
export function testRunners(command, sources) {
|
|
41
|
+
return runnerHint(expandCommand(command, sources)).filter((name) => TEST_RUNNERS.has(name));
|
|
42
|
+
}
|
|
43
|
+
export function expandCommand(command, sources) {
|
|
44
|
+
const seen = new Set();
|
|
45
|
+
const pending = [command];
|
|
46
|
+
const texts = [];
|
|
47
|
+
while (pending.length > 0) {
|
|
48
|
+
const current = pending.shift() ?? "";
|
|
49
|
+
if (seen.has(current))
|
|
50
|
+
continue;
|
|
51
|
+
seen.add(current);
|
|
52
|
+
texts.push(current);
|
|
53
|
+
for (const simple of parseLine(current).commands) {
|
|
54
|
+
const { name, args } = program(simple);
|
|
55
|
+
const wrapped = execTarget(name, args);
|
|
56
|
+
const [inner = name, ...rest] = wrapped ?? [name, ...args];
|
|
57
|
+
const script = mainScript(inner, rest, sources.scripts);
|
|
58
|
+
if (script && typeof sources.scripts[script] === "string") {
|
|
59
|
+
pending.push(sources.scripts[script]);
|
|
60
|
+
}
|
|
61
|
+
if (MAKE.test(inner) && sources.makefile) {
|
|
62
|
+
pending.push(...makeRecipes(sources.makefile, makeTargets(rest)));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return texts.join("\n");
|
|
67
|
+
}
|
|
68
|
+
export function directRunner(command, sources, depth = 0) {
|
|
69
|
+
const line = parseLine(command);
|
|
70
|
+
const [simple] = line.commands;
|
|
71
|
+
if (line.substitution || line.commands.length !== 1 || !simple || depth > MAX_DEPTH) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
const { name, args } = program(simple);
|
|
75
|
+
const found = runnerOf(name, args);
|
|
76
|
+
if (found)
|
|
77
|
+
return { ...found, suffix: (extra) => extra };
|
|
78
|
+
const script = mainScript(name, args, sources.scripts);
|
|
79
|
+
const text = script ? sources.scripts[script] : undefined;
|
|
80
|
+
if (typeof text !== "string")
|
|
81
|
+
return undefined;
|
|
82
|
+
const inner = directRunner(text, sources, depth + 1);
|
|
83
|
+
if (!inner)
|
|
84
|
+
return undefined;
|
|
85
|
+
const separator = name === "npm" && !args.includes("--") ? ["--"] : [];
|
|
86
|
+
return { ...inner, suffix: (extra) => [...separator, ...inner.suffix(extra)] };
|
|
87
|
+
}
|
|
88
|
+
export function makeTargets(args) {
|
|
89
|
+
const targets = [];
|
|
90
|
+
for (let index = 0; index < args.length; index++) {
|
|
91
|
+
const arg = args[index] ?? "";
|
|
92
|
+
if (["-f", "-C", "--file", "--directory", "-I", "-j", "-l", "-o", "-W"].includes(arg)) {
|
|
93
|
+
index++;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (!arg.startsWith("-") && !arg.includes("="))
|
|
97
|
+
targets.push(arg);
|
|
98
|
+
}
|
|
99
|
+
return targets;
|
|
100
|
+
}
|
|
101
|
+
export function makeRecipes(text, targets) {
|
|
102
|
+
const rules = parseMakefile(text);
|
|
103
|
+
const wanted = targets.length > 0 ? targets : rules.first ? [rules.first] : [];
|
|
104
|
+
const seen = new Set();
|
|
105
|
+
const lines = [];
|
|
106
|
+
const visit = (target) => {
|
|
107
|
+
if (seen.has(target))
|
|
108
|
+
return;
|
|
109
|
+
seen.add(target);
|
|
110
|
+
const rule = rules.targets.get(target);
|
|
111
|
+
if (!rule)
|
|
112
|
+
return;
|
|
113
|
+
for (const prerequisite of rule.prerequisites)
|
|
114
|
+
visit(prerequisite);
|
|
115
|
+
lines.push(...rule.recipe);
|
|
116
|
+
};
|
|
117
|
+
for (const target of wanted)
|
|
118
|
+
visit(target);
|
|
119
|
+
return lines;
|
|
120
|
+
}
|
|
121
|
+
function parseMakefile(text) {
|
|
122
|
+
const targets = new Map();
|
|
123
|
+
let first;
|
|
124
|
+
let current = [];
|
|
125
|
+
for (const line of text.replace(/\r\n?/g, "\n").split("\n")) {
|
|
126
|
+
if (line.startsWith("\t")) {
|
|
127
|
+
const recipe = line.trim().replace(/^[@+-]+/, "");
|
|
128
|
+
if (recipe)
|
|
129
|
+
for (const rule of current)
|
|
130
|
+
rule.recipe.push(recipe);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const match = /^([^\s:#=][^:=]*?)\s*::?(?!=)\s*([^;#]*)(?:;(.*))?$/.exec(line);
|
|
134
|
+
if (!match) {
|
|
135
|
+
if (line.trim() !== "" && !line.trim().startsWith("#"))
|
|
136
|
+
current = [];
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const names = (match[1] ?? "").split(/\s+/).filter(Boolean);
|
|
140
|
+
const prerequisites = (match[2] ?? "").split(/\s+/).filter((word) => word && word !== "|");
|
|
141
|
+
current = names.map((name) => {
|
|
142
|
+
const rule = targets.get(name) ?? { prerequisites: [], recipe: [] };
|
|
143
|
+
rule.prerequisites.push(...prerequisites);
|
|
144
|
+
if (match[3]?.trim())
|
|
145
|
+
rule.recipe.push(match[3].trim());
|
|
146
|
+
targets.set(name, rule);
|
|
147
|
+
return rule;
|
|
148
|
+
});
|
|
149
|
+
first ??= names.find((name) => !name.startsWith("."));
|
|
150
|
+
}
|
|
151
|
+
return { targets, ...(first ? { first } : {}) };
|
|
152
|
+
}
|
|
153
|
+
function runnerOf(name, args) {
|
|
154
|
+
const wrapped = execTarget(name, args);
|
|
155
|
+
if (wrapped)
|
|
156
|
+
return runnerOf(wrapped[0] ?? "", wrapped.slice(1));
|
|
157
|
+
const tool = baseName(name.replace(/\\/g, "/")).replace(/\.(c?js|mjs|cmd|exe|ps1)$/i, "");
|
|
158
|
+
const [first] = positional(args);
|
|
159
|
+
const module = args.indexOf("-m");
|
|
160
|
+
if (tool === "vitest")
|
|
161
|
+
return { runner: "vitest", args };
|
|
162
|
+
if (tool === "jest")
|
|
163
|
+
return { runner: "jest", args };
|
|
164
|
+
if ((tool === "react-scripts" || tool === "craco") && first === "test") {
|
|
165
|
+
return { runner: "jest", args };
|
|
166
|
+
}
|
|
167
|
+
if (tool === "pytest" || tool === "py.test")
|
|
168
|
+
return { runner: "pytest", args };
|
|
169
|
+
if (PYTHONS.has(tool) && module !== -1 && args[module + 1] === "pytest") {
|
|
170
|
+
return { runner: "pytest", args };
|
|
171
|
+
}
|
|
172
|
+
if (tool === "go" && first === "test")
|
|
173
|
+
return { runner: "go", args };
|
|
174
|
+
if (tool === "cargo" && first === "test")
|
|
175
|
+
return { runner: "cargo", args };
|
|
176
|
+
if (tool === "dotnet" && first === "test")
|
|
177
|
+
return { runner: "dotnet", args };
|
|
178
|
+
if (SCRIPT_HOSTS.has(tool)) {
|
|
179
|
+
const script = args.find((arg) => !arg.startsWith("-"));
|
|
180
|
+
const path = script?.replace(/\\/g, "/");
|
|
181
|
+
if (script && path && /(^|\/)(vitest|jest)(\.[cm]?js)?$|\/(vitest|jest)\/bin\//.test(path)) {
|
|
182
|
+
return runnerOf(script, args.slice(args.indexOf(script) + 1));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (MANAGERS.has(tool) && first && !["run", "test", "exec", "dlx", "x"].includes(first)) {
|
|
186
|
+
return runnerOf(first, args.slice(args.indexOf(first) + 1));
|
|
187
|
+
}
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
|
+
import { repoState } from "../core/state.js";
|
|
4
|
+
const GUARDED = new Set(["capture.json", "attempts.jsonl", "lesson.md", "active.json"]);
|
|
5
|
+
export async function guardState(cwd) {
|
|
6
|
+
const root = repoState(cwd);
|
|
7
|
+
const before = await readGuarded(root);
|
|
8
|
+
return {
|
|
9
|
+
async verify() {
|
|
10
|
+
const after = await readGuarded(root);
|
|
11
|
+
const changed = [];
|
|
12
|
+
for (const [path, content] of before) {
|
|
13
|
+
const now = after.get(path);
|
|
14
|
+
if (now?.equals(content))
|
|
15
|
+
continue;
|
|
16
|
+
changed.push(relative(root, path).split("\\").join("/"));
|
|
17
|
+
await writeFile(path, content);
|
|
18
|
+
}
|
|
19
|
+
for (const path of after.keys()) {
|
|
20
|
+
if (before.has(path))
|
|
21
|
+
continue;
|
|
22
|
+
changed.push(relative(root, path).split("\\").join("/"));
|
|
23
|
+
await rm(path, { force: true });
|
|
24
|
+
}
|
|
25
|
+
return changed.sort();
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
async function readGuarded(root) {
|
|
30
|
+
const files = new Map();
|
|
31
|
+
for (const path of await walk(root)) {
|
|
32
|
+
const name = path.split(/[\\/]/).at(-1) ?? "";
|
|
33
|
+
if (!GUARDED.has(name))
|
|
34
|
+
continue;
|
|
35
|
+
const content = await readFile(path).catch(() => undefined);
|
|
36
|
+
if (content)
|
|
37
|
+
files.set(path, content);
|
|
38
|
+
}
|
|
39
|
+
return files;
|
|
40
|
+
}
|
|
41
|
+
async function walk(dir) {
|
|
42
|
+
let entries;
|
|
43
|
+
try {
|
|
44
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
const nested = await Promise.all(entries.map((entry) => {
|
|
50
|
+
const path = join(dir, entry.name);
|
|
51
|
+
if (entry.isDirectory())
|
|
52
|
+
return walk(path);
|
|
53
|
+
return Promise.resolve(entry.isFile() ? [path] : []);
|
|
54
|
+
}));
|
|
55
|
+
return nested.flat();
|
|
56
|
+
}
|
package/dist/i18n/en.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
export const en = {
|
|
2
|
+
"program.description": "Turn an idea or an existing repo into an execution plan for console AI agents.",
|
|
3
|
+
"option.backend": "AI backend to use",
|
|
4
|
+
"option.lang": "language for the CLI and the generated artifacts",
|
|
5
|
+
"option.dryRun": "show exactly what would be sent to the AI without writing anything",
|
|
6
|
+
"option.yes": "accept every confirmation",
|
|
7
|
+
"option.help": "show help",
|
|
8
|
+
"option.version": "show version",
|
|
9
|
+
"option.only": "generate only one group of artifacts",
|
|
10
|
+
"option.headless": "run the agent without an interactive session (accept edits, never bypass)",
|
|
11
|
+
"command.init": "detect the project, choose backend, agents and language, and run the interview",
|
|
12
|
+
"command.plan": "analyze the repo and generate the plan, memory and agents",
|
|
13
|
+
"command.next": "run the next pending task, verify and review it, and mark it done",
|
|
14
|
+
"command.status": "show phases, tasks and progress",
|
|
15
|
+
"command.replan": "re-analyze the repo, keep finished work and update pending tasks",
|
|
16
|
+
"command.review": "review a task's diff against its acceptance criteria and AGENTS.md",
|
|
17
|
+
"command.help": "show help for a command",
|
|
18
|
+
"option.brief": "comma-separated files with your brief or docs (skips pasting)",
|
|
19
|
+
"ui.cancelled": "Cancelled.",
|
|
20
|
+
"format.retrying": "The answer did not follow the output format; asking once more to fix only the format.",
|
|
21
|
+
"format.failed": "The answer still does not follow the output format ({{details}}). Raw output saved to {{path}}.",
|
|
22
|
+
"init.intro": "builder-assistant-engineer · setup",
|
|
23
|
+
"init.lang": "Language for the CLI and the generated plan",
|
|
24
|
+
"init.mode": "Project type",
|
|
25
|
+
"init.detected": "detected",
|
|
26
|
+
"init.backend": "Which AI should run the analysis?",
|
|
27
|
+
"init.backendMissing": "`{{command}}` was not found in PATH; install it before running plan.",
|
|
28
|
+
"init.targets": "Which agents will work on this repo?",
|
|
29
|
+
"init.redoInterview": "An interview already exists in .bae/interview.md. Redo it?",
|
|
30
|
+
"init.briefMissing": "Could not read the brief file(s): {{path}}",
|
|
31
|
+
"init.dryRunNoAi": "Dry run: nothing would be sent to the AI during init.",
|
|
32
|
+
"init.dryRunDone": "Dry run finished. Nothing was written.",
|
|
33
|
+
"init.done": "Setup saved in .bae/. Next: {{command}}",
|
|
34
|
+
"mode.greenfield": "Greenfield — new project, little or no code yet",
|
|
35
|
+
"mode.brownfield": "Brownfield — existing codebase",
|
|
36
|
+
"backend.label.manual": "Manual (copy and paste)",
|
|
37
|
+
"backend.hint.installed": "installed",
|
|
38
|
+
"backend.hint.missing": "not found in PATH",
|
|
39
|
+
"backend.hint.api": "Anthropic or OpenAI-compatible key via environment variables",
|
|
40
|
+
"backend.hint.apiReady": "credentials found in the environment",
|
|
41
|
+
"backend.hint.manual": "works with any AI, even a web chat",
|
|
42
|
+
"interview.intro": "Interview — press Enter to skip any question.",
|
|
43
|
+
"interview.brief": "Brief: paste a one-line summary, or paths to files with your brief or docs (comma-separated)",
|
|
44
|
+
"interview.briefPlaceholder": "e.g. docs/brief.md, notes/idea.md",
|
|
45
|
+
"interview.briefLoaded": "Loaded {{count}} file(s): {{paths}}",
|
|
46
|
+
"interview.objective": "What is the goal for this repo?",
|
|
47
|
+
"interview.q.what": "What do you want to build, and for whom?",
|
|
48
|
+
"interview.q.problem": "What problem does it solve, and how will you know it works?",
|
|
49
|
+
"interview.q.scope": "What is in the first deliverable, and what stays out?",
|
|
50
|
+
"interview.q.constraints": "Constraints: required stack, deadlines, platforms, integrations, budget?",
|
|
51
|
+
"interview.q.team": "Who will execute the plan (AI only, you + AI, a team), and at what level?",
|
|
52
|
+
"interview.q.rules": "Preferences and prohibitions: conventions, parts of the repo that must not be touched?",
|
|
53
|
+
"interview.followUps": "The analyst may ask up to {{max}} follow-up questions.",
|
|
54
|
+
"interview.thinking": "Thinking about the next question…",
|
|
55
|
+
"interview.other": "Other (type it)",
|
|
56
|
+
"interview.skip": "Skip",
|
|
57
|
+
"interview.answer": "Your answer",
|
|
58
|
+
"interview.adaptiveFailed": "Skipping the remaining follow-up questions: {{details}}",
|
|
59
|
+
"interview.adaptiveManual": "Follow-up questions are skipped with the manual backend.",
|
|
60
|
+
"objective.feature": "Build a new feature",
|
|
61
|
+
"objective.refactor": "Refactor",
|
|
62
|
+
"objective.migration": "Migrate (framework, language, infrastructure)",
|
|
63
|
+
"objective.bugs": "Fix bugs",
|
|
64
|
+
"objective.docs": "Document the codebase",
|
|
65
|
+
"md.interview": "Interview",
|
|
66
|
+
"md.mode": "Mode",
|
|
67
|
+
"md.objective": "Goal",
|
|
68
|
+
"md.brief": "Brief",
|
|
69
|
+
"md.followUps": "Follow-up questions",
|
|
70
|
+
"md.summary": "Summary",
|
|
71
|
+
"md.skipped": "skipped",
|
|
72
|
+
"config.missing": "No configuration found in .bae/config.json. Run {{command}} first.",
|
|
73
|
+
"digest.reading": "Reading the repository…",
|
|
74
|
+
"plan.intro": "builder-assistant-engineer · plan",
|
|
75
|
+
"plan.noInterview": "No interview in .bae/interview.md; planning from the repository alone.",
|
|
76
|
+
"plan.analyzing": "The analyst is building the plan (this can take several minutes)",
|
|
77
|
+
"plan.progress": "Receiving the plan… {{chars}} characters",
|
|
78
|
+
"plan.existingPlan": "A plan already exists in docs/plan/tasks. Regenerate it from scratch? (replan keeps progress)",
|
|
79
|
+
"plan.useReplan": "Nothing changed. Use replan to update the plan and keep finished work.",
|
|
80
|
+
"plan.dryRunDone": "Dry run: the prompt above is exactly what would be sent. Nothing was written.",
|
|
81
|
+
"plan.continuing": "The answer was cut off at {{marker}}; asking the analyst to continue from there.",
|
|
82
|
+
"plan.summary": "Summary",
|
|
83
|
+
"plan.blocking": "blocking",
|
|
84
|
+
"plan.done": "{{count}} file(s) written. Next: {{next}}",
|
|
85
|
+
"plan.nothingWritten": "No files were written.",
|
|
86
|
+
"artifacts.changes": "Changes",
|
|
87
|
+
"artifacts.confirm": "Write {{count}} file(s)?",
|
|
88
|
+
"artifacts.all": "Write all",
|
|
89
|
+
"artifacts.each": "Review one by one",
|
|
90
|
+
"artifacts.none": "Write nothing",
|
|
91
|
+
"artifacts.confirmOne": "Write {{path}}?",
|
|
92
|
+
"artifacts.new": "new, {{lines}} lines",
|
|
93
|
+
"artifacts.updated": "+{{added}} −{{removed}}",
|
|
94
|
+
"artifacts.unchanged": "unchanged",
|
|
95
|
+
"artifacts.keptDone": "done task, kept as is",
|
|
96
|
+
"tasks.invalid": "Ignoring an invalid task file: {{error}}",
|
|
97
|
+
"next.intro": "builder-assistant-engineer · next",
|
|
98
|
+
"next.meta": "phase {{phase}} · size {{size}} · risk {{risk}} · depends on {{deps}}",
|
|
99
|
+
"next.dryRunDone": "Dry run: the task above is exactly what the agent would receive. Nothing changed.",
|
|
100
|
+
"next.headlessNeedsAgent": "--headless needs an agent CLI backend (claude, opencode, codex, gemini); running the manual flow instead.",
|
|
101
|
+
"next.launching": "Opening {{backend}} with the task. Exit the session when the task is finished.",
|
|
102
|
+
"next.attempt": "Attempt {{attempt}} of {{max}} with {{backend}} (headless, accept edits)",
|
|
103
|
+
"next.done": "{{id}} is done. Next: {{command}}",
|
|
104
|
+
"next.notDone": "{{id}} stays in progress. Fix it and run {{command}} again.",
|
|
105
|
+
"next.blocked": "{{id}} is blocked: {{reason}} Logs: {{path}}",
|
|
106
|
+
"next.noPlan": "There are no tasks yet. Run {{command}} first.",
|
|
107
|
+
"next.allDone": "Every task is done.",
|
|
108
|
+
"next.nothingReady": "No task is ready",
|
|
109
|
+
"next.unblock": "Unblock or finish the tasks above, or run replan.",
|
|
110
|
+
"verify.commands": "Verification",
|
|
111
|
+
"verify.confirm": "Run these verification commands now?",
|
|
112
|
+
"verify.declined": "Verification was not run; the task stays in progress.",
|
|
113
|
+
"verify.none": "The task has no verification commands in a ```sh block under Verification.",
|
|
114
|
+
"verify.failed": "Verification failed: `{{command}}` exited with {{code}}.",
|
|
115
|
+
"verify.passed": "Verification passed.",
|
|
116
|
+
"verify.stillFailing": "`{{command}}` failed before the task and still fails (exit {{code}}), so it did not block. If an acceptance criterion needs it to pass, the task is not done.",
|
|
117
|
+
"review.intro": "builder-assistant-engineer · review",
|
|
118
|
+
"review.running": "The reviewer is checking {{id}}…",
|
|
119
|
+
"review.findings": "Review findings",
|
|
120
|
+
"review.passed": "Review passed.",
|
|
121
|
+
"review.failed": "Review failed for {{id}}.",
|
|
122
|
+
"review.noGit": "Review skipped: this is not a git repository, so there is no diff to review.",
|
|
123
|
+
"review.noChanges": "The task changed no files and its checks pass, so the reviewer was not called.",
|
|
124
|
+
"review.dryRun": "Dry run: the review prompt above was not sent.",
|
|
125
|
+
"review.noTask": "No task is in progress. Pass a task id, e.g. review T-003.",
|
|
126
|
+
"review.unknownTask": "Task {{id}} was not found in docs/plan/tasks.",
|
|
127
|
+
"status.empty": "No plan yet. Run {{command}} first.",
|
|
128
|
+
"status.phase": "Phase {{phase}}",
|
|
129
|
+
"status.waiting": "waiting on {{ids}}",
|
|
130
|
+
"status.total": "{{done}}/{{total}} done ({{percent}}%)",
|
|
131
|
+
"status.next": "next: {{id}} {{title}}",
|
|
132
|
+
"status.noNext": "no task ready",
|
|
133
|
+
"replan.intro": "builder-assistant-engineer · replan",
|
|
134
|
+
"replan.noPlan": "There is no plan to update. Run {{command}} first.",
|
|
135
|
+
"replan.noChangelog": "The analyst did not write docs/plan/CHANGELOG.md.",
|
|
136
|
+
"artifacts.removed": "removed, no longer in the plan",
|
|
137
|
+
"argument.task": "task id, e.g. T-003 (defaults to the task in progress)",
|
|
138
|
+
"error.configJson": "{{path}} is not valid JSON: {{details}}",
|
|
139
|
+
"error.configInvalid": "{{path}} is invalid:\n{{details}}",
|
|
140
|
+
"error.promptMissingVars": "{{path}} uses variables that were not provided: {{vars}}",
|
|
141
|
+
"error.unexpected": "Unexpected error. Please report it with the output below.",
|
|
142
|
+
"backend.notInstalled": "`{{command}}` is not installed or not in PATH. Install it or pick another backend with --backend.",
|
|
143
|
+
"backend.timedOut": "`{{command}}` did not finish within {{minutes}} minutes and was stopped (agent.timeoutMinutes in .bae/config.json).",
|
|
144
|
+
"backend.failed": "`{{command}}` exited with code {{code}}:\n{{details}}",
|
|
145
|
+
"backend.apiNotConfigured": "The api backend needs ANTHROPIC_API_KEY, or OPENAI_BASE_URL/OPENAI_API_KEY for an OpenAI-compatible endpoint.",
|
|
146
|
+
"backend.apiModelRequired": "Set BAE_MODEL to the model to use with {{baseUrl}}.",
|
|
147
|
+
"backend.apiProviderInvalid": "BAE_API_PROVIDER must be anthropic or openai, not {{provider}}.",
|
|
148
|
+
"backend.apiHttp": "{{provider}} API error ({{status}}): {{details}}",
|
|
149
|
+
"backend.apiNetwork": "Could not reach {{url}}: {{details}}",
|
|
150
|
+
"backend.apiInteractive": "The api backend cannot run an interactive session.",
|
|
151
|
+
"backend.refusal": "The model declined the request.",
|
|
152
|
+
"backend.truncated": "The response hit the output token limit and is incomplete.",
|
|
153
|
+
"manual.copied": "Prompt copied to the clipboard and saved to {{path}}.",
|
|
154
|
+
"manual.notCopied": "Could not use the clipboard; the prompt is saved to {{path}}.",
|
|
155
|
+
"manual.awaitingResponse": "Paste it into your AI. Then paste the full answer here and finish with Ctrl-D (Ctrl-Z, Enter on Windows), or save it to {{path}} and press Enter.",
|
|
156
|
+
"manual.awaitingDone": "Run it with your agent and press Enter when the task is finished.",
|
|
157
|
+
"manual.emptyResponse": "No answer received. Paste it here or save it to {{path}}.",
|
|
158
|
+
"plan.commands": "Project commands saved to .bae/config.json",
|
|
159
|
+
"regression.title": "Regression check",
|
|
160
|
+
"regression.baselineTitle": "Regression baseline, before the task",
|
|
161
|
+
"regression.confirm": "Run the project's lint and test commands now to record the baseline?",
|
|
162
|
+
"regression.preexisting": "`{{command}}` already fails before the task (exit {{code}}); recorded as preexisting, it will not block.",
|
|
163
|
+
"regression.found": "Regression: `{{command}}` exits with {{code}} after the task.",
|
|
164
|
+
"regression.stillFailing": "`{{command}}` still fails (exit {{code}}), as it did before the task.",
|
|
165
|
+
"regression.passed": "Regression check passed.",
|
|
166
|
+
"evidence.retrying": "{{count}} cited path(s) are not in the repository and not marked (new); asking the analyst to fix only those.",
|
|
167
|
+
"evidence.fixing": "The analyst is fixing the cited paths…",
|
|
168
|
+
"evidence.fixed": "Every cited path now exists or is marked (new).",
|
|
169
|
+
"evidence.unverified": "{{count}} cited path(s) are still unverified; they are listed in the summary.",
|
|
170
|
+
"evidence.summary": "Unverified paths (not in the repository and not marked new):",
|
|
171
|
+
"mechanical.secretFile": "Looks like a secrets file (.env, private key or credentials); keep it out of the change.",
|
|
172
|
+
"mechanical.secretValue": "Adds what looks like a credential ({{kind}}); read it from the environment instead.",
|
|
173
|
+
"mechanical.noTests": "The task requires tests (tests: required), but it neither runs more tests than before nor adds assertions to a test file.",
|
|
174
|
+
"mechanical.outOfScope": "Changed outside the task's Scope: {{files}}",
|
|
175
|
+
"mechanical.failed": "The automatic checks failed, so the reviewer was not run.",
|
|
176
|
+
"plan.openQuestions": "Open questions, saved to .bae/interview.md; answer them there and run replan when you can",
|
|
177
|
+
"plan.questionsSaved": "{{count}} question(s) saved to .bae/interview.md.",
|
|
178
|
+
"plan.rerun": "You answered blocking questions. Run the plan again now with your answers?",
|
|
179
|
+
"plan.rerunning": "Running the plan again with your answers.",
|
|
180
|
+
"md.planQuestions": "Questions from the plan ({{date}})",
|
|
181
|
+
"md.why": "Why",
|
|
182
|
+
"md.options": "Options",
|
|
183
|
+
"md.blocking": "Blocking: the plan assumed an answer",
|
|
184
|
+
"md.answer": "Answer",
|
|
185
|
+
"md.open": "open, not answered yet",
|
|
186
|
+
"handoff.missing": "{{path}} has no handoff note: write at most {{max}} lines under ## Log (what changed, decisions, traps).",
|
|
187
|
+
"handoff.tooLong": "The handoff note in {{path}} has {{count}} lines; keep it to {{max}}.",
|
|
188
|
+
"handoff.passed": "Handoff note present.",
|
|
189
|
+
"md.lessons": "Lessons learned",
|
|
190
|
+
"lesson.asking": "{{id}} failed repeatedly; asking the agent for the root cause and a rule…",
|
|
191
|
+
"lesson.title": "Lesson from {{id}}",
|
|
192
|
+
"lesson.body": "Root cause: {{cause}}\nRule: {{rule}}",
|
|
193
|
+
"lesson.confirm": "Add this rule to AGENTS.md?",
|
|
194
|
+
"lesson.added": "Rule added to AGENTS.md.",
|
|
195
|
+
"lesson.skipped": "Rule not added; it stays in {{path}}.",
|
|
196
|
+
"lesson.failed": "Could not get a lesson from the agent: {{details}}",
|
|
197
|
+
"status.blockedReason": "{{id}} is blocked: {{reason}}",
|
|
198
|
+
"status.metrics": "Local metrics",
|
|
199
|
+
"status.attempts": "attempts: {{attempts}} over {{tasks}} task(s), {{average}} per task",
|
|
200
|
+
"status.firstAttempt": "done on the first attempt: {{count}}/{{tasks}} ({{percent}}%)",
|
|
201
|
+
"status.regressions": "regressions caught: {{count}}",
|
|
202
|
+
"status.time": "time per done task: {{average}} on average, {{total}} in total",
|
|
203
|
+
"status.taskRuns": "{{attempts}} attempt(s), {{time}}",
|
|
204
|
+
"format.repaired": "Repaired the answer locally: {{repairs}}.",
|
|
205
|
+
"evidence.retryFailed": "The request to fix the cited paths failed, so the plan keeps them as they are: {{details}}",
|
|
206
|
+
"regression.noCommands": "No lint or test command in .bae/config.json or the manifests; the regression check is off until you add them under commands.",
|
|
207
|
+
"option.acceptFinding": "accept one finding by its id (repeatable); only secrets, and contract or test findings on files the task's Scope lists",
|
|
208
|
+
"review.noBase": "Review failed: the commit recorded when the task started no longer exists, so the task's changes cannot be isolated.",
|
|
209
|
+
"review.noCapture": "{{id}} has no capture from next; reviewing it against the current HEAD.",
|
|
210
|
+
"contract.title": "Contract",
|
|
211
|
+
"contract.failed": "The task changed files that define its own checks; they were restored from the state captured before the task.",
|
|
212
|
+
"contract.recovered": "An earlier run of {{id}} ended before its checks; the files it changed that define the checks were restored.",
|
|
213
|
+
"contract.task": "Edited the task file outside ## Log.",
|
|
214
|
+
"contract.tasks": "Edited another task file.",
|
|
215
|
+
"contract.bae": "Changed bae's own configuration or prompts.",
|
|
216
|
+
"contract.agents": "Changed an agent definition or the agent settings the reviewer runs with.",
|
|
217
|
+
"contract.gitignore": "Changed an ignore file, which decides what the review sees.",
|
|
218
|
+
"contract.scripts": "Changed {{detail}}, which the checks run.",
|
|
219
|
+
"contract.runner": "Changed the test runner configuration.",
|
|
220
|
+
"contract.restored": "Restored.",
|
|
221
|
+
"contract.removed": "Removed.",
|
|
222
|
+
"findings.accepted": "Accepted with --accept-finding.",
|
|
223
|
+
"findings.acceptedTitle": "Accepted findings",
|
|
224
|
+
"regression.failed": "The regression check failed.",
|
|
225
|
+
"regression.mustPass": "`{{command}}` still fails (exit {{code}}), and the task is tests: fix, so the test suite must end green.",
|
|
226
|
+
"regression.uncomparable": "`{{command}}` already failed before the task and still fails (exit {{code}}); its output has no counts to compare, so only green passes.",
|
|
227
|
+
"regression.worse": "Regression: `{{command}}` fails more checks than before the task ({{now}}; before: {{before}}).",
|
|
228
|
+
"regression.lateStop": "{{id}} has no regression baseline from before its agent ran.",
|
|
229
|
+
"regression.declinedStop": "Without running lint and tests first there is no baseline, so the task could not be done.",
|
|
230
|
+
"regression.unusable": "`{{command}}` gives no usable baseline (exit {{code}}): it did not finish, or it fails with no counts to compare. The task could not be done while it stays red.",
|
|
231
|
+
"review.noVerdict": "The reviewer gave no verdict.",
|
|
232
|
+
"review.error": "The reviewer could not give a verdict: {{details}}",
|
|
233
|
+
"skip.used": "Going on without this check because of --allow-skip: {{what}}",
|
|
234
|
+
"skip.title": "Skipped with --allow-skip",
|
|
235
|
+
"skip.stopped": "Nothing was launched. Fix the cause, or run next again with --allow-skip to go on without that check; the skip is recorded in the run log.",
|
|
236
|
+
"next.budgetUsed": "The task already used its {{max}} automatic attempts.",
|
|
237
|
+
"next.refusedBlocked": "{{id}} is blocked before launching the agent, because its checks cannot run. Fix the task's Verification or the commands in .bae/config.json, then set the task back to pending.",
|
|
238
|
+
"capture.lateStop": "{{id}} is in progress without a capture from before its agent ran, so its checks have no trustworthy starting point. Set it back to pending, or run with --allow-skip.",
|
|
239
|
+
"capture.noGit": "This is not a git repository, so the review cannot see the task's changes.",
|
|
240
|
+
"option.allowSkip": "go on when a check cannot run (no git, no baseline, no reviewer verdict); every skip is recorded",
|
|
241
|
+
"integrity.title": "Test integrity",
|
|
242
|
+
"integrity.failed": "The task removed or disabled tests.",
|
|
243
|
+
"integrity.deleted": "Deletes a test file.",
|
|
244
|
+
"integrity.skipMarker": "Adds a marker that skips or isolates tests ({{marker}}).",
|
|
245
|
+
"integrity.exclusion": "Adds a test runner configuration that leaves tests out ({{keys}}).",
|
|
246
|
+
"integrity.fewerTests": "`{{command}}` runs fewer tests than before the task ({{now}}; before: {{before}}).",
|
|
247
|
+
"integrity.moreSkipped": "`{{command}}` skips more tests than before the task ({{now}}; before: {{before}}).",
|
|
248
|
+
"verify.noBash": "Verification runs as a bash script and no bash was found. On Windows install Git for Windows, which brings Git Bash; elsewhere put bash on PATH.",
|
|
249
|
+
"verify.masks": "Verification hides failures in `{{command}}` (|| true, set +e). The block runs with set -euo pipefail and must fail when a check fails.",
|
|
250
|
+
"verify.trivial": "Verification runs nothing that checks the task:\n{{command}}\nUse the project's test runner, a linter, or a check with an expected result (test -f, grep -q, curl -f).",
|
|
251
|
+
"verify.notAllowed": "`{{command}}` is not on the list of commands bae runs when nobody confirms them ({{why}}). Add a prefix to verify.allow in .bae/config.json, or run next without --yes and --headless and confirm it yourself.",
|
|
252
|
+
"regression.notAllowed": "The project command `{{command}}` is not on the list of commands bae runs when nobody confirms them ({{why}}). Add a prefix to verify.allow in .bae/config.json.",
|
|
253
|
+
"verify.dynamic": "it runs code that is built at run time or in another shell",
|
|
254
|
+
"verify.unknown": "unknown program",
|
|
255
|
+
"verify.unsafeWarning": "`{{command}}` looks dangerous ({{reason}}); read it before you confirm.",
|
|
256
|
+
"verify.onlyExcused": "Verification only runs commands that already failed before the task, so it checks nothing about this task.",
|
|
257
|
+
"evidence.linesFailed": "The plan cites lines that do not exist, even after asking the analyst to fix them:\n{{list}}\nThe plan was not written; the rejected answer is in {{path}}. Run plan again.",
|
|
258
|
+
"state.tampered": "Code run for this task changed bae's own state outside the repository ({{files}}); it was restored and the attempt fails.",
|
|
259
|
+
"review.gitError": "Review failed: git could not list the task's changes (a broken index, config or filter), so the checks cannot see them.",
|
|
260
|
+
"contract.memory": "Changed an agent memory file, which agents and reviewers read.",
|
|
261
|
+
"contract.git": "Changed git attributes, which decide how the review sees files.",
|
|
262
|
+
"contract.gitdir": "Changed the repository's git settings in .git, which the checks rely on.",
|
|
263
|
+
"contract.toolchain": "Changed a package manager or tool setting that decides how the checks run.",
|
|
264
|
+
"contract.shadow": "Added a file that would run instead of the project's own tool.",
|
|
265
|
+
"contract.indexFlags": "Marked files as unchanged in the git index (assume-unchanged or skip-worktree), which hides them from the review: {{files}}. The flags were cleared.",
|
|
266
|
+
"integrity.removedTests": "Removes tests that are not added back: {{tests}}.",
|
|
267
|
+
"integrity.lostAssertions": "Removes {{count}} assertion line(s) from a test file without adding them back.",
|
|
268
|
+
"capture.headMoved": "{{id}} keeps the commit recorded when it first started; commits made since then count as part of the task's changes.",
|
|
269
|
+
"contract.restoreFailed": "Could not restore {{files}} from the capture. Fix them by hand; the next run checks them again before starting.",
|
|
270
|
+
"integrity.suppression": "Adds a comment that silences a checker ({{markers}}).",
|
|
271
|
+
"integrity.expectedOutput": "Changes a snapshot or expected-output file, which decides what the tests accept.",
|
|
272
|
+
"regression.baselineTampered": "Running the project's commands for the baseline changed files the checks depend on ({{files}}); they were restored and nothing was launched.",
|
|
273
|
+
"regression.unknown": "`{{command}}` exits with {{code}}, but bae could not read how many tests ran, so it cannot tell whether they passed.",
|
|
274
|
+
"regression.unknownRunner": "bae does not recognize the test runner behind `{{command}}`, so it cannot tell whether the tests pass. Set commands.test in .bae/config.json to the runner itself, for example `npx vitest run`, `npx jest`, `pytest`, `go test -v ./...`, `cargo test` or `dotnet test`.",
|
|
275
|
+
"regression.noCounts": "`{{command}}` gives no usable baseline: it passes but does not say how many tests ran, so the task could not be compared with it.",
|
|
276
|
+
"regression.unreadSkipped": "`{{command}}` did not say how many tests ran",
|
|
277
|
+
"contract.scoped": "The task's Scope lists this file, so the change stays: the checks run with it and the reviewer sees it.",
|
|
278
|
+
"contract.weakerScripts": "It stops running what these scripts ran: {{scripts}}.",
|
|
279
|
+
"contract.weakerRunner": "It adds settings that leave tests out ({{keys}}).",
|
|
280
|
+
"contract.weakerToolchain": "It adds a setting that changes how the package manager or the test runner starts.",
|
|
281
|
+
"review.noEvidence": "(none: this review was not run right after next's checks)",
|
|
282
|
+
"integrity.scoped": "The task's Scope lists this file, so the reviewer must say why this change is correct.",
|
|
283
|
+
"review.unjustified": "The reviewer passed the task without saying why these changes to test files are correct: {{files}}.",
|
|
284
|
+
"mechanical.secretHistory": "A commit made during the task adds what looks like a credential ({{kind}}). It stays in the git history even if the file no longer has it, so rewrite those commits.",
|
|
285
|
+
"mechanical.secretFileHistory": "A commit made during the task adds a secrets file. It stays in the git history even if the file is gone, so rewrite those commits.",
|
|
286
|
+
"regression.noTestsYet": "`{{command}}` has no tests to run yet, so there is no baseline for it; after the task it must pass and say how many tests ran.",
|
|
287
|
+
"integrity.planned": "The task's Scope marks test files it removes, so the reviewer judges it instead.",
|
|
288
|
+
};
|