diff --git a/.pi/extensions/rtk.ts b/.pi/extensions/rtk.ts new file mode 100644 index 0000000..32cb2b5 --- /dev/null +++ b/.pi/extensions/rtk.ts @@ -0,0 +1,80 @@ +// RTK Pi extension — rewrites bash commands to use rtk for token savings. +// Requires: rtk >= 0.23.0 in PATH. +// +// This is a thin delegating extension: all rewrite logic lives in `rtk rewrite`, +// which is the single source of truth (src/discover/registry.rs). +// To add or change rewrite rules, edit the Rust registry — not this file. +// +// Exit code contract for `rtk rewrite`: +// 0 + stdout Rewrite found → mutate command +// 1 No RTK equivalent → pass through unchanged +// 3 + stdout Rewrite (advisory) → mutate command + +import type { ExtensionAPI } from "@earendil-works/pi-coding-agent" +import { isToolCallEventType } from "@earendil-works/pi-coding-agent" + +const REWRITE_TIMEOUT_MS = 2_000 +const MIN_SUPPORTED_RTK_MINOR = 23 + +// Parse "X.Y.Z" semver, return [major, minor, patch] or null. +function parseSemver(raw: string): [number, number, number] | null { + const m = raw.trim().match(/(\d+)\.(\d+)\.(\d+)/) + if (!m) return null + return [parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10)] +} + +// Calls `rtk rewrite`; returns the rewritten command or null (pass through). +async function rewriteCommand( + pi: ExtensionAPI, + cmd: string, + signal?: AbortSignal +): Promise { + const result = await pi.exec("rtk", ["rewrite", cmd], { + timeout: REWRITE_TIMEOUT_MS, + signal, + }) + if (result.killed) return null + if (result.code !== 0 && result.code !== 3) return null + return result.stdout.trim() || null +} + +export default async function (pi: ExtensionAPI) { + // Probe rtk version at load time; disables extension if missing or too old. + const ver = await pi.exec("rtk", ["--version"], { timeout: REWRITE_TIMEOUT_MS }) + if (ver.code !== 0) { + console.warn("[rtk] rtk binary not found in PATH — extension disabled") + return + } + + // Warn and bail if rtk predates 0.23.0 (when `rtk rewrite` was introduced). + const parsed = parseSemver(ver.stdout.replace(/^rtk\s+/, "")) + if (parsed) { + const [major, minor] = parsed + if (major === 0 && minor < MIN_SUPPORTED_RTK_MINOR) { + console.warn(`[rtk] rtk ${ver.stdout.trim()} is too old (need >= 0.23.0) — extension disabled`) + return + } + } + + pi.on("tool_call", async (event, ctx) => { + try { + if (!isToolCallEventType("bash", event)) return + + const cmd = event.input.command + if (typeof cmd !== "string" || cmd.trim() === "") return + + if (cmd.startsWith("rtk ")) return + if (process.env.RTK_DISABLED === "1") return + + // Delegate to RTK. + const rewritten = await rewriteCommand(pi, cmd, ctx.signal) + if (rewritten && rewritten !== cmd) { + event.input.command = rewritten + } + } catch (err) { + // Fail open: never block execution on an unexpected error. + console.warn("[rtk] unexpected error in tool_call handler; passing through command", err) + return + } + }) +} diff --git a/AGENTS.md b/AGENTS.md index 13ae9e1..d882009 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,8 @@ # Monorepo of rssbase.io > RSSBase is a RSS feed reader, using [atproto](https://atproto.com/) to store user data. +@RTK.md + ## Convention - name: RSSBase - url: rssbase.io diff --git a/README.md b/README.md index 1fded49..d0ce2b4 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ else fi # Get the codebase -git clone git@tangled.org:mykiwi.dev/rssbase && cd rssbase +git clone git@tangled.org:mykiwi.dev/rssbase.io && cd rssbase.io devenv allow # Your developer environment is ready 👨‍🚀🚀 @@ -50,7 +50,12 @@ Do not keep `cachix.push` enabled in `devenv.local.nix`: it slows normal shell e ```bash CACHIX_AUTH_TOKEN=... devenv shell cachix-push ``` -
+
+ +## Roadmap (project in very early stages) +- [ ] deployed +- [ ] add feeds +- [ ] list feeds [devenv]: https://devenv.sh [nix]: https://search.nixos.org/packages diff --git a/RTK.md b/RTK.md new file mode 100644 index 0000000..7ae285e --- /dev/null +++ b/RTK.md @@ -0,0 +1,32 @@ +# RTK - Rust Token Killer (Codex CLI) + +**Usage**: Token-optimized CLI proxy for shell commands. + +## Rule + +Always prefix shell commands with `rtk`. + +Examples: + +```bash +rtk git status +rtk cargo test +rtk npm run build +rtk pytest -q +``` + +## Meta Commands + +```bash +rtk gain # Token savings analytics +rtk gain --history # Recent command savings history +rtk proxy # Run raw command without filtering +``` + +## Verification + +```bash +rtk --version +rtk gain +which rtk +``` diff --git a/devenv.lock b/devenv.lock index 07af2c8..78246aa 100644 --- a/devenv.lock +++ b/devenv.lock @@ -3,11 +3,11 @@ "devenv": { "locked": { "dir": "src/modules", - "lastModified": 1782492839, - "narHash": "sha256-j9wrcB4al5QhMelEghJ0Qs+RQPT+wyCcI4070NEgPLQ=", + "lastModified": 1783109842, + "narHash": "sha256-Fi7lW+sg1ChduOOQRBCK5JeA1cw4x5wSjIJcppRGGqQ=", "owner": "cachix", "repo": "devenv", - "rev": "3d39d0817d62069f7b18821c34a617b5141cb278", + "rev": "e30eb49258bee68353bd9c619823f635f4afa86c", "type": "github" }, "original": { @@ -22,11 +22,11 @@ "nixpkgs-src": "nixpkgs-src" }, "locked": { - "lastModified": 1782132010, - "narHash": "sha256-ZnAVHdVrotp80iIMm5CSR1fdxPlw7Uwmwxb+O/wsgZ8=", + "lastModified": 1782924808, + "narHash": "sha256-tn2ahNv3ZNXyVHdPx/uw+N0xa7YSMtXUr6OEvu+s8ng=", "owner": "cachix", "repo": "devenv-nixpkgs", - "rev": "12866ae2dddbc0ab8b329915f8072bb9c75bde89", + "rev": "e2c881cb8d5f1cac6cef9d71f0d450a55691b999", "type": "github" }, "original": { @@ -39,11 +39,11 @@ "nixpkgs-src": { "flake": false, "locked": { - "lastModified": 1781607440, - "narHash": "sha256-rxO+uc/KFbSJp+pgyXRuAX6QlG9hJdnt0BXpEQRXY+U=", + "lastModified": 1782636521, + "narHash": "sha256-OG8laCOGtkxlB1JH3XqOnXxnkGJme4mQdXo5hkhCQIY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3e41b24abd260e8f71dbe2f5737d24122f972158", + "rev": "e1c1b84752fb0897897380a3cae9dc7fcab91ca3", "type": "github" }, "original": { diff --git a/devenv.nix b/devenv.nix index fab5f58..80e61b2 100644 --- a/devenv.nix +++ b/devenv.nix @@ -4,8 +4,8 @@ }: let - dagger = pkgs.callPackage ./dagger { }; - tang = pkgs.callPackage ./tangled-cli { }; + dagger = pkgs.callPackage ./nix-dagger { }; + tang = pkgs.callPackage ./nix-tangled-cli { }; devenvSummary = pkgs.callPackage ./devenv.custom-code.nix { }; in { @@ -16,6 +16,7 @@ in pkgs.docker-buildx # BuildKit builder for CI image builds pkgs.docker-compose # used by application dagger # dagger CLI dev build (until 1.0) + pkgs.rtk # used by coding agent pkgs.skills # used to manage .agents/skills tang # tangled.org cli ]; @@ -27,6 +28,7 @@ in { pkg = pkgs.docker-buildx; } { pkg = pkgs.docker-compose; } { pkg = dagger; } + { pkg = pkgs.rtk; } { pkg = pkgs.skills; } { pkg = tang; } ]; @@ -37,5 +39,7 @@ in # https://app.cachix.org/cache/rssbase#pull cachix.pull = [ "rssbase" ]; + env.RTK_TELEMETRY_DISABLED = 1; + # See full reference at https://devenv.sh/reference/options/ } diff --git a/dagger/README.md b/nix-dagger/README.md similarity index 100% rename from dagger/README.md rename to nix-dagger/README.md diff --git a/dagger/default.nix b/nix-dagger/default.nix similarity index 100% rename from dagger/default.nix rename to nix-dagger/default.nix diff --git a/tangled-cli/README.md b/nix-tangled-cli/README.md similarity index 100% rename from tangled-cli/README.md rename to nix-tangled-cli/README.md diff --git a/tangled-cli/default.nix b/nix-tangled-cli/default.nix similarity index 100% rename from tangled-cli/default.nix rename to nix-tangled-cli/default.nix