影分身の術 — Shadow Clone Jutsu for your git repo. · 中文
Run several AI coding-agent sessions on one repo at the same time. Point two agents at the same checkout and they fight over one working tree — same files, same branches, each other's uncommitted changes. kage gives each session its own full copy of the repo in a sibling folder, so they can't collide.
npm install -g pi-kage
eval "$(kage shell-init)" # so kage can cd your shell in & out (recommended)
cd my-app
kage --agent pi # 🥷 copy → ../my-app--<name>, cd in, open a fresh pi
# ...edit · commit · push · open a PR · quit pi...
kage finish # 💨 merge pi's session memory back, delete the clone, cd you homeLaunching an agent is optional: bare kage just makes the clone and cd's you in — --agent (or
kage config agent, or $KAGE_AGENT) is what tells kage to start one for you.
Code comes back through git (a PR, or a branch fetch). The agent's session memory comes back through
its own session store (~/.pi, ~/.claude, or ~/.codex). kage never copies a working tree back onto
the origin — that's the whole point.
A git worktree gives you a second working directory, but
every worktree shares one .git — so two agents can't check out the same branch, and stash/refs/index
are shared. A worktree is also a clean checkout: no node_modules, .env, build cache, so each one
needs a setup pass first.
A full copy has independent .git, independent branches, and every gitignored/untracked file already
in place. The cost is disk and copy time — which on APFS (macOS) and reflink filesystems (Linux) kage
sidesteps with a copy-on-write clone: near-instant, no extra space until files actually change. Other
filesystems fall back to a plain recursive copy.
npm install -g pi-kage # or: pnpm add -g pi-kage
npx pi-kage # run without installing
# install script (single zero-dependency Node script → ~/.local/bin)
curl -fsSL https://raw.githubusercontent.com/kid7st/kage/main/install.sh | shRequires git and Node ≥ 18 on your PATH, plus at least one coding-agent CLI to drive —
pi (the default), Claude Code,
or Codex. kage itself has no runtime dependencies.
From source:
git clone https://github.com/kid7st/kage
cd kage && npm install && npm link # npm install builds bin/kage.mjs from src/| Command | Run from | What it does |
|---|---|---|
kage [path] [--name x] [--agent <id>] |
origin repo | Copy the repo to ../<repo>--<name> (default kage-<ts>), import the origin's recent sessions (resumable, never replayed), cd you into the clone, and launch an agent only if you named one (--agent/$KAGE_AGENT/kage config agent) — else just drop you in. --name only names the folder; kage never creates a branch. No args + existing clones → interactive menu. |
kage status [--pr] |
origin repo | Dashboard: branch, dirty/clean, ahead/behind, "safe to clean". --pr adds PR state via gh. |
kage finish [name] [--force] [--push] [--pr] |
origin / inside clone | Preserve the clone's commits (push, or with no remote a local kage/<name> branch), merge its new sessions back, delete it, cd you home. Refuses uncommitted changes — and, with a remote, unpushed commits — unless --force. --push/--pr push first (and open a PR via gh). |
kage rm [name] [--force] |
origin / inside clone | Discard a clone without merging memory. Refuses local-only work unless --force. |
kage pull <path...> |
inside a clone | Copy specific files/dirs (even gitignored, e.g. a generated .env) back to the origin. |
kage config [<key> [value]] [--unset] |
anywhere | Get/set persisted defaults. One key today: agent (your default launch agent), validated against known agents. |
kage shell-init |
shell rc | Shell wrapper (cd into a clone on create, back to origin on finish/rm) + tab completion. Use eval "$(kage shell-init)". |
kage --help / --version |
anywhere | Usage / version. |
Run bare kage inside a repo that already has clones to get an interactive picker: create a new clone,
or enter / finish / remove an existing one. finish and rm show the same picker when you
have several clones and don't name one.
kage is agent-agnostic — and launching an agent is opt-in. By default kage just creates the clone and
cd's your shell into it; you work however you like. To have kage launch one for you, name it with --agent,
or set a default with kage config agent (or $KAGE_AGENT) — precedence --agent > $KAGE_AGENT >
kage config agent; with none set, nothing is launched. Whichever way you work, every agent gets the same
isolated clone and git flow-back — what differs is memory flow-back, i.e. whether the sessions you
create in the clone come home to the origin:
| Agent | Launch it with | Memory flow-back |
|---|---|---|
| pi | kage --agent pi |
✅ full round-trip — recent sessions copied in on create, new ones merged back on finish |
| Claude Code | kage --agent claude |
✅ full round-trip (same as pi) |
| Codex | kage --agent codex |
codex resume --all |
Why Codex is git-only. pi and Claude Code key their session stores by working directory, so kage can
copy a clone's sessions over to the origin's directory and back. Codex keeps one global, sqlite-indexed
store that kage can't cleanly re-home — so --agent codex gives you the isolated clone + git flow-back,
and your Codex history stays globally reachable. Details in
docs/multi-agent-design.md.
You can run several agents at once — a clone each, or different agents in one clone. Each agent uses its
own store, so they never collide, and finish merges back whichever stores gained new sessions.
Driving a GUI/IDE agent kage can't spawn? Use --open <cmd> to create the clone and open it yourself
(e.g. kage --open code runs code <clone>), or --no-launch to just make the clone and print its path.
Memory still flows for any agent whose store kage manages, no matter how you opened the clone.
eval "$(kage shell-init)" # add to ~/.zshrc or ~/.bashrckage cd's your shell into the new clone, and finish/rm cd it back to the origin afterward
(a CLI can't move its parent shell otherwise, so this needs the wrapper — without it, kage just prints the
cd for you to run). It also adds tab completion for subcommands and clone names.
- Isolation. A clone is a full independent copy with its own
.git. kage does not create a branch — the clone stays on the origin's current branch and stays out of git flow, so you own the branching/PR workflow inside the clone (tell the agent via yourAGENTS.md). - Code flows back via git, never the working tree. With a remote: push the branch, merge the PR.
Without a remote:
finishfetches the clone's branch into the origin's git as a localkage/<name>-<sha>branch (origin working tree untouched —git mergeit when you like). Because a fetch can't preserve uncommitted work,finishrefuses to delete a dirty clone unless--force. - Memory flows via the agent's own session store, never replayed. On create, the origin's 5 most
recent sessions for that agent are copied in — the agent's resume picker surfaces them when you launch it, but you
start a fresh session. On
finish, sessions the clone created come back whole; a copied-in session you resumed comes back as a separate new session, so the origin's original is never mutated. (Codex is the exception — git-only flow-back; see Agents.) - The origin is read-only to kage. It only copies out and writes session memory — it never touches the origin's working tree, even while another session is live there.
- The copy snapshots the origin's current state, including uncommitted changes.
- Submodules: a submodule's
.gitis an absolute path and breaks on copy — rungit submodule update --initin the clone. - kage reads each agent's sessions from where that agent itself stores them, honoring the agent's own
config var —
PI_CODING_AGENT_DIR(pi),CLAUDE_CONFIG_DIR(Claude Code),CODEX_HOME(Codex) — so kage and the agent always agree on the location.
The CLI is TypeScript compiled to a single zero-dependency file:
src/kage.mts→bin/kage.mjs(the only thing shipped)test/kage.test.mts→dist/— black-boxnode:testsmoke tests that spawn the built CLI
bin/ and dist/ are gitignored build artifacts; bin/ is produced on npm install (via prepare),
so npm link from a clone just works. Linting/formatting is Biome (dev-only).
npm run build # tsc: src/kage.mts → bin/kage.mjs
npm run lint # biome + tsc type check
npm test # build + node:test smoke tests (temp repos, no network)Releases: bump version in package.json, then git tag vX.Y.Z && git push origin main vX.Y.Z.
CI runs lint + tests and npm publish --provenance on any v* tag.