pi-kage 0.3.6 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,111 +4,73 @@
4
4
  [![npm](https://img.shields.io/npm/v/pi-kage)](https://www.npmjs.com/package/pi-kage)
5
5
  [![license](https://img.shields.io/npm/l/pi-kage)](./LICENSE)
6
6
 
7
- > **影分身の術**cast the **Shadow Clone Jutsu** on your git repo.
7
+ > 影分身の術 — Shadow Clone Jutsu for your git repo. · [中文](./README.zh-CN.md)
8
8
 
9
9
  <p align="center"><img src="./assets/demo.svg" alt="kage demo" width="100%"></p>
10
10
 
11
- `kage` copies your repo into an isolated sibling folder, drops you straight into a **fresh**
12
- [pi](https://github.com/earendil-works) session to work in parallel, and when you're done merges the
13
- clone's new sessions back into the original and dispels the clone.
11
+ Run several AI coding-agent sessions on one repo at the same time. Point two agents at the same
12
+ checkout and they fight over one working tree — same files, same branches, each other's uncommitted
13
+ changes. kage gives each session its **own full copy** of the repo in a sibling folder, so they
14
+ can't collide.
14
15
 
15
16
  ```bash
16
17
  npm install -g pi-kage
17
18
  cd my-app
18
- kage # 🥷 clone → ../my-app--kage-<ts>, open a fresh pi (origin history resumable)
19
- # ...work in the clone: commit, push, open a PR, quit pi...
20
- kage finish # 💨 merge the clone's new sessions back, delete the clone
19
+ kage # 🥷 copy → ../my-app--kage-<ts>, open a fresh pi
20
+ # ...commit, push, open a PR, quit pi...
21
+ kage finish # 💨 merge the clone's sessions back, delete the clone
21
22
  ```
22
23
 
23
- ---
24
+ Code comes back through git (a PR, or a branch fetch). The agent's session memory comes back through
25
+ `~/.pi`. kage never copies a working tree back onto the origin — that's the whole point.
24
26
 
25
- ## The problem
27
+ ## Why a full copy, not `git worktree`?
26
28
 
27
- Running **multiple agent sessions on the same repo at once** is a mess: they edit the same files,
28
- fight over the working tree, and collide on branches. You end up babysitting merge conflicts
29
- instead of shipping.
29
+ A [`git worktree`](https://git-scm.com/docs/git-worktree) gives you a second working directory, but
30
+ every worktree shares one `.git` so two agents can't check out the same branch, and stash/refs/index
31
+ are shared. A worktree is also a clean checkout: no `node_modules`, `.env`, build cache, so each one
32
+ needs a setup pass first.
30
33
 
31
- ## The idea
32
-
33
- A shadow clone is a **full, independent copy** of the repo like a second engineer on a second
34
- machine. Each parallel session gets its own working tree, branch, commits, and PR. Code merges the
35
- normal way: on GitHub. No local collisions, ever. And like a real Naruto shadow clone, it carries
36
- your memory out and returns it on dispel (see [How it works](#how-it-works)).
37
-
38
- Why a full folder copy instead of `git worktree`? A worktree shares one `.git`, which means you
39
- can't check out the same branch twice, you share stash/refs, and you get a *fresh* checkout with no
40
- `node_modules` / `.env` / build cache. A real copy avoids all of that. On macOS APFS the copy is a
41
- `cp -c` clonefile (copy-on-write): near-instant and space-free until files diverge.
34
+ A full copy has independent `.git`, independent branches, and every gitignored/untracked file already
35
+ in place. The cost is disk and copy time — which on APFS (macOS) and reflink filesystems (Linux) kage
36
+ sidesteps with a copy-on-write clone: near-instant, no extra space until files actually change. Other
37
+ filesystems fall back to a plain recursive copy.
42
38
 
43
39
  ## Install
44
40
 
45
41
  ```bash
46
- # npm
47
- npm install -g pi-kage # then use `kage` anywhere
48
- npx pi-kage # or run without installing
42
+ npm install -g pi-kage # or: pnpm add -g pi-kage
43
+ npx pi-kage # run without installing
49
44
 
50
- # pnpm
51
- pnpm add -g pi-kage
52
- pnpm dlx pi-kage # or run without installing
53
-
54
- # or install script (no npm needed — kage is a single, zero-dependency Node script)
45
+ # install script (single zero-dependency Node script → ~/.local/bin)
55
46
  curl -fsSL https://raw.githubusercontent.com/kid7st/kage/main/install.sh | sh
56
47
  ```
57
48
 
58
- The install script drops the single `kage` file into `~/.local/bin` (override with `KAGE_BIN_DIR`,
59
- pin a version with `KAGE_VERSION`). kage has **no dependencies** — it only needs Node, git, and pi.
49
+ Requires **git**, [**pi**](https://github.com/earendil-works), and **Node 18** on your `PATH`.
50
+ kage has no runtime dependencies.
60
51
 
61
52
  From source:
62
53
 
63
54
  ```bash
64
55
  git clone https://github.com/kid7st/kage
65
- cd kage && npm install && npm link # `npm install` builds bin/kage.mjs from src/ (TypeScript)
56
+ cd kage && npm install && npm link # npm install builds bin/kage.mjs from src/
66
57
  ```
67
58
 
68
- Requires **git**, [**pi**](https://github.com/earendil-works), and **Node ≥ 18** on your `PATH`.
69
-
70
- ## Lifecycle
59
+ ## Commands
71
60
 
72
- ```
73
- origin repo (you) shadow clone (independent copy)
74
- ───────────────── ──────────────────────────────
75
- $ kage --name fix-login ─copy + history─► ../my-app--fix-login
76
- $ pi (fresh session; origin history resumable)
77
- · git switch -c fix-login
78
- · edit / commit / push / open PR
79
- · quit pi
80
- $ kage finish fix-login ◄─new sessions── (the clone's .jsonl, copied back)
81
- · safety check (committed? pushed?)
82
- · merge the clone's new sessions into ~/.pi
83
- · delete the clone folder
84
- code arrives via the merged GitHub PR ✓
85
- ```
86
-
87
- ## Usage
88
-
89
- ```bash
90
- cd ~/code/my-app
91
-
92
- kage # clone . → ../my-app--kage-<ts>, open a fresh pi (origin history resumable)
93
- kage --name fix-login # name the clone folder: ../my-app--fix-login
94
- kage /path/to/other-repo # clone a different repo (path defaults to cwd)
95
-
96
- # back in the origin after you quit the clone's pi:
97
- kage # no args inside a repo with clones -> interactive menu
98
- kage status # status dashboard: branch · dirty · ahead/behind · safe-to-clean
99
- kage status --pr # also show PR state (via gh)
100
- kage finish fix-login # check → merge the clone's new sessions back → delete the clone
101
- kage finish fix-login --pr # push the branch + open a PR (via gh), then finish
102
- kage finish --force # skip the uncommitted/unpushed guard
103
- kage rm old-experiment # discard a clone without merging (refuses if it has local-only work)
104
-
105
- # inside a clone, to retrieve a non-git file (e.g. a generated .env):
106
- kage pull .env config/local.json
107
- ```
61
+ | Command | Run from | What it does |
62
+ |---|---|---|
63
+ | `kage [path] [--name x]` | origin repo | Copy the repo to `../<repo>--<name>` (default `kage-<ts>`), copy in the origin's 5 most recent pi sessions (resumable, never replayed), and launch a **fresh** pi. `--name` only names the folder — kage never creates a branch. No args + existing clones → interactive menu. |
64
+ | `kage status [--pr]` | origin repo | Dashboard: branch, dirty/clean, ahead/behind, "safe to clean". `--pr` adds PR state via `gh`. (`kage list` is an alias.) |
65
+ | `kage finish [name] [--force] [--push] [--pr]` | origin / inside clone | Refuse if the clone has uncommitted or unpushed work, merge its **new** sessions back, delete it. `--push` pushes the branch first; `--pr` pushes + opens a PR via `gh`; `--force` skips the guard. |
66
+ | `kage rm [name] [--force]` | origin / inside clone | Discard a clone **without** merging memory. Refuses local-only work unless `--force`. |
67
+ | `kage pull <path...>` | inside a clone | Copy specific files/dirs (even gitignored, e.g. a generated `.env`) back to the origin. |
68
+ | `kage shell-init` | shell rc | Shell wrapper (cd back to origin after `finish`/`rm`) + tab completion. Use `eval "$(kage shell-init)"`. |
69
+ | `kage --help` / `--version` | anywhere | Usage / version. |
108
70
 
109
- With no arguments inside a repo that already has clones, `kage` shows an interactive picker: create a
110
- new clone, or select an existing one to **enter** (`pi -c`), **finish**, or **remove**. `finish` and `rm`
111
- show the same picker when you have multiple clones and don't name one.
71
+ Run bare `kage` inside a repo that already has clones to get an interactive picker: create a new clone,
72
+ or **enter** / **finish** / **remove** an existing one. `finish` and `rm` show the same picker when you
73
+ have several clones and don't name one.
112
74
 
113
75
  ### Shell integration (optional)
114
76
 
@@ -116,84 +78,50 @@ show the same picker when you have multiple clones and don't name one.
116
78
  eval "$(kage shell-init)" # add to ~/.zshrc or ~/.bashrc
117
79
  ```
118
80
 
119
- This wraps `kage` so that `finish`/`rm` run from inside a clone **cd you back to the origin**
120
- automatically (a CLI can't change its parent shell's directory otherwise), and adds tab completion
121
- for subcommands and clone names.
122
-
123
- ### Commands
124
-
125
- | Command | Run from | What it does |
126
- |---|---|---|
127
- | `kage [path] [--name x]` | origin repo | Copy the repo to `../<repo>--<name>` (default `kage-<ts>`), copy the origin's 5 most recent pi sessions into the clone (resumable there, never replayed), and launch a **fresh** `pi` session. `--name` only names the folder — kage never creates a branch. With no args (and existing clones) it opens an interactive picker. |
128
- | `kage status [--pr]` | origin repo | Status dashboard of clones: branch, dirty/clean, ahead/behind upstream, and a “safe to clean” flag. `--pr` adds PR state via `gh`. (`kage list` is a kept alias.) |
129
- | `kage finish [name] [--force] [--push] [--pr]` | origin, inside the clone, or anywhere with a clone path | Refuse if the clone has uncommitted or unpushed work (`--force` overrides), merge the clone's **new** sessions back (copied-in origin history is skipped), then delete the clone. `--push` pushes the branch first; `--pr` pushes and opens a PR via `gh`. Auto-selects / prompts when there are several. |
130
- | `kage rm [name] [--force]` | origin, inside the clone, or anywhere with a clone path | Discard a clone **without** merging memory. Refuses if it has local-only work unless `--force`. For abandoned experiments. `name` may be a clone name (run from the repo) or a path to the clone folder (works from anywhere). |
131
- | `kage pull <path...>` | inside a clone | Copy specific files/dirs (even gitignored ones) back to the origin at the same relative path. |
132
- | `kage shell-init` | shell rc | Print a shell wrapper (cd-back after `finish`/`rm`) + tab completion. Use `eval "$(kage shell-init)"`. |
133
- | `kage --help` / `--version` | anywhere | Usage / version. |
81
+ Running `finish`/`rm` from inside a clone deletes the directory your shell is sitting in. The wrapper
82
+ cd's you back to the origin automatically (a CLI can't change its parent shell otherwise) and adds tab
83
+ completion for subcommands and clone names.
134
84
 
135
85
  ## How it works
136
86
 
137
- Four invariants keep parallel work safe and lossless:
138
-
139
- 1. **Isolation** a clone is a full independent copy with its own `.git`. kage **doesn't create a
140
- branch**: the clone stays on the origin's current branch and kage stays out of git flow entirely,
141
- so you decide your own branching/PR workflow inside the clone (instruct the agent via your
142
- `AGENTS.md`).
143
- 2. **Code flows back via git, never the working tree.** With a remote you push the branch and merge a
144
- PR. With **no remote**, `finish` fetches the clone's branch into the origin's git as a local
145
- `kage/<name>-<sha>` branch (origin's working tree untouched `git merge` it when you like; the short
146
- sha keeps the ref unique so reusing a name never collides). kage never copies the clone's working
147
- tree onto the origin that would re-create the collisions it avoids — so `finish` refuses to delete
148
- **uncommitted** work, which a fetch can't preserve.
149
- 3. **Memory flows through `~/.pi`, never replayed.** On create, the origin's 5 most recent session
150
- `.jsonl` files are copied into the clone — `pi`'s resume picker surfaces them, but the clone opens a
151
- **fresh** session (kage never replays turns or fakes a resumed conversation). On `finish`, sessions
152
- the clone created are copied back whole; an unchanged copied-in session adds nothing; and a
153
- copied-in session you *resumed and added to* comes back as a **new, self-contained** session — so
154
- the origin's original session (the leaf pi would resume) is never mutated and your turns aren't lost.
155
- 4. **The origin is read-only to kage** — it only copies out and writes session memory; it never
156
- touches the origin's working tree, even while another session is live there.
157
-
158
- With a remote configured, `finish` nudges you to push first (so PR-flow mistakes surface) unless you
159
- pass `--push` / `--pr` / `--force`.
160
-
161
- ## Notes & caveats
162
-
163
- - The copy is a snapshot of the origin's **current** state, **including uncommitted changes**.
164
- - **Submodules**: a submodule's `.git` pointer is an absolute path and breaks on copy — run
87
+ - **Isolation.** A clone is a full independent copy with its own `.git`. kage does **not** create a
88
+ branch — the clone stays on the origin's current branch and stays out of git flow, so you own the
89
+ branching/PR workflow inside the clone (tell the agent via your `AGENTS.md`).
90
+ - **Code flows back via git, never the working tree.** With a remote: push the branch, merge the PR.
91
+ Without a remote: `finish` fetches the clone's branch into the origin's git as a local
92
+ `kage/<name>-<sha>` branch (origin working tree untouched — `git merge` it when you like). Because a
93
+ fetch can't preserve uncommitted work, `finish` refuses to delete a dirty clone unless `--force`.
94
+ - **Memory flows via `~/.pi`, never replayed.** On create, the origin's 5 most recent sessions are
95
+ copied in — pi's resume picker surfaces them, but the clone opens a **fresh** session. On `finish`,
96
+ sessions the clone created come back whole; a copied-in session you resumed comes back as a separate
97
+ new session, so the origin's original is never mutated.
98
+ - **The origin is read-only to kage.** It only copies out and writes session memory — it never touches
99
+ the origin's working tree, even while another session is live there.
100
+
101
+ ## Notes
102
+
103
+ - The copy snapshots the origin's **current** state, including uncommitted changes.
104
+ - **Submodules**: a submodule's `.git` is an absolute path and breaks on copy run
165
105
  `git submodule update --init` in the clone.
166
- - Non-APFS / non-reflink filesystems fall back to a full (heavier) copy.
167
- - Session storage is assumed at `~/.pi/agent/sessions`; override with `KAGE_SESSIONS_DIR`.
106
+ - Session storage defaults to `~/.pi/agent/sessions`; override with `KAGE_SESSIONS_DIR`.
168
107
 
169
108
  ## Development
170
109
 
171
- The CLI and its tests are written in TypeScript and compiled with `tsc`:
172
-
173
- - `src/kage.mts` → `bin/kage.mjs` — a single, zero-runtime-dependency file (the only thing the
174
- install script + npm package ship).
175
- - `test/kage.test.mts` → `dist/kage.test.mjs` — black-box `node:test` smoke tests that spawn the
176
- built CLI (compiled to `dist/` so they run on every Node in CI, no TS loader needed).
177
-
178
- `bin/` and `dist/` are build artifacts — both gitignored. `bin/` is produced on `npm install`
179
- (via `prepare`), so `npm link` from a clone just works.
110
+ The CLI is TypeScript compiled to a single zero-dependency file:
180
111
 
181
- Linting/formatting is handled by [Biome](https://biomejs.dev) — a dev-only dependency that never
182
- ships in the package, so the zero-runtime-dependency artifact is unaffected.
183
-
184
- ```bash
185
- npm run build # tsc: src/kage.mts -> bin/kage.mjs
186
- npm run format # biome: auto-format + safe lint fixes
187
- npm run lint # biome (lint + format check) + tsc type check (src + test)
188
- npm test # build CLI + tests, then run node:test smoke tests (temp repos, no network)
189
- ```
112
+ - `src/kage.mts` `bin/kage.mjs` (the only thing shipped)
113
+ - `test/kage.test.mts` `dist/` black-box `node:test` smoke tests that spawn the built CLI
190
114
 
191
- Releases publish automatically: bump `version` in `package.json`, then
115
+ `bin/` and `dist/` are gitignored build artifacts; `bin/` is produced on `npm install` (via `prepare`),
116
+ so `npm link` from a clone just works. Linting/formatting is [Biome](https://biomejs.dev) (dev-only).
192
117
 
193
118
  ```bash
194
- git tag vX.Y.Z && git push origin main vX.Y.Z
119
+ npm run build # tsc: src/kage.mts bin/kage.mjs
120
+ npm run lint # biome + tsc type check
121
+ npm test # build + node:test smoke tests (temp repos, no network)
195
122
  ```
196
123
 
124
+ Releases: bump `version` in `package.json`, then `git tag vX.Y.Z && git push origin main vX.Y.Z`.
197
125
  CI runs lint + tests and `npm publish --provenance` on any `v*` tag.
198
126
 
199
127
  ## License
@@ -0,0 +1,121 @@
1
+ # kage 🥷
2
+
3
+ [![CI](https://github.com/kid7st/kage/actions/workflows/ci.yml/badge.svg)](https://github.com/kid7st/kage/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/pi-kage)](https://www.npmjs.com/package/pi-kage)
5
+ [![license](https://img.shields.io/npm/l/pi-kage)](./LICENSE)
6
+
7
+ > 影分身の術 —— 给你的 git 仓库来一发影分身。· [English](./README.md)
8
+
9
+ <p align="center"><img src="./assets/demo.svg" alt="kage demo" width="100%"></p>
10
+
11
+ 在同一个仓库上同时跑多个 AI coding agent。让两个 agent 指向同一个 checkout,它们就会抢同一个工作区
12
+ ——改同样的文件、撞同样的分支、踩到彼此未提交的改动。kage 给每个 session 一份**独立的完整副本**,放在
13
+ 同级目录里,从根本上避免冲突。
14
+
15
+ ```bash
16
+ npm install -g pi-kage
17
+ cd my-app
18
+ kage # 🥷 复制 → ../my-app--kage-<ts>,打开一个全新的 pi
19
+ # ...提交、push、开 PR、退出 pi...
20
+ kage finish # 💨 把分身的 session 合并回来,删掉分身
21
+ ```
22
+
23
+ 代码通过 git 回流(一个 PR,或者 fetch 分支);agent 的 session 记忆通过 `~/.pi` 回流。kage 从不把工作区
24
+ 复制回原仓库 —— 这正是它存在的意义。
25
+
26
+ ## 为什么用完整副本,而不是 `git worktree`?
27
+
28
+ [`git worktree`](https://git-scm.com/docs/git-worktree) 能给你第二个工作目录,但所有 worktree 共享同一个
29
+ `.git`:两个 agent 没法 checkout 同一个分支,stash / refs / index 也都是共享的。而且 worktree 是干净的
30
+ checkout —— 没有 `node_modules`、`.env`、build 缓存,每个都得先跑一遍环境初始化。
31
+
32
+ 完整副本则有独立的 `.git`、独立的分支,所有被 gitignore 或未跟踪的文件也都已就位。代价是磁盘和复制时间
33
+ —— 而在 APFS(macOS)和支持 reflink 的文件系统(Linux)上,kage 用写时复制(copy-on-write)绕开了这个代价:
34
+ 几乎瞬间完成,文件真正发生改动前不占额外空间。其它文件系统则退化为普通的递归复制。
35
+
36
+ ## 安装
37
+
38
+ ```bash
39
+ npm install -g pi-kage # 或:pnpm add -g pi-kage
40
+ npx pi-kage # 不安装直接运行
41
+
42
+ # 安装脚本(单个零依赖的 Node 脚本 → ~/.local/bin)
43
+ curl -fsSL https://raw.githubusercontent.com/kid7st/kage/main/install.sh | sh
44
+ ```
45
+
46
+ 需要 `PATH` 上有 **git**、[**pi**](https://github.com/earendil-works) 和 **Node ≥ 18**。
47
+ kage 没有任何运行时依赖。
48
+
49
+ 从源码安装:
50
+
51
+ ```bash
52
+ git clone https://github.com/kid7st/kage
53
+ cd kage && npm install && npm link # npm install 会从 src/ 编译出 bin/kage.mjs
54
+ ```
55
+
56
+ ## 命令
57
+
58
+ | 命令 | 在哪运行 | 作用 |
59
+ |---|---|---|
60
+ | `kage [path] [--name x]` | 原仓库 | 把仓库复制到 `../<repo>--<name>`(默认 `kage-<ts>`),拷入原仓库最近 5 个 pi session(可 resume,绝不重放),并启动一个**全新**的 pi。`--name` 只命名文件夹 —— kage 从不建分支。无参数 + 已有分身 → 进入交互菜单。 |
61
+ | `kage status [--pr]` | 原仓库 | 仪表盘:分支、是否有改动、ahead/behind、是否「可安全清理」。`--pr` 通过 `gh` 附带 PR 状态。(`kage list` 是别名。) |
62
+ | `kage finish [name] [--force] [--push] [--pr]` | 原仓库 / 分身内 | 若分身有未提交或未 push 的改动则拒绝,把它**新产生**的 session 合并回来,再删除它。`--push` 先 push 分支;`--pr` push 并通过 `gh` 开 PR;`--force` 跳过检查。 |
63
+ | `kage rm [name] [--force]` | 原仓库 / 分身内 | **不**合并记忆地丢弃一个分身。若有仅存在于本地的工作则拒绝,除非加 `--force`。 |
64
+ | `kage pull <path...>` | 分身内 | 把指定文件/目录(包括被 gitignore 的,比如生成的 `.env`)按相同相对路径拷回原仓库。 |
65
+ | `kage shell-init` | shell 配置 | shell 包装函数(`finish`/`rm` 后自动 cd 回原仓库)+ tab 补全。用 `eval "$(kage shell-init)"`。 |
66
+ | `kage --help` / `--version` | 任意位置 | 用法 / 版本。 |
67
+
68
+ 在已有分身的仓库里直接运行 `kage`,会弹出交互选择器:新建一个分身,或对已有分身执行 **进入** / **finish** /
69
+ **删除**。当有多个分身又没指定名字时,`finish` 和 `rm` 也会弹出同样的选择器。
70
+
71
+ ### Shell 集成(可选)
72
+
73
+ ```bash
74
+ eval "$(kage shell-init)" # 加到 ~/.zshrc 或 ~/.bashrc
75
+ ```
76
+
77
+ 在分身内运行 `finish`/`rm` 会删掉你 shell 当前所在的目录。这个包装函数会自动把你 cd 回原仓库(否则 CLI
78
+ 没法改变父 shell 的目录),并为子命令和分身名加上 tab 补全。
79
+
80
+ ## 工作原理
81
+
82
+ - **隔离。** 一个分身是带独立 `.git` 的完整副本。kage **不**建分支 —— 分身停在原仓库当前分支上,且完全不
83
+ 介入 git 流程,所以分身内的分支 / PR 工作流由你自己决定(通过 `AGENTS.md` 告诉 agent)。
84
+ - **代码只经由 git 回流,绝不经过工作区。** 有 remote 时:push 分支、合并 PR。没有 remote 时:`finish` 会把
85
+ 分身的分支 fetch 进原仓库的 git,存成本地分支 `kage/<name>-<sha>`(原仓库工作区不动 —— 你想合并时再
86
+ `git merge`)。由于 fetch 无法保留未提交的改动,`finish` 拒绝删除有改动的分身,除非加 `--force`。
87
+ - **记忆只经由 `~/.pi` 回流,绝不重放。** 创建时拷入原仓库最近 5 个 session —— pi 的 resume 选择器能看到它们,
88
+ 但分身本身打开的是**全新** session。`finish` 时,分身自己产生的 session 整份拷回;你 resume 过的拷入 session
89
+ 会作为一个独立的新 session 回来,原仓库的原始 session 绝不被改动。
90
+ - **对 kage 而言原仓库是只读的。** 它只往外复制、只写 session 记忆 —— 即使原仓库里另有一个 session 正活跃,
91
+ 它也绝不碰原仓库的工作区。
92
+
93
+ ## 注意事项
94
+
95
+ - 副本是原仓库**当前**状态的快照,包含未提交的改动。
96
+ - **Submodule**:submodule 的 `.git` 是绝对路径,复制后会失效 —— 在分身里跑一次
97
+ `git submodule update --init`。
98
+ - Session 存储默认在 `~/.pi/agent/sessions`,可用 `KAGE_SESSIONS_DIR` 覆盖。
99
+
100
+ ## 开发
101
+
102
+ CLI 用 TypeScript 写,编译成单个零依赖文件:
103
+
104
+ - `src/kage.mts` → `bin/kage.mjs`(唯一发布的文件)
105
+ - `test/kage.test.mts` → `dist/` —— 启动编译后 CLI 的黑盒 `node:test` 冒烟测试
106
+
107
+ `bin/` 和 `dist/` 是被 gitignore 的构建产物;`bin/` 在 `npm install` 时(通过 `prepare`)生成,所以从克隆出来
108
+ 的仓库直接 `npm link` 即可。代码检查 / 格式化用 [Biome](https://biomejs.dev)(仅开发依赖)。
109
+
110
+ ```bash
111
+ npm run build # tsc: src/kage.mts → bin/kage.mjs
112
+ npm run lint # biome + tsc 类型检查
113
+ npm test # 构建 + node:test 冒烟测试(临时仓库,不联网)
114
+ ```
115
+
116
+ 发布:在 `package.json` 里更新 `version`,然后 `git tag vX.Y.Z && git push origin main vX.Y.Z`。
117
+ 任何 `v*` tag 都会触发 CI 跑 lint + 测试并执行 `npm publish --provenance`。
118
+
119
+ ## License
120
+
121
+ [MIT](./LICENSE)
package/bin/kage.mjs CHANGED
@@ -28,7 +28,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, wri
28
28
  import { homedir } from "node:os";
29
29
  import { basename, dirname, join, resolve, sep } from "node:path";
30
30
  import readline from "node:readline";
31
- const VERSION = "0.3.6"; // keep in sync with package.json (enforced by test)
31
+ const VERSION = "0.3.8"; // keep in sync with package.json (enforced by test)
32
32
  const MARKER = ".kage.json";
33
33
  const SESSIONS = process.env.KAGE_SESSIONS_DIR || join(homedir(), ".pi", "agent", "sessions");
34
34
  const RECENT_SESSIONS = 5; // how many of the origin's most-recent sessions to copy into a clone
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-kage",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "🥷 Shadow Clone Jutsu for your git repo: copy it into an isolated folder, work in parallel with pi, then merge the session memory back",
5
5
  "keywords": [
6
6
  "pi",