From 5b3239e5ea5e53e9b2a7610195c31d7ff20081e1 Mon Sep 17 00:00:00 2001 From: Ratul Maharaj <56479869+RatulMaharaj@users.noreply.github.com> Date: Sun, 5 Jul 2026 21:22:01 +0200 Subject: [PATCH 1/2] feat(cli): add `af update` command, switch accent to brand purple Reinstalls the CLI at the latest JSR version via `deno install -f`. Also swaps the cyan accent for the docs' primary purple (#685EF6) and aligns success/warning/error colors with the brand palette. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01715nBsHA6Y5pAqkkQxnsEE --- packages/cli/README.md | 2 +- packages/cli/banner.ts | 4 +-- packages/cli/main.ts | 6 +++++ packages/cli/style.ts | 8 +++--- packages/cli/update_command.ts | 45 ++++++++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 packages/cli/update_command.ts diff --git a/packages/cli/README.md b/packages/cli/README.md index 5f93d71..3eb5143 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -16,4 +16,4 @@ af validate # check the file and report which env vars it references af run # run it in Docker; a REPL without triggers, a service with them ``` -`af up -d` starts one or more agents in the background, `af ps` lists them and `af down` stops them. The full command reference lives at [docs.looped.sh/agent-framework/cli](https://docs.looped.sh/agent-framework/cli). +`af up -d` starts one or more agents in the background, `af ps` lists them and `af down` stops them. Run `af update` to reinstall the CLI at the latest published version. The full command reference lives at [docs.looped.sh/agent-framework/cli](https://docs.looped.sh/agent-framework/cli). diff --git a/packages/cli/banner.ts b/packages/cli/banner.ts index 1fd4a0d..99414b3 100644 --- a/packages/cli/banner.ts +++ b/packages/cli/banner.ts @@ -1,7 +1,7 @@ // The first-boot banner: shown once per agent, on the run where it names // itself. A little something for the devs. -import { LOOP_GRADIENT } from "./style.ts"; +import { ACCENT, LOOP_GRADIENT } from "./style.ts"; const ART = String.raw` ██╗ ██████╗ ██████╗ ██████╗ ███████╗██████╗ █████╗ ███████╗ @@ -26,7 +26,7 @@ export function printBirthBanner(handle: string, name: string) { ` ✦ first boot — %c${handle}%c has chosen a name: %c${name}%c\n`, "color: gray", "", - "color: #22d3ee; font-weight: bold", + `color: ${ACCENT}; font-weight: bold`, "", ); } diff --git a/packages/cli/main.ts b/packages/cli/main.ts index 7689ed5..cbe643e 100644 --- a/packages/cli/main.ts +++ b/packages/cli/main.ts @@ -6,6 +6,7 @@ * af up start agents in Docker (foreground; -d to detach) * af down / af ps stop / list af containers * af validate validate a config and report env references + * af update reinstall af at the latest published version * ``` * * Nothing executes on the host: run/up start the published container image. @@ -36,6 +37,7 @@ import { import { init } from "./init_command.ts"; import { runLocal, validate } from "./local.ts"; import { accent, dim, table } from "./style.ts"; +import { update } from "./update_command.ts"; // Set by the image: `run` executes in-process (the container entrypoint) // instead of spawning docker inside docker. @@ -52,6 +54,7 @@ function usage(): string { ["af flags [agent.yaml]", "Print compiled Deno permission flags"], ["af schema", "Print the agent.yaml JSON Schema"], ["af discord-invite [agent.yaml]", "Print the bot's OAuth invite URL (no bitfield math)"], + ["af update", "Reinstall af at the latest published version"], ]; const flags: [string, string][] = [ ["-d, --detach", "up: leave agents running in the background (restart unless-stopped)"], @@ -113,6 +116,9 @@ async function main() { if (!arg) fail("usage: af discord-invite [agent.yaml]"); await discordInvite(arg); break; + case "update": + await update(); + break; default: console.log(usage()); Deno.exit(command && command !== "--help" && command !== "-h" ? 1 : 0); diff --git a/packages/cli/style.ts b/packages/cli/style.ts index 7fdc61d..5cd7a7c 100644 --- a/packages/cli/style.ts +++ b/packages/cli/style.ts @@ -5,10 +5,10 @@ /** The Looped loop: cyan → violet. The banner paints with it; log prefixes cycle it. */ export const LOOP_GRADIENT = ["#22d3ee", "#38bdf8", "#60a5fa", "#818cf8", "#a78bfa", "#c084fc"]; -export const ACCENT = LOOP_GRADIENT[0]; // Looped cyan -const OK = "#34d399"; -const WARN = "#fbbf24"; -const ERR = "#f87171"; +export const ACCENT = "#685EF6"; // Looped primary purple +const OK = "#37946A"; +const WARN = "#ED9B00"; +const ERR = "#D02E1F"; function detectColor(): boolean { try { diff --git a/packages/cli/update_command.ts b/packages/cli/update_command.ts new file mode 100644 index 0000000..66c5fc5 --- /dev/null +++ b/packages/cli/update_command.ts @@ -0,0 +1,45 @@ +// `af update` — reinstall the CLI from JSR at its latest published version. +// Mirrors the install command from the README, with -f to overwrite the +// existing binary and @latest to bypass any cached resolution. + +import { fail } from "./docker_commands.ts"; +import { ok, Spinner } from "./style.ts"; + +const INSTALL_ARGS = [ + "install", + "-g", + "-f", + "--allow-read", + "--allow-write", + "--allow-env", + "--allow-net", + "--allow-run=bash,docker", + "-n", + "af", + "jsr:@looped/af@latest", +]; + +export async function update() { + const spinner = new Spinner(); + spinner.start("updating af..."); + try { + const child = new Deno.Command("deno", { + args: INSTALL_ARGS, + stdout: "piped", + stderr: "piped", + stdin: "null", + }); + const out = await child.output(); + if (out.code !== 0) { + spinner.stop(); + fail(new TextDecoder().decode(out.stderr).trim() || "deno install failed"); + } + spinner.stop(`${ok("✓")} af is up to date`); + } catch (e) { + spinner.stop(); + if (e instanceof Deno.errors.NotFound) { + fail("deno not found — af update reinstalls via `deno install`"); + } + throw e; + } +} From e291ecda30989f689deae4fc0368ad23e5f003fc Mon Sep 17 00:00:00 2001 From: Ratul Maharaj <56479869+RatulMaharaj@users.noreply.github.com> Date: Sun, 5 Jul 2026 21:32:41 +0200 Subject: [PATCH 2/2] =?UTF-8?q?fix(cli):=20drop=20the=20"one=20job=20?= =?UTF-8?q?=C2=B7=20one=20file=20=C2=B7=20it's=20hired"=20tagline=20from?= =?UTF-8?q?=20the=20birth=20banner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01715nBsHA6Y5pAqkkQxnsEE --- packages/cli/banner.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/cli/banner.ts b/packages/cli/banner.ts index 99414b3..8879bb7 100644 --- a/packages/cli/banner.ts +++ b/packages/cli/banner.ts @@ -17,11 +17,7 @@ export function printBirthBanner(handle: string, name: string) { for (let i = 0; i < lines.length; i++) { console.log(`%c${lines[i]}`, `color: ${LOOP_GRADIENT[i % LOOP_GRADIENT.length]}`); } - console.log( - `\n %cone job · one file · it’s hired%c looped.sh\n`, - "color: gray; font-style: italic", - "color: gray", - ); + console.log(); console.log( ` ✦ first boot — %c${handle}%c has chosen a name: %c${name}%c\n`, "color: gray",