gearbox-code 0.1.9 → 0.1.11
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 +3 -0
- package/dist/cli.mjs +456 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,9 @@ Common setup commands:
|
|
|
38
38
|
```bash
|
|
39
39
|
gearbox auth add <api-key> # auto-detects known key prefixes
|
|
40
40
|
gearbox auth add <provider> <api-key> # anthropic, openai, google, deepseek, openrouter, groq, xai, mistral...
|
|
41
|
+
gearbox auth add codex # ChatGPT subscription through the Codex CLI
|
|
42
|
+
gearbox auth add codex work # second ChatGPT account, isolated CODEX_HOME
|
|
43
|
+
gearbox auth add claude work # second Claude account, isolated config
|
|
41
44
|
gearbox auth import # import credentials from env/cloud config
|
|
42
45
|
gearbox auth providers # list supported providers
|
|
43
46
|
```
|
package/dist/cli.mjs
CHANGED
|
@@ -64452,6 +64452,17 @@ function cleanCliStderr(text2) {
|
|
|
64452
64452
|
}
|
|
64453
64453
|
return cleaned;
|
|
64454
64454
|
}
|
|
64455
|
+
function cliFailureMessage(binary, stderr, opts = {}) {
|
|
64456
|
+
const err = cleanCliStderr(stderr);
|
|
64457
|
+
const isCodex = binary.includes("codex");
|
|
64458
|
+
if (isCodex && /app_session_terminated|Your session has ended|Failed to refresh token|HTTP error: 401 Unauthorized/i.test(err)) {
|
|
64459
|
+
const account = opts.accountLabel ? ` for ${opts.accountLabel}` : "";
|
|
64460
|
+
const relogin = opts.reloginCommand ? ` Run ${opts.reloginCommand} to sign in again, then /retry.` : " Sign in to that Codex account again, then /retry.";
|
|
64461
|
+
return `Codex session expired${account}.${relogin}`;
|
|
64462
|
+
}
|
|
64463
|
+
const hint = isCodex ? "Codex CLI failed before returning an assistant message. Check the line above, then /retry." : `${binary} failed before returning an assistant message. Check the line above, then /retry.`;
|
|
64464
|
+
return err ? `${hint} ${err}` : hint;
|
|
64465
|
+
}
|
|
64455
64466
|
async function runCliTask(opts) {
|
|
64456
64467
|
const { binary, prompt, messages, onEvent, signal } = opts;
|
|
64457
64468
|
const args = buildCliArgs(binary, prompt, { sessionId: opts.sessionId, autoApprove: opts.autoApprove, modelId: opts.modelId, effort: opts.effort });
|
|
@@ -64513,8 +64524,7 @@ async function runCliTask(opts) {
|
|
|
64513
64524
|
if (!signal?.aborted) {
|
|
64514
64525
|
const err = cleanCliStderr(stderr);
|
|
64515
64526
|
if ((proc.exitCode ?? 0) !== 0) {
|
|
64516
|
-
|
|
64517
|
-
onEvent({ type: "error", message: err ? `${hint} ${err}` : hint });
|
|
64527
|
+
onEvent({ type: "error", message: cliFailureMessage(binary, stderr, { accountLabel: opts.accountLabel, reloginCommand: opts.reloginCommand }) });
|
|
64518
64528
|
} else if (!state.text && !sawEvent && err) {
|
|
64519
64529
|
onEvent({ type: "error", message: `${binary} produced no JSON output: ${err}` });
|
|
64520
64530
|
} else if (!state.text && !sawEvent) {
|
|
@@ -70303,7 +70313,7 @@ var import_react21 = __toESM(require_react(), 1);
|
|
|
70303
70313
|
import { createInterface } from "node:readline/promises";
|
|
70304
70314
|
import { execFileSync as execFileSync4, spawnSync } from "node:child_process";
|
|
70305
70315
|
import { resolve as resolve11 } from "node:path";
|
|
70306
|
-
import { existsSync as
|
|
70316
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
70307
70317
|
|
|
70308
70318
|
// src/ui/App.tsx
|
|
70309
70319
|
var import_react26 = __toESM(require_react(), 1);
|
|
@@ -132179,6 +132189,70 @@ async function runShellStream(command, opts = {}) {
|
|
|
132179
132189
|
|
|
132180
132190
|
// src/tools.ts
|
|
132181
132191
|
init_proc();
|
|
132192
|
+
|
|
132193
|
+
// src/fetch.ts
|
|
132194
|
+
var MAX_FETCH_CHARS = 80000;
|
|
132195
|
+
var MAX_RETURN_CHARS = 20000;
|
|
132196
|
+
var ENTITY = {
|
|
132197
|
+
amp: "&",
|
|
132198
|
+
lt: "<",
|
|
132199
|
+
gt: ">",
|
|
132200
|
+
quot: '"',
|
|
132201
|
+
apos: "'",
|
|
132202
|
+
nbsp: " "
|
|
132203
|
+
};
|
|
132204
|
+
function urlsInText(text2, limit = 2) {
|
|
132205
|
+
const out = [];
|
|
132206
|
+
const re = /\bhttps?:\/\/[^\s<>"')\]]+/gi;
|
|
132207
|
+
for (const m2 of text2.matchAll(re)) {
|
|
132208
|
+
const url2 = m2[0].replace(/[.,;:!?]+$/, "");
|
|
132209
|
+
if (!out.includes(url2))
|
|
132210
|
+
out.push(url2);
|
|
132211
|
+
if (out.length >= limit)
|
|
132212
|
+
break;
|
|
132213
|
+
}
|
|
132214
|
+
return out;
|
|
132215
|
+
}
|
|
132216
|
+
function decodeEntities(text2) {
|
|
132217
|
+
return text2.replace(/&(#x?[0-9a-f]+|[a-z]+);/gi, (_, raw) => {
|
|
132218
|
+
const key = raw.toLowerCase();
|
|
132219
|
+
if (key[0] === "#") {
|
|
132220
|
+
const n = key[1] === "x" ? Number.parseInt(key.slice(2), 16) : Number.parseInt(key.slice(1), 10);
|
|
132221
|
+
return Number.isFinite(n) ? String.fromCodePoint(n) : "";
|
|
132222
|
+
}
|
|
132223
|
+
return ENTITY[key] ?? "";
|
|
132224
|
+
});
|
|
132225
|
+
}
|
|
132226
|
+
function stripHtml(html2) {
|
|
132227
|
+
return decodeEntities(html2.replace(/<script\b[\s\S]*?<\/script>/gi, " ").replace(/<style\b[\s\S]*?<\/style>/gi, " ").replace(/<noscript\b[\s\S]*?<\/noscript>/gi, " ").replace(/<\/(p|div|section|article|header|footer|li|tr|h[1-6])>/gi, `
|
|
132228
|
+
`).replace(/<br\s*\/?>/gi, `
|
|
132229
|
+
`).replace(/<[^>]+>/g, " ").replace(/[ \t]+\n/g, `
|
|
132230
|
+
`).replace(/\n{3,}/g, `
|
|
132231
|
+
|
|
132232
|
+
`).replace(/[ \t]{2,}/g, " ").trim());
|
|
132233
|
+
}
|
|
132234
|
+
async function fetchUrlText(url2) {
|
|
132235
|
+
const u = new URL(url2);
|
|
132236
|
+
if (u.protocol !== "http:" && u.protocol !== "https:")
|
|
132237
|
+
throw new Error("only http(s) URLs are supported");
|
|
132238
|
+
const res = await fetch(u, {
|
|
132239
|
+
headers: {
|
|
132240
|
+
"user-agent": "Gearbox/0.1 (+https://github.com/AnayGarodia/gearbox)",
|
|
132241
|
+
accept: "text/html,text/plain,application/json;q=0.8,*/*;q=0.2"
|
|
132242
|
+
}
|
|
132243
|
+
});
|
|
132244
|
+
if (!res.ok)
|
|
132245
|
+
throw new Error(`fetch failed: HTTP ${res.status}`);
|
|
132246
|
+
const raw = (await res.text()).slice(0, MAX_FETCH_CHARS);
|
|
132247
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
132248
|
+
const title = raw.match(/<title[^>]*>([\s\S]*?)<\/title>/i)?.[1]?.replace(/\s+/g, " ").trim();
|
|
132249
|
+
const text2 = (/html/i.test(contentType) || /<html|<body|<p\b|<div\b/i.test(raw) ? stripHtml(raw) : raw.trim()).slice(0, MAX_RETURN_CHARS);
|
|
132250
|
+
if (!text2)
|
|
132251
|
+
throw new Error("fetched URL had no readable text");
|
|
132252
|
+
return { url: u.toString(), title: title ? decodeEntities(title) : undefined, text: text2, chars: text2.length };
|
|
132253
|
+
}
|
|
132254
|
+
|
|
132255
|
+
// src/tools.ts
|
|
132182
132256
|
var ROOT = process.cwd();
|
|
132183
132257
|
var CAP2 = 60000;
|
|
132184
132258
|
var DENIED = "Permission denied by the user — they declined this action.";
|
|
@@ -132192,6 +132266,44 @@ function safe(path) {
|
|
|
132192
132266
|
}
|
|
132193
132267
|
var clip2 = (s2) => s2.length > CAP2 ? s2.slice(0, CAP2) + `
|
|
132194
132268
|
… [clipped ${s2.length - CAP2} chars]` : s2;
|
|
132269
|
+
function countOccurrences(text2, find2) {
|
|
132270
|
+
if (!find2)
|
|
132271
|
+
return 0;
|
|
132272
|
+
let count = 0;
|
|
132273
|
+
let at3 = 0;
|
|
132274
|
+
while ((at3 = text2.indexOf(find2, at3)) >= 0) {
|
|
132275
|
+
count++;
|
|
132276
|
+
at3 += find2.length;
|
|
132277
|
+
}
|
|
132278
|
+
return count;
|
|
132279
|
+
}
|
|
132280
|
+
function replaceOccurrence(text2, find2, replace2, occurrence) {
|
|
132281
|
+
let at3 = -1;
|
|
132282
|
+
let from = 0;
|
|
132283
|
+
for (let i2 = 0;i2 < occurrence; i2++) {
|
|
132284
|
+
at3 = text2.indexOf(find2, from);
|
|
132285
|
+
if (at3 < 0)
|
|
132286
|
+
return text2;
|
|
132287
|
+
from = at3 + find2.length;
|
|
132288
|
+
}
|
|
132289
|
+
return text2.slice(0, at3) + replace2 + text2.slice(at3 + find2.length);
|
|
132290
|
+
}
|
|
132291
|
+
function notFoundHint(path, before2, find2) {
|
|
132292
|
+
const needle = find2.trim().split(/\s+/).filter((w) => w.length >= 3)[0]?.toLowerCase();
|
|
132293
|
+
if (!needle)
|
|
132294
|
+
return `text not found in ${path}`;
|
|
132295
|
+
const lines = before2.split(`
|
|
132296
|
+
`);
|
|
132297
|
+
const hit = lines.findIndex((l) => l.toLowerCase().includes(needle));
|
|
132298
|
+
if (hit < 0)
|
|
132299
|
+
return `text not found in ${path}`;
|
|
132300
|
+
const start = Math.max(0, hit - 2);
|
|
132301
|
+
const end = Math.min(lines.length, hit + 3);
|
|
132302
|
+
const snippet = lines.slice(start, end).map((l, i2) => `${start + i2 + 1}: ${l}`).join(`
|
|
132303
|
+
`);
|
|
132304
|
+
return `text not found in ${path}. Nearby match for "${needle}":
|
|
132305
|
+
${snippet}`;
|
|
132306
|
+
}
|
|
132195
132307
|
function createTools(onEvent) {
|
|
132196
132308
|
return {
|
|
132197
132309
|
read_file: tool5({
|
|
@@ -132214,19 +132326,38 @@ function createTools(onEvent) {
|
|
|
132214
132326
|
}
|
|
132215
132327
|
}),
|
|
132216
132328
|
edit_file: tool5({
|
|
132217
|
-
description: "
|
|
132218
|
-
inputSchema: exports_external2.object({
|
|
132219
|
-
|
|
132329
|
+
description: "Edit a file by exact text replacement. Use occurrence for a specific match, or replaceAll for every exact match.",
|
|
132330
|
+
inputSchema: exports_external2.object({
|
|
132331
|
+
path: exports_external2.string(),
|
|
132332
|
+
find: exports_external2.string().min(1),
|
|
132333
|
+
replace: exports_external2.string(),
|
|
132334
|
+
occurrence: exports_external2.number().int().positive().default(1).describe("1-based match to replace when replaceAll is false"),
|
|
132335
|
+
replaceAll: exports_external2.boolean().default(false).describe("replace every exact occurrence")
|
|
132336
|
+
}),
|
|
132337
|
+
execute: async ({ path, find: find2, replace: replace2, occurrence, replaceAll }) => {
|
|
132220
132338
|
const abs = safe(path);
|
|
132221
132339
|
const before2 = await readFile(abs, "utf8");
|
|
132222
|
-
|
|
132223
|
-
|
|
132340
|
+
const matches2 = countOccurrences(before2, find2);
|
|
132341
|
+
if (matches2 === 0)
|
|
132342
|
+
throw new Error(notFoundHint(path, before2, find2));
|
|
132343
|
+
if (!replaceAll && occurrence > matches2)
|
|
132344
|
+
throw new Error(`only found ${matches2} occurrence${matches2 === 1 ? "" : "s"} in ${path}; requested occurrence ${occurrence}`);
|
|
132224
132345
|
if (!await requestPermission({ kind: "edit", title: "Edit a file", detail: path }))
|
|
132225
132346
|
throw new Error(DENIED);
|
|
132226
|
-
const after2 = before2.
|
|
132347
|
+
const after2 = replaceAll ? before2.split(find2).join(replace2) : replaceOccurrence(before2, find2, replace2, occurrence);
|
|
132227
132348
|
await writeFile2(abs, after2, "utf8");
|
|
132228
132349
|
const diff2 = computeDiff(before2, after2);
|
|
132229
|
-
|
|
132350
|
+
const changed = replaceAll ? matches2 : 1;
|
|
132351
|
+
return { summary: `edited ${path} · ${changed} replacement${changed === 1 ? "" : "s"} (${diffStat(diff2)})`, diff: diff2 };
|
|
132352
|
+
}
|
|
132353
|
+
}),
|
|
132354
|
+
fetch_url: tool5({
|
|
132355
|
+
description: "Fetch a public http(s) URL and return readable text. Use this for docs, release notes, issue pages, or pasted links.",
|
|
132356
|
+
inputSchema: exports_external2.object({ url: exports_external2.string().url() }),
|
|
132357
|
+
execute: async ({ url: url2 }) => {
|
|
132358
|
+
const page = await fetchUrlText(url2);
|
|
132359
|
+
return clip2([`URL: ${page.url}`, page.title ? `Title: ${page.title}` : "", "", page.text].filter(Boolean).join(`
|
|
132360
|
+
`));
|
|
132230
132361
|
}
|
|
132231
132362
|
}),
|
|
132232
132363
|
search: tool5({
|
|
@@ -132332,7 +132463,8 @@ var readOnlyTools = {
|
|
|
132332
132463
|
read_file: tools.read_file,
|
|
132333
132464
|
list_dir: tools.list_dir,
|
|
132334
132465
|
search: tools.search,
|
|
132335
|
-
glob: tools.glob
|
|
132466
|
+
glob: tools.glob,
|
|
132467
|
+
fetch_url: tools.fetch_url
|
|
132336
132468
|
};
|
|
132337
132469
|
|
|
132338
132470
|
// node_modules/js-tiktoken/dist/chunk-VL2OQCWN.js
|
|
@@ -132789,6 +132921,42 @@ function retrieveFiles(query, cwd2 = process.cwd(), k = 6, budget = 8000, modelI
|
|
|
132789
132921
|
return out;
|
|
132790
132922
|
}
|
|
132791
132923
|
|
|
132924
|
+
// src/context/git.ts
|
|
132925
|
+
init_proc();
|
|
132926
|
+
function git(args, cwd2) {
|
|
132927
|
+
const r2 = spawnSyncProc(["git", ...args], { cwd: cwd2, stdout: "pipe", stderr: "ignore" });
|
|
132928
|
+
return r2.exitCode === 0 ? r2.stdout.toString().trim() : "";
|
|
132929
|
+
}
|
|
132930
|
+
function gitContext(cwd2 = process.cwd()) {
|
|
132931
|
+
if (!git(["rev-parse", "--is-inside-work-tree"], cwd2))
|
|
132932
|
+
return "";
|
|
132933
|
+
const branch = git(["branch", "--show-current"], cwd2) || git(["rev-parse", "--short", "HEAD"], cwd2);
|
|
132934
|
+
const status = git(["status", "--short"], cwd2);
|
|
132935
|
+
const staged = git(["diff", "--cached", "--stat"], cwd2);
|
|
132936
|
+
const unstaged = git(["diff", "--stat"], cwd2);
|
|
132937
|
+
const commits = git(["log", "--oneline", "-5"], cwd2);
|
|
132938
|
+
const parts = [];
|
|
132939
|
+
if (branch)
|
|
132940
|
+
parts.push(`branch: ${branch}`);
|
|
132941
|
+
if (status)
|
|
132942
|
+
parts.push(`dirty files:
|
|
132943
|
+
${status}`);
|
|
132944
|
+
else
|
|
132945
|
+
parts.push("working tree: clean");
|
|
132946
|
+
if (staged)
|
|
132947
|
+
parts.push(`staged diff stat:
|
|
132948
|
+
${staged}`);
|
|
132949
|
+
if (unstaged)
|
|
132950
|
+
parts.push(`unstaged diff stat:
|
|
132951
|
+
${unstaged}`);
|
|
132952
|
+
if (commits)
|
|
132953
|
+
parts.push(`recent commits:
|
|
132954
|
+
${commits}`);
|
|
132955
|
+
return parts.join(`
|
|
132956
|
+
|
|
132957
|
+
`);
|
|
132958
|
+
}
|
|
132959
|
+
|
|
132792
132960
|
// src/context/builder.ts
|
|
132793
132961
|
var BASE_SYSTEM = `You are Gearbox, a precise terminal coding agent.
|
|
132794
132962
|
Work in small, verifiable steps. Use the tools to read before you write, and
|
|
@@ -132900,6 +133068,14 @@ function buildContext(opts) {
|
|
|
132900
133068
|
${memory}`;
|
|
132901
133069
|
sections.push({ name: "memory", tokens: countTokens(memory, modelId) });
|
|
132902
133070
|
}
|
|
133071
|
+
const git2 = safe2(() => gitContext(cwd2), "");
|
|
133072
|
+
if (git2) {
|
|
133073
|
+
system += `
|
|
133074
|
+
|
|
133075
|
+
# GIT CONTEXT (current repository state; do not overwrite unrelated user changes)
|
|
133076
|
+
${git2}`;
|
|
133077
|
+
sections.push({ name: "git", tokens: countTokens(git2, modelId) });
|
|
133078
|
+
}
|
|
132903
133079
|
const mapBudget = Math.min(4000, Math.floor(inputBudget * 0.05));
|
|
132904
133080
|
const map3 = safe2(() => repoMap(cwd2, mapBudget), "");
|
|
132905
133081
|
if (map3) {
|
|
@@ -133369,6 +133545,171 @@ ${summary}` },
|
|
|
133369
133545
|
return { messages, summarizedTurns: old.length, before: before2, after: after2 };
|
|
133370
133546
|
}
|
|
133371
133547
|
|
|
133548
|
+
// src/init.ts
|
|
133549
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "node:fs";
|
|
133550
|
+
import { join as join12 } from "node:path";
|
|
133551
|
+
|
|
133552
|
+
// src/verify.ts
|
|
133553
|
+
import { existsSync as existsSync7, readFileSync as readFileSync12 } from "node:fs";
|
|
133554
|
+
import { join as join11 } from "node:path";
|
|
133555
|
+
function readJson(path) {
|
|
133556
|
+
try {
|
|
133557
|
+
return JSON.parse(readFileSync12(path, "utf8"));
|
|
133558
|
+
} catch {
|
|
133559
|
+
return null;
|
|
133560
|
+
}
|
|
133561
|
+
}
|
|
133562
|
+
function packageManager(cwd2) {
|
|
133563
|
+
if (existsSync7(join11(cwd2, "bun.lock")) || existsSync7(join11(cwd2, "bun.lockb")))
|
|
133564
|
+
return "bun";
|
|
133565
|
+
if (existsSync7(join11(cwd2, "pnpm-lock.yaml")))
|
|
133566
|
+
return "pnpm";
|
|
133567
|
+
if (existsSync7(join11(cwd2, "yarn.lock")))
|
|
133568
|
+
return "yarn";
|
|
133569
|
+
return "npm";
|
|
133570
|
+
}
|
|
133571
|
+
function packageCommands(cwd2) {
|
|
133572
|
+
const pkg = readJson(join11(cwd2, "package.json"));
|
|
133573
|
+
const scripts = pkg?.scripts ?? {};
|
|
133574
|
+
if (!scripts || typeof scripts !== "object")
|
|
133575
|
+
return [];
|
|
133576
|
+
const pm = packageManager(cwd2);
|
|
133577
|
+
const run = (name31) => pm === "npm" ? `npm run ${name31}` : `${pm} run ${name31}`;
|
|
133578
|
+
const cmds = [];
|
|
133579
|
+
if (scripts.typecheck)
|
|
133580
|
+
cmds.push({ command: run("typecheck"), reason: "typecheck script" });
|
|
133581
|
+
if (scripts.test)
|
|
133582
|
+
cmds.push({ command: pm === "npm" ? "npm test" : pm === "bun" ? "bun test" : `${pm} test`, reason: "test script" });
|
|
133583
|
+
if (scripts.build)
|
|
133584
|
+
cmds.push({ command: run("build"), reason: "build script" });
|
|
133585
|
+
return cmds;
|
|
133586
|
+
}
|
|
133587
|
+
function detectVerificationCommands(cwd2 = process.cwd(), changedFiles = []) {
|
|
133588
|
+
const cmds = packageCommands(cwd2);
|
|
133589
|
+
const hasPython = changedFiles.some((f3) => /\.py$/.test(f3)) || existsSync7(join11(cwd2, "pyproject.toml")) || existsSync7(join11(cwd2, "pytest.ini"));
|
|
133590
|
+
const hasRust = changedFiles.some((f3) => /\.rs$/.test(f3)) || existsSync7(join11(cwd2, "Cargo.toml"));
|
|
133591
|
+
const hasGo = changedFiles.some((f3) => /\.go$/.test(f3)) || existsSync7(join11(cwd2, "go.mod"));
|
|
133592
|
+
if (hasPython && !cmds.some((c) => /\bpytest\b/.test(c.command)))
|
|
133593
|
+
cmds.push({ command: "pytest", reason: "python project" });
|
|
133594
|
+
if (hasRust && !cmds.some((c) => /\bcargo\s+test\b/.test(c.command)))
|
|
133595
|
+
cmds.push({ command: "cargo test", reason: "rust project" });
|
|
133596
|
+
if (hasGo && !cmds.some((c) => /\bgo\s+test\b/.test(c.command)))
|
|
133597
|
+
cmds.push({ command: "go test ./...", reason: "go project" });
|
|
133598
|
+
return cmds.slice(0, 3);
|
|
133599
|
+
}
|
|
133600
|
+
function summarize(output) {
|
|
133601
|
+
const lines = output.split(`
|
|
133602
|
+
`).map((l) => l.trim()).filter(Boolean);
|
|
133603
|
+
const fail = lines.find((l) => /\b(error|failed|failures?|exception|panic)\b/i.test(l));
|
|
133604
|
+
return (fail ?? lines[0] ?? "(no output)").slice(0, 160);
|
|
133605
|
+
}
|
|
133606
|
+
async function runVerification(commands, opts) {
|
|
133607
|
+
const results = [];
|
|
133608
|
+
for (const c of commands) {
|
|
133609
|
+
opts.onEvent({ type: "phase", label: "verifying", detail: `${c.command} · ${c.reason}`, state: "running" });
|
|
133610
|
+
const r2 = await runShellStream(c.command, { signal: opts.signal, timeoutMs: opts.timeoutMs ?? 120000 });
|
|
133611
|
+
results.push(r2);
|
|
133612
|
+
opts.onEvent({ type: "verification", command: c.command, ok: r2.ok, summary: r2.ok ? "passed" : summarize(r2.output) });
|
|
133613
|
+
opts.onEvent({ type: "phase", label: "verification", detail: c.command, state: r2.ok ? "ok" : "err" });
|
|
133614
|
+
if (!r2.ok)
|
|
133615
|
+
break;
|
|
133616
|
+
}
|
|
133617
|
+
return results;
|
|
133618
|
+
}
|
|
133619
|
+
|
|
133620
|
+
// src/init.ts
|
|
133621
|
+
function readJson2(path) {
|
|
133622
|
+
try {
|
|
133623
|
+
return JSON.parse(readFileSync13(path, "utf8"));
|
|
133624
|
+
} catch {
|
|
133625
|
+
return null;
|
|
133626
|
+
}
|
|
133627
|
+
}
|
|
133628
|
+
function rootEntries(cwd2) {
|
|
133629
|
+
try {
|
|
133630
|
+
return readdirSync4(cwd2, { withFileTypes: true }).filter((e2) => ![".git", "node_modules", "dist", "build", ".next", "coverage"].includes(e2.name)).map((e2) => e2.name + (e2.isDirectory() ? "/" : "")).sort().slice(0, 80);
|
|
133631
|
+
} catch {
|
|
133632
|
+
return [];
|
|
133633
|
+
}
|
|
133634
|
+
}
|
|
133635
|
+
function detectStack(cwd2) {
|
|
133636
|
+
const out = [];
|
|
133637
|
+
const pkg = readJson2(join12(cwd2, "package.json"));
|
|
133638
|
+
if (pkg) {
|
|
133639
|
+
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
133640
|
+
out.push("JavaScript/TypeScript");
|
|
133641
|
+
if (deps.react || deps.ink)
|
|
133642
|
+
out.push(deps.ink ? "Ink terminal UI" : "React");
|
|
133643
|
+
if (existsSync8(join12(cwd2, "bun.lock")) || existsSync8(join12(cwd2, "bun.lockb")))
|
|
133644
|
+
out.push("Bun");
|
|
133645
|
+
}
|
|
133646
|
+
if (existsSync8(join12(cwd2, "pyproject.toml")))
|
|
133647
|
+
out.push("Python");
|
|
133648
|
+
if (existsSync8(join12(cwd2, "Cargo.toml")))
|
|
133649
|
+
out.push("Rust");
|
|
133650
|
+
if (existsSync8(join12(cwd2, "go.mod")))
|
|
133651
|
+
out.push("Go");
|
|
133652
|
+
return [...new Set(out)];
|
|
133653
|
+
}
|
|
133654
|
+
function packageScripts(cwd2) {
|
|
133655
|
+
const pkg = readJson2(join12(cwd2, "package.json"));
|
|
133656
|
+
const scripts = pkg?.scripts ?? {};
|
|
133657
|
+
return Object.keys(scripts).sort().map((k) => `${k}: ${scripts[k]}`).slice(0, 20);
|
|
133658
|
+
}
|
|
133659
|
+
function existingDocs(cwd2) {
|
|
133660
|
+
return ["README.md", "DESIGN.md", "ROADMAP.md", "VISION.md", "AGENTS.md", "CLAUDE.md"].filter((name31) => existsSync8(join12(cwd2, name31)));
|
|
133661
|
+
}
|
|
133662
|
+
function buildProjectGuide(cwd2 = process.cwd()) {
|
|
133663
|
+
const name31 = readJson2(join12(cwd2, "package.json"))?.name ?? cwd2.split(/[\\/]/).filter(Boolean).at(-1) ?? "project";
|
|
133664
|
+
const stack = detectStack(cwd2);
|
|
133665
|
+
const checks3 = detectVerificationCommands(cwd2).map((c) => c.command);
|
|
133666
|
+
const scripts = packageScripts(cwd2);
|
|
133667
|
+
const entries = rootEntries(cwd2);
|
|
133668
|
+
const docs = existingDocs(cwd2);
|
|
133669
|
+
return `# ${name31} - Gearbox Guide
|
|
133670
|
+
|
|
133671
|
+
## What This Project Is
|
|
133672
|
+
|
|
133673
|
+
This file was generated from the repository structure so Gearbox has project context before editing.
|
|
133674
|
+
${stack.length ? `Detected stack: ${stack.join(", ")}.` : "Detected stack: unknown from root files."}
|
|
133675
|
+
|
|
133676
|
+
## Run And Verify
|
|
133677
|
+
|
|
133678
|
+
${checks3.length ? checks3.map((c) => `- \`${c}\``).join(`
|
|
133679
|
+
`) : "- No standard verification command was detected. Add one here when known."}
|
|
133680
|
+
|
|
133681
|
+
## Layout
|
|
133682
|
+
|
|
133683
|
+
${entries.length ? entries.map((e2) => `- \`${e2}\``).join(`
|
|
133684
|
+
`) : "- Root layout could not be read."}
|
|
133685
|
+
|
|
133686
|
+
${scripts.length ? `## Package Scripts
|
|
133687
|
+
|
|
133688
|
+
${scripts.map((s2) => `- \`${s2}\``).join(`
|
|
133689
|
+
`)}
|
|
133690
|
+
|
|
133691
|
+
` : ""}## Existing Project Docs
|
|
133692
|
+
|
|
133693
|
+
${docs.length ? docs.map((d) => `- \`${d}\``).join(`
|
|
133694
|
+
`) : "- No common docs detected."}
|
|
133695
|
+
|
|
133696
|
+
## Agent Conventions
|
|
133697
|
+
|
|
133698
|
+
- Read relevant files before editing.
|
|
133699
|
+
- Keep changes scoped to the user request.
|
|
133700
|
+
- Do not overwrite unrelated dirty work.
|
|
133701
|
+
- Run the verification commands above after edits when practical.
|
|
133702
|
+
`;
|
|
133703
|
+
}
|
|
133704
|
+
function writeProjectGuide(cwd2 = process.cwd()) {
|
|
133705
|
+
const path = join12(cwd2, "GEARBOX.md");
|
|
133706
|
+
const before2 = existsSync8(path) ? readFileSync13(path, "utf8") : "";
|
|
133707
|
+
const after2 = buildProjectGuide(cwd2);
|
|
133708
|
+
writeFileSync7(path, after2, "utf8");
|
|
133709
|
+
const diff2 = computeDiff(before2, after2);
|
|
133710
|
+
return { path, summary: `wrote GEARBOX.md (${diffStat(diff2)})`, diff: diff2 };
|
|
133711
|
+
}
|
|
133712
|
+
|
|
133372
133713
|
// src/ui/clipboard.ts
|
|
133373
133714
|
init_proc();
|
|
133374
133715
|
function osc52(text2) {
|
|
@@ -133572,7 +133913,7 @@ function gitBranch() {
|
|
|
133572
133913
|
init_proc();
|
|
133573
133914
|
var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
|
|
133574
133915
|
import { basename as basename2, extname } from "node:path";
|
|
133575
|
-
import { existsSync as
|
|
133916
|
+
import { existsSync as existsSync9, readFileSync as readFileSync14, statSync as statSync4 } from "node:fs";
|
|
133576
133917
|
import { writeFile as fsWriteFile } from "node:fs/promises";
|
|
133577
133918
|
import { spawnSync as nodeSpawnSync2 } from "node:child_process";
|
|
133578
133919
|
var accountResolver = new AccountResolver;
|
|
@@ -133676,12 +134017,12 @@ function previewLang(path) {
|
|
|
133676
134017
|
}
|
|
133677
134018
|
function filePreview(path) {
|
|
133678
134019
|
try {
|
|
133679
|
-
if (!path || !
|
|
134020
|
+
if (!path || !existsSync9(path))
|
|
133680
134021
|
return null;
|
|
133681
134022
|
const st = statSync4(path);
|
|
133682
134023
|
if (!st.isFile() || st.size > 400000)
|
|
133683
134024
|
return null;
|
|
133684
|
-
const raw =
|
|
134025
|
+
const raw = readFileSync14(path, "utf8").replace(/\r\n?/g, `
|
|
133685
134026
|
`);
|
|
133686
134027
|
const lines = raw.split(`
|
|
133687
134028
|
`);
|
|
@@ -134618,7 +134959,23 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
|
|
|
134618
134959
|
const cliChoices = cliModelChoices(cli.binary);
|
|
134619
134960
|
const cliChoice = cliChoices.find((m2) => m2.id === activeCliModelRef.current) ?? cliChoices[0];
|
|
134620
134961
|
const cliEffort = cliChoice ? normalizeEffort(effortRef.current, cliChoice.efforts ?? []) ?? undefined : undefined;
|
|
134621
|
-
const
|
|
134962
|
+
const activeAccount = getAccount(cli.id);
|
|
134963
|
+
const activeName = activeAccount ? accountName(activeAccount).match(/\((.*)\)/)?.[1] : undefined;
|
|
134964
|
+
const reloginCommand = cli.binary.includes("codex") ? `/account add codex${activeName ? ` ${activeName}` : ""}` : `/account add claude${activeName ? ` ${activeName}` : ""}`;
|
|
134965
|
+
const r3 = await runCliTask({
|
|
134966
|
+
binary: cli.binary,
|
|
134967
|
+
prompt,
|
|
134968
|
+
messages,
|
|
134969
|
+
onEvent,
|
|
134970
|
+
signal,
|
|
134971
|
+
sessionId: cliSessionRef.current,
|
|
134972
|
+
autoApprove: isYolo(),
|
|
134973
|
+
profile: cli.profile,
|
|
134974
|
+
modelId: activeCliModelRef.current,
|
|
134975
|
+
effort: cliEffort,
|
|
134976
|
+
accountLabel: activeAccount ? accountLabel(activeAccount) : cli.id,
|
|
134977
|
+
reloginCommand
|
|
134978
|
+
});
|
|
134622
134979
|
cliSessionRef.current = r3.sessionId ?? cliSessionRef.current;
|
|
134623
134980
|
cliMetaRef.current = { costUSD: r3.costUSD, rates: r3.rates };
|
|
134624
134981
|
return { messages: r3.messages, usage: r3.usage };
|
|
@@ -134728,9 +135085,32 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
|
|
|
134728
135085
|
echo(prompt);
|
|
134729
135086
|
lastPromptRef.current = prompt;
|
|
134730
135087
|
setVerb(nextVerb());
|
|
134731
|
-
|
|
135088
|
+
let { text: modelPrompt, attached } = expandMentions(prompt);
|
|
134732
135089
|
if (attached.length)
|
|
134733
135090
|
notice(`attached ${attached.length} file${attached.length > 1 ? "s" : ""}: ${attached.join(", ")}`);
|
|
135091
|
+
const urls = urlsInText(modelPrompt);
|
|
135092
|
+
if (urls.length) {
|
|
135093
|
+
const fetched = [];
|
|
135094
|
+
for (const url2 of urls) {
|
|
135095
|
+
try {
|
|
135096
|
+
const page = await fetchUrlText(url2);
|
|
135097
|
+
fetched.push(`=== ${page.url}${page.title ? ` · ${page.title}` : ""} ===
|
|
135098
|
+
${page.text}`);
|
|
135099
|
+
} catch (e2) {
|
|
135100
|
+
notice(`couldn't fetch ${url2}: ${(e2?.message ?? String(e2)).split(`
|
|
135101
|
+
`)[0]}`);
|
|
135102
|
+
}
|
|
135103
|
+
}
|
|
135104
|
+
if (fetched.length) {
|
|
135105
|
+
notice(`fetched ${fetched.length} URL${fetched.length === 1 ? "" : "s"} for context`);
|
|
135106
|
+
modelPrompt += `
|
|
135107
|
+
|
|
135108
|
+
# FETCHED URL CONTEXT
|
|
135109
|
+
${fetched.join(`
|
|
135110
|
+
|
|
135111
|
+
`)}`;
|
|
135112
|
+
}
|
|
135113
|
+
}
|
|
134734
135114
|
setBusy(true);
|
|
134735
135115
|
setSuggestion(null);
|
|
134736
135116
|
const turnStart = Date.now();
|
|
@@ -134889,6 +135269,16 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
|
|
|
134889
135269
|
try {
|
|
134890
135270
|
const r2 = await (runner ?? defaultRunner)({ prompt: modelPrompt, messages: msgRef.current, onEvent, selector: selectorRef.current, signal: ac.signal });
|
|
134891
135271
|
msgRef.current = r2.messages;
|
|
135272
|
+
if (!hadError && !ac.signal.aborted && changedFiles.size && checks3.length === 0) {
|
|
135273
|
+
const commands = detectVerificationCommands(process.cwd(), [...changedFiles]);
|
|
135274
|
+
if (commands.length) {
|
|
135275
|
+
const results = await runVerification(commands, { onEvent, signal: ac.signal });
|
|
135276
|
+
if (results.some((res) => !res.ok))
|
|
135277
|
+
hadError = true;
|
|
135278
|
+
} else {
|
|
135279
|
+
onEvent({ type: "phase", label: "verification skipped", detail: "no test/build/typecheck command detected", state: "err" });
|
|
135280
|
+
}
|
|
135281
|
+
}
|
|
134892
135282
|
let modelId = activeCliRef.current?.id ?? routedRef.current?.model.id;
|
|
134893
135283
|
if (!modelId) {
|
|
134894
135284
|
try {
|
|
@@ -135593,7 +135983,31 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
|
|
|
135593
135983
|
notice("busy — try /init again once the current turn finishes");
|
|
135594
135984
|
return;
|
|
135595
135985
|
}
|
|
135596
|
-
|
|
135986
|
+
echo(text2);
|
|
135987
|
+
setBusy(true);
|
|
135988
|
+
setSuggestion(null);
|
|
135989
|
+
(async () => {
|
|
135990
|
+
const id = idRef.current++;
|
|
135991
|
+
try {
|
|
135992
|
+
push({ kind: "tool", id, callId: `init:${id}`, name: "write_file", arg: "GEARBOX.md", status: "running", summary: "", startedAt: Date.now() });
|
|
135993
|
+
const res = writeProjectGuide(process.cwd());
|
|
135994
|
+
setItems((prev) => prev.map((i2) => i2.id === id && i2.kind === "tool" ? { ...i2, status: "ok", summary: res.summary, diff: res.diff, endedAt: Date.now() } : i2));
|
|
135995
|
+
const commands = detectVerificationCommands(process.cwd(), ["GEARBOX.md"]);
|
|
135996
|
+
if (commands.length)
|
|
135997
|
+
await runVerification(commands.slice(0, 1), { onEvent: (e2) => {
|
|
135998
|
+
if (e2.type === "verification")
|
|
135999
|
+
push({ kind: "verification", id: idRef.current++, command: e2.command, ok: e2.ok, summary: e2.summary });
|
|
136000
|
+
else if (e2.type === "phase")
|
|
136001
|
+
push({ kind: "phase", id: idRef.current++, label: e2.label, detail: e2.detail, state: e2.state ?? "running" });
|
|
136002
|
+
} });
|
|
136003
|
+
notice("initialized GEARBOX.md");
|
|
136004
|
+
persist();
|
|
136005
|
+
} catch (e2) {
|
|
136006
|
+
setItems((prev) => prev.map((i2) => i2.id === id && i2.kind === "tool" ? { ...i2, status: "err", summary: e2?.message ?? String(e2), endedAt: Date.now() } : i2));
|
|
136007
|
+
} finally {
|
|
136008
|
+
setBusy(false);
|
|
136009
|
+
}
|
|
136010
|
+
})();
|
|
135597
136011
|
return;
|
|
135598
136012
|
default: {
|
|
135599
136013
|
echo(text2);
|
|
@@ -135871,7 +136285,7 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
|
|
|
135871
136285
|
if (!busyRef.current && input.length > 3 && !input.includes(`
|
|
135872
136286
|
`)) {
|
|
135873
136287
|
const p = sanitizeInputText(input).trim().replace(/^'|'$/g, "").replace(/\\ /g, " ");
|
|
135874
|
-
if (/[/\\.]/.test(p) && p.length < 1024 &&
|
|
136288
|
+
if (/[/\\.]/.test(p) && p.length < 1024 && existsSync9(p)) {
|
|
135875
136289
|
const e2 = editRef.current;
|
|
135876
136290
|
const ins = `@${p} `;
|
|
135877
136291
|
setEdit({ value: e2.value.slice(0, e2.cursor) + ins + e2.value.slice(e2.cursor), cursor: e2.cursor + ins.length });
|
|
@@ -136281,7 +136695,7 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
|
|
|
136281
136695
|
var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
|
|
136282
136696
|
process.env.LANG = process.env.LANG || "en_US.UTF-8";
|
|
136283
136697
|
process.env.LC_ALL = process.env.LC_ALL || "en_US.UTF-8";
|
|
136284
|
-
var VERSION16 = "0.1.
|
|
136698
|
+
var VERSION16 = "0.1.11";
|
|
136285
136699
|
var args = process.argv.slice(2);
|
|
136286
136700
|
var supportsAnsi = process.env.NO_COLOR !== "1" && process.env.TERM !== "dumb" && (process.stdout.isTTY || process.env.FORCE_COLOR === "1");
|
|
136287
136701
|
var ansi = (code) => supportsAnsi ? `\x1B[${code}m` : "";
|
|
@@ -136499,7 +136913,7 @@ async function readStdin() {
|
|
|
136499
136913
|
}
|
|
136500
136914
|
if (args[0] === "upgrade" || args[0] === "update") {
|
|
136501
136915
|
const root2 = resolve11(import.meta.dir, "..");
|
|
136502
|
-
if (!
|
|
136916
|
+
if (!existsSync10(resolve11(root2, ".git"))) {
|
|
136503
136917
|
console.log("This build can't self-update (not a git checkout).");
|
|
136504
136918
|
console.log("Update by pulling the repo and reinstalling: git pull && bun install");
|
|
136505
136919
|
process.exit(0);
|
|
@@ -136539,6 +136953,8 @@ Set up at least one provider first:
|
|
|
136539
136953
|
gearbox onboard
|
|
136540
136954
|
gearbox auth add <api-key>
|
|
136541
136955
|
gearbox auth add <provider> <api-key>
|
|
136956
|
+
gearbox auth add codex [name]
|
|
136957
|
+
gearbox auth add claude [name]
|
|
136542
136958
|
gearbox auth import
|
|
136543
136959
|
|
|
136544
136960
|
Models: ${MODELS.map((m2) => m2.label).join(", ")}
|
|
@@ -136556,7 +136972,8 @@ if (args[0] === "onboard" || args[0] === "setup") {
|
|
|
136556
136972
|
if (args[0] === "auth") {
|
|
136557
136973
|
const { listAccounts: listAccounts2, loadAccounts: loadAccounts3, removeAccount: removeAccount2 } = await Promise.resolve().then(() => (init_store(), exports_store));
|
|
136558
136974
|
const { importableEnvCreds: importableEnvCreds2, importEnvCred: importEnvCred2, importableCloudCreds: importableCloudCreds2, importCloudCred: importCloudCred2 } = await Promise.resolve().then(() => (init_detect(), exports_detect));
|
|
136559
|
-
const { addApiKeyAccount: addApiKeyAccount2, addByPastedKey: addByPastedKey2, testAccount: testAccount2, addableProviders: addableProviders2 } = await Promise.resolve().then(() => (init_onboard(), exports_onboard));
|
|
136975
|
+
const { addApiKeyAccount: addApiKeyAccount2, addByPastedKey: addByPastedKey2, testAccount: testAccount2, addableProviders: addableProviders2, addCliAccount: addCliAccount2, cliAuthStatus: cliAuthStatus2, cliLoginArgs: cliLoginArgs2 } = await Promise.resolve().then(() => (init_onboard(), exports_onboard));
|
|
136976
|
+
const { subscriptionEnv: subscriptionEnv2 } = await Promise.resolve().then(() => (init_cli_backend(), exports_cli_backend));
|
|
136560
136977
|
const { detectProviderByKey: detectProviderByKey2 } = await Promise.resolve().then(() => (init_catalog(), exports_catalog));
|
|
136561
136978
|
const sub = args[1];
|
|
136562
136979
|
const rest2 = args.slice(2);
|
|
@@ -136580,11 +136997,25 @@ Importable from your env (gearbox auth import): ${imp.map((c) => c.envVar).join(
|
|
|
136580
136997
|
const names = [...keys2.map((c) => c.provider), ...cloud.map((c) => c.provider)];
|
|
136581
136998
|
console.log(names.length ? `Imported ${names.length}: ${names.join(", ")}` : "Nothing to import.");
|
|
136582
136999
|
} else if (sub === "add") {
|
|
136583
|
-
const
|
|
137000
|
+
const head2 = (rest2[0] ?? "").toLowerCase();
|
|
137001
|
+
const cliProvider = head2 === "codex" || head2 === "chatgpt" ? "codex-cli" : head2 === "claude" ? "claude-cli" : "";
|
|
137002
|
+
const res = cliProvider ? addCliAccount2(cliProvider, rest2.slice(1).join(" ").trim() || undefined) : rest2[0] && !rest2[1] && detectProviderByKey2(rest2[0]) ? await addByPastedKey2(rest2[0]) : rest2[0] && rest2[1] ? await addApiKeyAccount2(rest2[0], rest2[1]) : { ok: false, message: "usage: gearbox auth add <key> | gearbox auth add <provider> <key> | gearbox auth add codex [name]" };
|
|
136584
137003
|
console.log(res.message);
|
|
136585
137004
|
if (res.ok && res.account) {
|
|
136586
|
-
|
|
136587
|
-
|
|
137005
|
+
if (res.account.exec === "cli" && res.account.auth.kind === "cli") {
|
|
137006
|
+
const bin = res.account.auth.binary;
|
|
137007
|
+
const profile = res.account.auth.loginProfile;
|
|
137008
|
+
let st = await cliAuthStatus2(bin, profile);
|
|
137009
|
+
if (!st.loggedIn) {
|
|
137010
|
+
console.log(` sign-in: starting ${bin} ${cliLoginArgs2(bin).join(" ")}`);
|
|
137011
|
+
spawnSync(bin, cliLoginArgs2(bin), { stdio: "inherit", env: subscriptionEnv2(bin, profile) });
|
|
137012
|
+
st = await cliAuthStatus2(bin, profile);
|
|
137013
|
+
}
|
|
137014
|
+
console.log(st.loggedIn ? ` sign-in: ✓ ${st.detail ?? "ready"}` : ` sign-in: ✗ not signed in${st.detail ? ` (${st.detail})` : ""}`);
|
|
137015
|
+
} else {
|
|
137016
|
+
const t2 = await testAccount2(res.account);
|
|
137017
|
+
console.log(t2.ok ? " test: ✓ " + t2.message : " test: ✗ " + t2.message + " (stored anyway)");
|
|
137018
|
+
}
|
|
136588
137019
|
}
|
|
136589
137020
|
} else if (sub === "test" && rest2[0]) {
|
|
136590
137021
|
const a = listAccounts2().find((x2) => x2.id === rest2[0]);
|
|
@@ -136596,7 +137027,7 @@ Importable from your env (gearbox auth import): ${imp.map((c) => c.envVar).join(
|
|
|
136596
137027
|
for (const p of addableProviders2())
|
|
136597
137028
|
console.log(`${p.id.padEnd(16)} ${p.label} (${p.group})`);
|
|
136598
137029
|
} else {
|
|
136599
|
-
console.log("gearbox auth [list|import|add <key>|add <provider> <key>|test <id>|rm <id>|providers]");
|
|
137030
|
+
console.log("gearbox auth [list|import|add <key>|add <provider> <key>|add codex [name]|add claude [name]|test <id>|rm <id>|providers]");
|
|
136600
137031
|
}
|
|
136601
137032
|
process.exit(0);
|
|
136602
137033
|
}
|
package/package.json
CHANGED