pi-kage 0.3.0 → 0.3.2

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.
Files changed (3) hide show
  1. package/README.md +10 -14
  2. package/bin/kage.mjs +9 -2
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -35,9 +35,9 @@ machine. Each parallel session gets its own working tree, its own branch, its ow
35
35
  Code merges the normal way: on GitHub. No local collisions, ever.
36
36
 
37
37
  And like a real Naruto shadow clone, it **carries your memory out** (the origin's 5 most recent pi
38
- sessions are copied into the clone, so you can `resume` them there) and **returns it on dispel** (the clone's
39
- *new* sessions are merged back into the original when you `finish`). The clone always opens a **fresh**
40
- session — kage never replays your old turns or fakes a "resumed" conversation.
38
+ sessions are copied into the clone, so you can `resume` them there) and **returns it on dispel** (the
39
+ clone's *new* sessions are merged back into the original when you `finish`). The clone always opens a
40
+ **fresh** session — kage never replays your old turns or fakes a "resumed" conversation.
41
41
 
42
42
  Why a full folder copy instead of `git worktree`? A worktree shares one `.git`, which means you
43
43
  can't check out the same branch twice, you share stash/refs, and you get a *fresh* checkout with no
@@ -126,8 +126,8 @@ for subcommands and clone names.
126
126
  |---|---|---|
127
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
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 (or inside the clone) | 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 (or inside the clone) | Discard a clone **without** merging memory. Refuses if it has local-only work unless `--force`. For abandoned experiments. |
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
131
  | `kage pull <path...>` | inside a clone | Copy specific files/dirs (even gitignored ones) back to the origin at the same relative path. |
132
132
  | `kage shell-init` | shell rc | Print a shell wrapper (cd-back after `finish`/`rm`) + tab completion. Use `eval "$(kage shell-init)"`. |
133
133
  | `kage --help` / `--version` | anywhere | Usage / version. |
@@ -158,15 +158,11 @@ Four invariants keep parallel work safe and lossless:
158
158
  - kage **doesn't create a branch** — the clone stays on the origin's current branch, and kage stays out
159
159
  of git flow entirely. Decide your own branching/PR workflow inside the clone (instruct the agent via
160
160
  your `AGENTS.md` / project conventions).
161
- - The clone opens a **fresh** pi session. The origin's 5 most recent sessions are copied in and are **resumable** via
162
- pi's resume picker. Real work belongs in the clone's own fresh session, but if you do resume a copied
163
- origin session and add turns, on `finish` that continuation is written back as a **separate** session
164
- (the origin's original session is left untouched), so nothing is lost and no active conversation is hijacked.
165
- - **Upgrading from an older kage:** clones created before the copy-in/fresh-session redesign carry a
166
- fabricated *seed* session in their `.kage.json` (`seedFile`/`seedLeafId`). `finish` no longer special-
167
- cases those, so finishing such a clone would copy the replayed seed context back into the origin. For
168
- any clone created by an older kage, prefer `kage rm` (the code is already on its branch / PR) instead
169
- of `kage finish`.
161
+ - The clone opens a **fresh** pi session. The origin's 5 most recent sessions are copied in and are
162
+ **resumable** via pi's resume picker. Real work belongs in the clone's own fresh session, but if you do
163
+ resume a copied origin session and add turns, on `finish` that continuation is written back as a
164
+ **separate** session (the origin's original session is left untouched), so nothing is lost and no
165
+ active conversation is hijacked.
170
166
  - **No remote?** `finish` still works losslessly: committed work that isn't on a remote is fetched into
171
167
  the origin as a local `kage/<name>-<sha>` branch (the exact name is printed; `git merge` it to
172
168
  integrate). The short sha keeps the ref unique, so reusing a clone name never collides. With a remote
package/bin/kage.mjs CHANGED
@@ -30,7 +30,7 @@ import { homedir } from "node:os";
30
30
  import { basename, dirname, join, resolve, sep } from "node:path";
31
31
  import readline from "node:readline";
32
32
 
33
- const VERSION = "0.3.0"; // keep in sync with package.json (enforced by test)
33
+ const VERSION = "0.3.2"; // keep in sync with package.json (enforced by test)
34
34
  const MARKER = ".kage.json";
35
35
  const SESSIONS = process.env.KAGE_SESSIONS_DIR || join(homedir(), ".pi", "agent", "sessions");
36
36
  const RECENT_SESSIONS = 5; // how many of the origin's most-recent sessions to copy into a clone
@@ -287,8 +287,15 @@ async function pickClone(action, name) {
287
287
  const here = repoTopLevel(process.cwd());
288
288
  const hm = here && readMarker(here);
289
289
  if (hm && !name) return { originRepo: hm.originRepo, clone: { dir: here, name: hm.name || basename(here), marker: hm } };
290
+ // If `name` resolves to a clone directory, use its marker directly — works from anywhere,
291
+ // even outside a repo (e.g. `kage rm ../app--fix` from the parent dir).
292
+ if (name) {
293
+ const asPath = resolve(name);
294
+ const pm = readMarker(asPath);
295
+ if (pm) return { originRepo: pm.originRepo, clone: { dir: asPath, name: pm.name || basename(asPath), marker: pm } };
296
+ }
290
297
  const originRepo = hm ? hm.originRepo : here;
291
- if (!originRepo) die("not a git repository");
298
+ if (!originRepo) die("not a git repository (run inside the repo or clone, or pass a path to a clone)");
292
299
  const clones = listClones(originRepo);
293
300
  if (clones.length === 0) die("no shadow clones found for this repo");
294
301
  if (name) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-kage",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
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",