{"id":"17f32b8e-fc43-4ece-aa8e-c523f771e9f9","slug":"but-axi","kind":"skill","name":"but-axi","summary":"AXI wrapper for the GitButler's but CLI","owner":"edheltzel","repoUrl":"https://github.com/edheltzel/but-axi","license":"MIT","licenseTier":"permissive","trust":"community","stars":2,"match":null,"fewStars":true,"format":"skill","createdAt":"2026-10-05T23:53:48.000Z","addedAt":"2026-10-22T00:00:10.701Z","logoUrl":"https://avatars.githubusercontent.com/u/402910?u=2aa52205d871aae58db500f488d6ab02de73d0ac&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["developer-tools"],"tags":["axi","git","gitbutler","gitworkflow"],"runtime":"node","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-26T18:58:30.507Z","sample":false,"url":"https://conduits.dev/tools/but-axi","language":"TypeScript","transports":["stdio"],"install":{"npm":"but-axi"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"```text\n██████╗ ██╗   ██╗████████╗     █████╗ ██╗  ██╗██╗\n██╔══██╗██║   ██║╚══██╔══╝    ██╔══██╗╚██╗██╔╝██║\n██████╔╝██║   ██║   ██║       ███████║ ╚███╔╝ ██║\n██╔══██╗██║   ██║   ██║       ██╔══██║ ██╔██╗ ██║\n██████╔╝╚██████╔╝   ██║       ██║  ██║██╔╝ ██╗██║\n╚═════╝  ╚═════╝    ╚═╝       ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝\n```\n\n<h1 align=\"center\">but-axi</h1>\n\nGitButler CLI for agents, designed with [AXI](https://axi.md) (Agent eXperience Interface).\n\n`but-axi` wraps the official GitButler [`but`](https://docs.gitbutler.com/cli-overview) CLI. It runs `but ... --json` underneath and returns token-efficient [TOON](https://github.com/toon-format/toon) output with counts up front, explicit empty states, truncated diffs, structured errors, and `help[]` next-step hints.\nMutations (commit, branch new, push, undo, redo, oplog snapshot, oplog restore) return their result and a compact workspace summary in one call: workspace path, counts, upstream, and applied branches. That summary is not the full `status` payload (no file list, no commit list).\nIt is built for agents that drive version control through a shell.\n\n## AXI principles\n\n`but-axi` follows the [10 AXI principles](https://axi.md):\n\n| # | Principle | How but-axi does it |\n| - | --------- | ------------------- |\n| 1 | Token-efficient output | TOON tables such as `branches[2]{name,commits,state}:` instead of raw JSON |\n| 2 | Minimal default schemas | 3-4 fields per row. `--fields a,b` adds more |\n| 3 | Content truncation | Patches over 3000 chars and commit bodies over 800 chars are cut, with a size hint and `--full` |\n| 4 | Pre-computed aggregates | A `count:` line first: stacks, branches, commits, uncommitted, conflicts |\n| 5 | Definitive empty states | `branches: 0 applied branches`, `hunks: 0 matching hunks`, `pushed 0 branches (already up to date)` |\n| 6 | Structured errors and exit codes | `error:`, `code:`, `help[]` on stdout. Exit 0 ok, 1 runtime error, 2 usage error. Never prompts. Idempotent commit, branch new, push |\n| 7 | Ambient context | `but-axi setup hooks` installs a SessionStart dashboard for Claude Code, Codex, and Cursor |\n| 8 | Content first | Bare `but-axi` shows live workspace state, not help text |\n| 9 | Contextual disclosure | Every output ends with `help[]` command templates. `-C <path>` is carried forward and runtime values stay placeholders like `<branch>` |\n| 10 | Consistent help | `but-axi <command> --help` is short |\n\n## Quick Start\n\nInstall the but-axi skill in the [Agent Skills](https://agentskills.io) format with [`npx skills`](https://github.com/vercel-labs/skills):\n\n```sh\nnpx skills add edheltzel/but-axi --skill but-axi -g\n```\n\nThat is the entire setup - no npm install needed.\nThe skill is a minimal discovery stub that directs your agent to the always-current `npx -y but-axi` dashboard and help output instead of duplicating command guidance.\nYou still need [GitButler](https://gitbutler.com/downloads) with its [`but`](https://docs.gitbutler.com/cli-overview) CLI, Git, and Node.js 22+.\n\nThe skill is not a user-facing slash command (`user-invocable: false`).\nJust ask for anything that touches GitButler version control - status, diffs, commits, branches, push, undo/redo - and the agent loads the skill on its own when it recognizes the task.\n\n`-g` installs the skill for all projects (`~/.agents/skills/` / `~/.claude/skills/`, for example); drop it to install for the current project only.\n\n## Other Ways to Install\n\nThe skill is the recommended path, but it is not the only one.\n\n### Zero setup\n\nbut-axi is an AXI, so any capable agent can run the CLI directly with nothing installed at all.\nJust tell your agent:\n\n```\nExecute `npx -y but-axi` to get GitButler tools.\n```\n\n### Session hook\n\nWant ambient GitButler context - the current workspace dashboard - fed into every agent session instead of loading on demand?\nInstall the CLI globally and opt into the hook:\n\n```sh\nnpm install -g but-axi\nbut-axi setup hooks\n```\n\nThis installs a `SessionStart` hook for **Claude Code**, **Codex**, **Cursor**, and **omp** that surfaces the current GitButler workspace state at the start of each session.\n**Restart your agent session after running this** so the new hook takes effect.\n\n### Clone (development)\n\n```sh\ngit clone https://github.com/edheltzel/but-axi.git\ncd but-axi\nnpm install\nnpm link\nbut-axi\nbut-axi setup hooks\n```\n\n### Session hook\n\n```sh\nbut-axi setup hooks                      # install for every agent found (claude, codex, cursor, omp)\nbut-axi setup hooks --agents claude      # only some agents\nbut-axi setup hooks --dry-run            # show what would change\nbut-axi setup hooks --status             # installed or not, per agent\nbut-axi setup hooks --uninstall          # remove the managed entries\n```\n\n| Agent | Config file | Hook |\n| ----- | ----------- | ---- |\n| Claude Code | `~/.claude/settings.json` | `hooks.SessionStart` group, plain text output |\n| Codex | `~/.codex/hooks.json` | `hooks.SessionStart` group, plain text output (needs `[features] hooks = true` in `~/.codex/config.toml`) |\n| Cursor | `~/.cursor/hooks.json` | `hooks.sessionStart` entry, output `{\"additional_context\": \"...\"}` |\n| omp | `~/.omp/agent/extensions/but-axi.ts` | Auto-discovered TypeScript extension (no config edit), see below |\n\nSetup merges into the existing file and never replaces it. Each file it edits is first copied to `<file>.bak-but-axi-<timestamp>`.\nRunning setup again changes nothing (`already installed (no change)`), and a stale entry is repaired in place.\nInstall and uninstall find the entry by the marker `# but-axi hook session-start` in the command.\nThe hook command uses absolute paths to `node` and the script, so it works when an app starts with a short `PATH`.\nThe hook reads the session directory from the hook's stdin JSON (`cwd`, or Cursor's `workspace_roots`) and prints nothing outside a GitButler workspace or on any error.\n\nomp has no shell hook config, so but-axi writes a small extension instead, rendered from [`omp/but-axi.ts`](omp/but-axi.ts) with the absolute `node` and script paths baked in.\nOn `session_start` it runs `but-axi hook session-start --agent omp` in the session directory (5 second timeout, then SIGTERM and SIGKILL; output capped at 64 KB).\nBefore the first agent turn it appends the dashboard to the system prompt. Outside a GitButler workspace, on an error, or on a timeout it adds nothing.\nThe file carries the marker `but-axi-omp-extension`. Setup only overwrites or removes a `but-axi.ts` that has the marker, and refuses to touch one that does not.\nBefore it updates or removes the file, setup copies it to `~/.omp/agent/but-axi.ts.bak-but-axi-<timestamp>`. The copy goes outside `extensions/` so omp does not load it.\n\nNot covered: jcode (its `session_start` setting is a single command that is already in use).\nCursor note: Cursor has a [known issue](https://forum.cursor.com/t/sessionstart-hook-additional-context-is-never-injected-into-agents-initial-system-context/158452) where `sessionStart` `additional_context` can be dropped.\n\n## Usage\n\n```sh\nbut-axi                                  # home view: live workspace state + next steps\nbut-axi status                           # uncommitted files, branches, commits, with IDs\nbut-axi status --fields sha,author       # extra columns\nbut-axi diff                             # all uncommitted hunks + truncated patch\nbut-axi diff <id> --full                 # one file/commit/branch, complete patch\nbut-axi diff --query login               # only hunks and patch lines that match\nbut-axi show <commit-id|branch>          # commit details or branch commit list\nbut-axi commit -b <branch> -m \"<msg>\"    # commit everything, return compact summary\nbut-axi commit -m \"<msg>\" <id> <id>      # commit selected hunks/files\nbut-axi branch                           # applied + unapplied branches\nbut-axi branch list --query <text>       # filter that list; --above is only for branch new\nbut-axi branch new <name>                # idempotent create\nbut-axi push [<branch>] [--dry-run]      # push, return pushed refs + summary\nbut-axi undo | but-axi redo              # oplog undo/redo, return compact summary\nbut-axi oplog [--limit 20]               # operation history\nbut-axi oplog snapshot -m \"<msg>\"        # snapshot id + compact summary\nbut-axi oplog restore <id>               # restore, return compact summary\nbut-axi -C ~/code/app status             # any command against another directory\n```\n\nHome view in a fresh workspace with two uncommitted files:\n\n```text\n$ but-axi -C /tmp/but-axi-demo/app\nbin: /opt/homebrew/bin/but-axi\ndescription: Agent-ergonomic GitButler CLI that wraps `but --json` with compact TOON views and combined actions\nworkspace: /private/tmp/but-axi-demo/app\ncount: 0 stacks; 0 branches; 0 commits; 2 uncommitted; 0 conflicts\nupstream: up to date\nbranches: 0 applied branches\nhelp[4]:\n  Run `but-axi -C /tmp/but-axi-demo/app diff` to inspect 2 uncommitted\n  Run `but-axi -C /tmp/but-axi-demo/app commit -b <branch> -m \"<message>\"`\n  Run `but-axi -C /tmp/but-axi-demo/app status` for IDs, files and commits\n  Run `but-axi -C /tmp/but-axi-demo/app --help` for all commands\n```\n\nA commit returns the new commit and a compact workspace summary in one call:\n\n```text\n$ but-axi -C /tmp/but-axi-demo/app commit -b feature/login -m \"feat: add login helper\"\ncommitted:\n  id: rqk\n  sha: fb48aec\n  branch: feature/login\n  subject: \"feat: add login helper\"\nworkspace: /private/tmp/but-axi-demo/app\ncount: 1 stack; 1 branch; 1 commit (1 on unpushed branches); 0 uncommitted; 0 conflicts\nupstream: up to date\nbranches[1]{name,commits,state}:\n  feature/login,1,unpushed\nhelp[3]:\n  Run `but-axi -C /tmp/but-axi-demo/app push feature/login`\n  Run `but-axi -C /tmp/but-axi-demo/app show rqk`\n  Run `but-axi -C /tmp/but-axi-demo/app undo` to revert this commit\n```\n\nPush works the same way:\n\n```text\n$ but-axi -C /tmp/but-axi-demo/app push feature/login\nresult: pushed 1 branch\npushed[1]{branch,remote,from,to}:\n  feature/login,origin,(new),fb48aec\nworkspace: /private/tmp/but-axi-demo/app\ncount: 1 stack; 1 branch; 1 commit (0 on unpushed branches); 0 uncommitted; 0 conflicts\nupstream: up to date\nbranches[1]{name,commits,state}:\n  feature/login,1,pushed\nhelp[2]:\n  Run `but pr new <branch> -m \"<title>\"` to open a PR (raw but)\n  Run `but-axi -C /tmp/but-axi-demo/app status`\n```\n\nLong patches are cut with a size hint:\n\n```text\npatch_note: \"(truncated, 24979 chars total — use --full to see complete diff)\"\n```\n\n### Commands\n\n| Command | Description |\n| ------- | ----------- |\n| *(none)* | Home view: `bin`, `description`, workspace, counts, branches, `help[]` |\n| `status` | `uncommitted{id,path,change}`, `branches{id,name,commits,state}`, `commits{id,branch,subject}` |\n| `diff [<id>]` | `hunks{id,path,status,lines}` plus a truncated `patch` |\n| `show <id\\|branch>` | Commit: id, sha, author, date, subject, body, files. Branch: commits, base, stackedOn |\n| `commit` | `-m` (repeatable), `-b <branch>`, optional change IDs. Never opens an editor. With nothing to commit it prints `skipped` and exits 0 |\n| `branch [list\\|new\\|show]` | List, create (an existing branch is reported as `already exists (no change)`), show |\n| `push [<branch>]` | `--dry-run`, `--with-force`, `--no-verify`. Already pushed gives `pushed 0 branches` and exit 0 |\n| `undo`, `redo` | Oplog undo or redo plus the compact workspace summary |\n| `oplog [list\\|snapshot\\|restore]` | list: `--limit <n>`. snapshot: `-m <msg>`, returns the snapshot id and compact summary. restore: `<id>` and that summary. A flag from the wrong subcommand is a usage error |\n| `setup hooks` | `--status`, `--uninstall`, `--agents`, `--dry-run` |\n| `hook session-start` | Hook entrypoint (`--agent claude\\|codex\\|cursor\\|omp`). Any other agent name is a usage error |\n| `version` | Print the version. Extra arguments are a usage error |\n\n### Global flags\n\n- `-C <path>` runs against another directory. It is carried forward into `help[]`.\n- `--fields a,b` adds columns from a list's extra fields. `<command> --help` lists them. An unknown field exits 2.\n- `--full` turns off truncation and row caps.\n- `--query <text>` (`-q`) keeps only matching rows and patch lines, and reports `query:` and `matched: N of M`.\n- `--help` (`-h`) prints short help for any command.\n- `--version` (`-v`, `-V`) prints the version. A bare version flag does not load command handlers.\n\n### Exit codes and errors\n\n| Exit | Meaning |\n| ---- | ------- |\n| 0 | Success, including no-op results such as `skipped` or `already exists` |\n| 1 | Runtime error from `but` or but-axi, such as not a workspace, unknown ID, or a failed push |\n| 2 | Usage error: unknown command, flag, or `--fields` value; missing or invalid argument (including missing `-m` and a bad `--limit`); extra argument |\n\nErrors go to stdout in the same TOON shape. Debug output goes to stderr, and only when `BUT_AXI_DEBUG=1` is set.\n\n```text\n$ but-axi status --verbose\nerror: unknown flag --verbose\ncode: UNKNOWN_FLAG\nhelp[1]:\n  Run `but-axi status --help` to see supported flags\n```\n\n### help[] hints\n\nEach response ends with `help[N]:` lines, which are ready-to-run command templates.\nFixed context (`-C <path>`) is carried forward. Values the agent has to choose stay as placeholders (`<branch>`, `<id>`, `\"<message>\"`). Values known from the result, such as the branch a commit landed on, are filled in.\n\n### Environment\n\n- `BUT_AXI_BUT=/path/to/but` sets which `but` binary to use. By default but-axi looks on `PATH`, then in `/opt/homebrew/bin`, `/usr/local/bin`, and `~/.local/bin`.\n- `BUT_AXI_NODE=/path/to/node` sets the node binary written into hook commands. The default is `/opt/homebrew/bin/node` when it exists.\n- `BUT_AXI_DEBUG=1` writes the `but` invocations to stderr.\n\n## Fallback to raw `but`\n\nbut-axi wraps the core loop: inspect, commit, branch, push, and undo.\nFor everything else, use `but` directly: `amend`, `absorb`, `squash`, `move`, `reword`, `uncommit`, `discard`, `resolve`, `apply`, `unapply`, `pull`, `pr`, `land`, `pick`, `worktree`, `setup`, `teardown`.\nIf you call one of these on but-axi, it exits 2 and tells you to use `but <command> --help`.\nCLI IDs from `but-axi status` and `but-axi diff` are the same IDs `but` uses, so you can pass them straight to `but`.\n\n## Agent skill\n\nThe `gitbutler-axi` Agent Skill (`~/.agents/skills/gitbutler-axi/SKILL.md` on the author's machine) teaches agents when to use but-axi, how to read its output, and when to fall back to raw `but`.\nThe hook gives an agent context at the start of every session. The skill gives it the full guide when it needs one.\n\n## Development\n\n```sh\nnpm install          # dependencies + build (prepare)\nnpm run build        # tsc -> dist/\nnpm test             # build + vitest (unit tests + CLI exit-code tests + a real `but` flow in /tmp)\n```\n\nThe integration suite creates a throwaway repo and a bare remote under `/tmp`, runs `but setup`, then commits, pushes, and undoes there. It is skipped when `but` is not installed.\n\n## License\n\nMIT\n"}