treefmt-check
checks.aarch64-linux.formatting
· build #602
· raw
1tribuchet: building on eliza2treefmt v2.6.0traversed 1126 files3emitted 455 files for processing4formatted 455 files (4 changed) in 1.865s5 M home/.pi/agent/extensions/inbox.ts6 M home/.pi/agent/extensions/python/index.ts7diff --git a/home/.pi/agent/extensions/inbox.ts b/home/.pi/agent/extensions/inbox.ts8index 0b5f038..f693974 1006449--- a/home/.pi/agent/extensions/inbox.ts10+++ b/home/.pi/agent/extensions/inbox.ts11@@ -30,15 +30,25 @@ function parse(raw: string): Event {12 }13 14 export default function (pi: ExtensionAPI) {15- const path = join(process.env.XDG_RUNTIME_DIR ?? "/tmp", "pi-inbox", `${process.pid}.sock`);16+ const path = join(17+ process.env.XDG_RUNTIME_DIR ?? "/tmp",18+ "pi-inbox",19+ `${process.pid}.sock`,20+ );21 22 pi.registerMessageRenderer("inbox", (message, _options, theme) => {23 const ev = message.details as Event;24 let lines = ev.text.split("\n");25 if (lines.length > TAIL_LINES) {26- lines = [`… ${lines.length - TAIL_LINES} lines`, ...lines.slice(-TAIL_LINES)];27+ lines = [28+ `… ${lines.length - TAIL_LINES} lines`,29+ ...lines.slice(-TAIL_LINES),30+ ];31 }32- const title = theme.fg("accent", theme.bold(`▌inbox ${ev.source ?? ""}`.trimEnd()));33+ const title = theme.fg(34+ "accent",35+ theme.bold(`▌inbox ${ev.source ?? ""}`.trimEnd()),36+ );37 return new Text(`${title}\n${theme.fg("muted", lines.join("\n"))}`, 0, 0);38 });39 40@@ -46,20 +56,27 @@ export default function (pi: ExtensionAPI) {41 mkdirSync(join(path, ".."), { recursive: true, mode: 0o700 });42 rmSync(path, { force: true });43 const server = createServer(async (conn) => {44- const raw = (await text(conn).catch(() => "")).slice(0, MAX_BYTES);45- const ev = parse(raw);46- if (!ev.text) return;47- const header = ev.source ? `[inbox: ${ev.source}]` : "[inbox]";48- pi.sendMessage(49- { customType: "inbox", content: `${header}\n${ev.text}`, display: true, details: ev },50- { deliverAs: "followUp", triggerTurn: true },51- );52+ const raw = (await text(conn).catch(() => "")).slice(0, MAX_BYTES);53+ const ev = parse(raw);54+ if (!ev.text) return;55+ const header = ev.source ? `[inbox: ${ev.source}]` : "[inbox]";56+ pi.sendMessage(57+ {58+ customType: "inbox",59+ content: `${header}\n${ev.text}`,60+ display: true,61+ details: ev,62+ },63+ { deliverAs: "followUp", triggerTurn: true },64+ );65 }).listen(path);66 process.env.PI_INBOX = path;67 68 pi.on("session_start", async (_event, ctx) => {69 const file = ctx.sessionManager.getSessionFile();70- process.env.PI_SESSION_ID = file ? basename(file, ".jsonl") : `pid-${process.pid}`;71+ process.env.PI_SESSION_ID = file72+ ? basename(file, ".jsonl")73+ : `pid-${process.pid}`;74 });75 76 pi.on("session_shutdown", async () => {77diff --git a/home/.pi/agent/extensions/python/index.ts b/home/.pi/agent/extensions/python/index.ts78index ca86ebf..a841c8a 10064479--- a/home/.pi/agent/extensions/python/index.ts80+++ b/home/.pi/agent/extensions/python/index.ts81@@ -58,121 +58,127 @@ export default function (pi: ExtensionAPI) {82 registerTool();83 });84 85- const registerTool = () => pi.registerTool({86- name: "python",87- label: "python",88- description:89- "Execute Python code in a persistent interpreter: variables, imports and open files survive between calls for the whole session. " +90- "The value of a trailing expression is echoed like in a REPL; use print() for anything else. " +91- "matplotlib figures are returned as images. Available: polars, matplotlib, requests, plumbum, pexpect, pyelftools + stdlib. " +92- `Output is truncated to the last ${DEFAULT_MAX_LINES} lines / ${93- formatSize(DEFAULT_MAX_BYTES)94- }.`,95- promptSnippet:96- "Run Python in a persistent interpreter (state kept across calls; polars, matplotlib, requests, pexpect, pyelftools)",97- promptGuidelines: [98- "Use python instead of bash for multi-step data work (JSON/CSV/logs/ELF), calculations, and anything where re-parsing input on every call would be wasteful; state persists, so load once and iterate.",99- "Drive interactive programs (ssh, REPLs, debuggers, installers) with pexpect inside the python tool: `child = pexpect.spawn(cmd, encoding='utf-8')` persists across calls; always pass `timeout=` to expect().",100- ...(process.env.PI_INBOX101- ? [102- "Don't block on long waits in python: run them in a `threading.Thread` that calls the predefined `notify(text, source=\"python\")` when done. It arrives later as an `[inbox: <source>]` message.",103- ]104- : []),105- ],106- parameters: Type.Object({107- code: Type.String({ description: "Python source to execute" }),108- timeout: Type.Optional(109- Type.Number({110- description: "Timeout in seconds (interrupts the cell, keeps state)",111- }),112- ),113- }),114+ const registerTool = () =>115+ pi.registerTool({116+ name: "python",117+ label: "python",118+ description:119+ "Execute Python code in a persistent interpreter: variables, imports and open files survive between calls for the whole session. " +120+ "The value of a trailing expression is echoed like in a REPL; use print() for anything else. " +121+ "matplotlib figures are returned as images. Available: polars, matplotlib, requests, plumbum, pexpect, pyelftools + stdlib. " +122+ `Output is truncated to the last ${DEFAULT_MAX_LINES} lines / ${123+ formatSize(DEFAULT_MAX_BYTES)124+ }.`,125+ promptSnippet:126+ "Run Python in a persistent interpreter (state kept across calls; polars, matplotlib, requests, pexpect, pyelftools)",127+ promptGuidelines: [128+ "Use python instead of bash for multi-step data work (JSON/CSV/logs/ELF), calculations, and anything where re-parsing input on every call would be wasteful; state persists, so load once and iterate.",129+ "Drive interactive programs (ssh, REPLs, debuggers, installers) with pexpect inside the python tool: `child = pexpect.spawn(cmd, encoding='utf-8')` persists across calls; always pass `timeout=` to expect().",130+ ...(process.env.PI_INBOX131+ ? [132+ 'Don\'t block on long waits in python: run them in a `threading.Thread` that calls the predefined `notify(text, source="python")` when done. It arrives later as an `[inbox: <source>]` message.',133+ ]134+ : []),135+ ],136+ parameters: Type.Object({137+ code: Type.String({ description: "Python source to execute" }),138+ timeout: Type.Optional(139+ Type.Number({140+ description:141+ "Timeout in seconds (interrupts the cell, keeps state)",142+ }),143+ ),144+ }),145 146- // Without this pi's fallback renders only the tool name, hiding the code.147- renderCall(args, theme, context) {148- const text = (context.lastComponent as Text | undefined) ??149- new Text("", 0, 0);150- let out = theme.fg("toolTitle", theme.bold("python"));151- if (args.timeout) out += theme.fg("muted", ` (timeout ${args.timeout}s)`);152- const code = (args.code ?? "").trimEnd();153- if (code) out += "\n" + highlightCode(code, "python").join("\n");154- text.setText(out);155- return text;156- },157+ // Without this pi's fallback renders only the tool name, hiding the code.158+ renderCall(args, theme, context) {159+ const text = (context.lastComponent as Text | undefined) ??160+ new Text("", 0, 0);161+ let out = theme.fg("toolTitle", theme.bold("python"));162+ if (args.timeout) {163+ out += theme.fg("muted", ` (timeout ${args.timeout}s)`);164+ }165+ const code = (args.code ?? "").trimEnd();166+ if (code) out += "\n" + highlightCode(code, "python").join("\n");167+ text.setText(out);168+ return text;169+ },170 171- renderResult(result, options, theme, context) {172- const text = (context.lastComponent as Text | undefined) ??173- new Text("", 0, 0);174- const output = result.content175- .filter((c) => c.type === "text")176- .map((c) => (c as { text: string }).text)177- .join("\n")178- .trimEnd();179- const images = result.content.filter((c) => c.type === "image").length;180- const lines = output ? output.split("\n") : [];181- // Tail, not head: tracebacks and REPL results come last.182- const shown = options.expanded ? lines : lines.slice(-10);183- const color = context.isError ? "error" : "toolOutput";184- let out = theme.fg("muted", "─── output ───");185- if (lines.length > shown.length) {186- out += theme.fg(187- "muted",188- `\n... (${lines.length - shown.length} earlier lines, `,189- ) + keyHint("app.tools.expand", "to expand") + theme.fg("muted", ")");190- }191- if (shown.length) {192- out += "\n" + shown.map((l) => theme.fg(color, l)).join("\n");193- }194- if (images) out += theme.fg("muted", `\n[${images} image(s)]`);195- text.setText(out);196- return text;197- },198+ renderResult(result, options, theme, context) {199+ const text = (context.lastComponent as Text | undefined) ??200+ new Text("", 0, 0);201+ const output = result.content202+ .filter((c) => c.type === "text")203+ .map((c) => (c as { text: string }).text)204+ .join("\n")205+ .trimEnd();206+ const images = result.content.filter((c) => c.type === "image").length;207+ const lines = output ? output.split("\n") : [];208+ // Tail, not head: tracebacks and REPL results come last.209+ const shown = options.expanded ? lines : lines.slice(-10);210+ const color = context.isError ? "error" : "toolOutput";211+ let out = theme.fg("muted", "─── output ───");212+ if (lines.length > shown.length) {213+ out += theme.fg(214+ "muted",215+ `\n... (${lines.length - shown.length} earlier lines, `,216+ ) + keyHint("app.tools.expand", "to expand") + theme.fg("muted", ")");217+ }218+ if (shown.length) {219+ out += "\n" + shown.map((l) => theme.fg(color, l)).join("\n");220+ }221+ if (images) out += theme.fg("muted", `\n[${images} image(s)]`);222+ text.setText(out);223+ return text;224+ },225 226- async execute(_id, params, signal, _onUpdate, ctx) {227- kernel ??= new Kernel(python, ctx.cwd);228- const ac = new AbortController();229- signal?.addEventListener("abort", () => ac.abort(), { once: true });230- let timedOut = false;231- const timer = params.timeout232- ? setTimeout(() => {233- timedOut = true;234- ac.abort();235- }, params.timeout * 1000)236- : undefined;237- const r = await kernel.exec(params.code, ac.signal).finally(() =>238- clearTimeout(timer)239- );240- // Throw like bash so pi ends the run as "aborted" and sends queued prompts.241- if (signal?.aborted) throw new Error("Command aborted");242- if (timedOut && r.error?.startsWith("KeyboardInterrupt")) {243- r.error =244- `Timed out after ${params.timeout}s (cell interrupted, interpreter state kept)`;245- }246+ async execute(_id, params, signal, _onUpdate, ctx) {247+ kernel ??= new Kernel(python, ctx.cwd);248+ const ac = new AbortController();249+ signal?.addEventListener("abort", () => ac.abort(), { once: true });250+ let timedOut = false;251+ const timer = params.timeout252+ ? setTimeout(() => {253+ timedOut = true;254+ ac.abort();255+ }, params.timeout * 1000)256+ : undefined;257+ const r = await kernel.exec(params.code, ac.signal).finally(() =>258+ clearTimeout(timer)259+ );260+ // Throw like bash so pi ends the run as "aborted" and sends queued prompts.261+ if (signal?.aborted) throw new Error("Command aborted");262+ if (timedOut && r.error?.startsWith("KeyboardInterrupt")) {263+ r.error =264+ `Timed out after ${params.timeout}s (cell interrupted, interpreter state kept)`;265+ }266 267- let text = r.stdout;268- if (r.stderr) {269- text += (text && !text.endsWith("\n") ? "\n" : "") + r.stderr;270- }271- if (r.result !== null) {272- text += (text && !text.endsWith("\n") ? "\n" : "") + r.result;273- }274- if (r.error) text += (text && !text.endsWith("\n") ? "\n" : "") + r.error;275- const t = truncateTail(text || (r.images.length ? "" : "(no output)"));276- if (t.truncated) {277- t.content =278- `[output truncated: showing last ${t.outputLines} of ${t.totalLines} lines]\n${t.content}`;279- }280+ let text = r.stdout;281+ if (r.stderr) {282+ text += (text && !text.endsWith("\n") ? "\n" : "") + r.stderr;283+ }284+ if (r.result !== null) {285+ text += (text && !text.endsWith("\n") ? "\n" : "") + r.result;286+ }287+ if (r.error) {288+ text += (text && !text.endsWith("\n") ? "\n" : "") + r.error;289+ }290+ const t = truncateTail(text || (r.images.length ? "" : "(no output)"));291+ if (t.truncated) {292+ t.content =293+ `[output truncated: showing last ${t.outputLines} of ${t.totalLines} lines]\n${t.content}`;294+ }295 296- const content: ({ type: "text"; text: string } | {297- type: "image";298- data: string;299- mimeType: string;300- })[] = [];301- if (t.content) content.push({ type: "text", text: t.content });302- for (const data of r.images) {303- content.push({ type: "image", data, mimeType: "image/png" });304- }305- return { content, details: undefined, isError: r.error !== null };306- },307- });308+ const content: ({ type: "text"; text: string } | {309+ type: "image";310+ data: string;311+ mimeType: string;312+ })[] = [];313+ if (t.content) content.push({ type: "text", text: t.content });314+ for (const data of r.images) {315+ content.push({ type: "image", data, mimeType: "image/png" });316+ }317+ return { content, details: undefined, isError: r.error !== null };318+ },319+ });320 }