pi-dev 0.1.7 → 0.2.0

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
@@ -42,21 +42,34 @@ You ask. It classifies. It executes. It reports.
42
42
  Requires Node ≥ 20 and the [pi runtime](https://github.com/badlogic/pi).
43
43
 
44
44
  ```bash
45
- # Install the skills + seed your global preferences
45
+ # Interactive: pick global vs project-local, then install + seed preferences
46
46
  npx pi-dev@latest install
47
47
 
48
- # Refresh skills only (keeps your preferences as-is)
48
+ # Or be explicit:
49
+ npx pi-dev@latest install --global # ~/.pi/agent/skills/ (every repo)
50
+ npx pi-dev@latest install --local # ./.pi/skills/ (this repo only)
51
+
52
+ # Refresh skills (auto-detects scope from disk; preferences are kept)
49
53
  npx pi-dev@latest update
50
54
 
51
- # See what's installed
55
+ # See what's installed under each scope
52
56
  npx pi-dev list
53
57
 
54
- # Verify your environment
58
+ # Verify the active scope's layout
55
59
  npx pi-dev doctor
56
60
  ```
57
61
 
58
- `install` copies the skill folders to `~/.pi/agent/skills/` (where the pi runtime looks) and seeds `~/.pi/agent/preferences.md` only if you don't already have one.
59
- `update` refreshes skill folders but leaves your preferences alone unless you pass `--include-prefs`.
62
+ **Scope choice cheat-sheet:**
63
+
64
+ | | global | local |
65
+ | --- | --- | --- |
66
+ | Skills | `~/.pi/agent/skills/` | `<repo>/.pi/skills/` |
67
+ | Preferences | `~/.pi/agent/preferences.md` | `<repo>/.pi/preferences.md` |
68
+ | Pi sessions see it from | every cwd | only this repo |
69
+ | Goes in the repo's git? | no | yes (commit `.pi/skills/`, gitignore `.pi/sessions/`) |
70
+ | Use when | the skills are part of *your* engineering taste | the skills are part of *the project's* contract |
71
+
72
+ Non-interactive runs (CI, piped input) default to `--global` silently. Pass `-y` to skip the prompt and accept the default.
60
73
 
61
74
  ## How `/do` actually flows
62
75
 
package/dist/cli.js CHANGED
@@ -9,20 +9,32 @@ function help() {
9
9
  console.log(`pi-dev — autonomous engineering skill framework for the pi runtime
10
10
 
11
11
  Usage:
12
- pi-dev install [--skip-prefs] Copy skills into ~/.pi/agent/skills and seed
13
- ~/.pi/agent/preferences.md if missing.
14
- pi-dev update [--include-prefs] Refresh skills. By default keeps your global
15
- preferences. Pass --include-prefs to overwrite.
16
- pi-dev list Show installed skills + global prefs path.
17
- pi-dev uninstall <skill> Soft-remove a skill (renamed to .removed-…).
18
- pi-dev doctor Check ~/.pi layout and external CLIs.
19
- pi-dev version Print version.
20
- pi-dev help This message.
12
+ pi-dev install [scope] [--skip-prefs] [--include-maintainer] [-y]
13
+ Install skills + seed preferences. Scope is one of:
14
+ --global ~/.pi/agent/skills/ (default, every pi session sees it)
15
+ --local .pi/skills/ in cwd (only this repo)
16
+ Without a flag, an interactive TTY is prompted; non-TTY defaults to global.
17
+ Pass -y to skip the prompt and accept the default.
18
+ Pass --include-maintainer to also install maintainer-only skills
19
+ (only useful if you are developing pi-dev itself).
20
+
21
+ pi-dev update [--include-prefs] [--include-maintainer] [--global|--local]
22
+ Refresh skills in place. Scope is auto-detected from disk
23
+ (local wins if .pi/skills/ exists in cwd). Preferences are kept by default;
24
+ pass --include-prefs to re-seed. Maintainer skills already on disk are
25
+ preserved automatically; pass --include-maintainer to add them on update.
26
+
27
+ pi-dev list Show installed skills under both scopes (if present).
28
+ pi-dev uninstall <skill> [--global|--local]
29
+ Soft-remove a skill (renamed to .removed-…).
30
+ pi-dev doctor Check the active scope's layout and external CLIs.
31
+ pi-dev version Print version.
32
+ pi-dev help This message.
21
33
 
22
34
  After install, in any pi session, you primarily call:
23
- /do — the one-shot engineering entry point
24
- /taste — view or update preferences
25
- /where — recall prior pi sessions for this cwd
35
+ /do — the one-shot engineering entry point
36
+ /taste — view or update preferences
37
+ /where — recall prior pi sessions for this cwd
26
38
 
27
39
  All other skills are invoked automatically by /do.
28
40
  `);
@@ -30,44 +42,66 @@ All other skills are invoked automatically by /do.
30
42
  function getFlag(name) {
31
43
  return args.includes(`--${name}`);
32
44
  }
33
- switch (cmd) {
34
- case "install":
35
- install({ skipPrefs: getFlag("skip-prefs") });
36
- break;
37
- case "update":
38
- update({ skipPrefs: !getFlag("include-prefs") });
39
- break;
40
- case "list":
41
- listInstalled();
42
- break;
43
- case "uninstall": {
44
- const name = args[1];
45
- if (!name) {
46
- console.error("Usage: pi-dev uninstall <skill>");
47
- process.exit(1);
45
+ function getScope() {
46
+ if (getFlag("global"))
47
+ return "global";
48
+ if (getFlag("local"))
49
+ return "local";
50
+ return undefined;
51
+ }
52
+ async function main() {
53
+ switch (cmd) {
54
+ case "install":
55
+ await install({
56
+ skipPrefs: getFlag("skip-prefs"),
57
+ scope: getScope(),
58
+ yes: getFlag("yes") || args.includes("-y"),
59
+ includeMaintainer: getFlag("include-maintainer"),
60
+ });
61
+ break;
62
+ case "update":
63
+ await update({
64
+ skipPrefs: !getFlag("include-prefs"),
65
+ scope: getScope(),
66
+ includeMaintainer: getFlag("include-maintainer"),
67
+ });
68
+ break;
69
+ case "list":
70
+ listInstalled();
71
+ break;
72
+ case "uninstall": {
73
+ const name = args[1];
74
+ if (!name || name.startsWith("--")) {
75
+ console.error("Usage: pi-dev uninstall <skill> [--global|--local]");
76
+ process.exit(1);
77
+ }
78
+ uninstallSkill(name, getScope());
79
+ break;
48
80
  }
49
- uninstallSkill(name);
50
- break;
51
- }
52
- case "doctor":
53
- doctor();
54
- break;
55
- case "version":
56
- case "--version":
57
- case "-v": {
58
- const here = dirname(fileURLToPath(import.meta.url));
59
- const pkg = JSON.parse(readFileSync(join(here, "..", "package.json"), "utf8"));
60
- console.log(pkg.version);
61
- break;
81
+ case "doctor":
82
+ doctor();
83
+ break;
84
+ case "version":
85
+ case "--version":
86
+ case "-v": {
87
+ const here = dirname(fileURLToPath(import.meta.url));
88
+ const pkg = JSON.parse(readFileSync(join(here, "..", "package.json"), "utf8"));
89
+ console.log(pkg.version);
90
+ break;
91
+ }
92
+ case "help":
93
+ case "--help":
94
+ case "-h":
95
+ case undefined:
96
+ help();
97
+ break;
98
+ default:
99
+ console.error(`Unknown command: ${cmd}\n`);
100
+ help();
101
+ process.exit(1);
62
102
  }
63
- case "help":
64
- case "--help":
65
- case "-h":
66
- case undefined:
67
- help();
68
- break;
69
- default:
70
- console.error(`Unknown command: ${cmd}\n`);
71
- help();
72
- process.exit(1);
73
103
  }
104
+ main().catch((err) => {
105
+ console.error(err);
106
+ process.exit(1);
107
+ });
package/dist/install.js CHANGED
@@ -1,21 +1,56 @@
1
1
  import { existsSync, mkdirSync, cpSync, copyFileSync, renameSync } from "node:fs";
2
2
  import { execSync } from "node:child_process";
3
3
  import { join } from "node:path";
4
- import { SKILLS } from "./manifest.js";
5
- import { PI_AGENT_DIR, PI_SKILLS_DIR, PI_GLOBAL_PREFS, PKG_SKILLS_DIR, PKG_GLOBAL_PREFS_PRESET, } from "./paths.js";
6
- export function install(opts = {}) {
7
- mkdirSync(PI_SKILLS_DIR, { recursive: true });
4
+ import { createInterface } from "node:readline";
5
+ import { SKILLS, CONSUMER_SKILLS } from "./manifest.js";
6
+ import { PKG_SKILLS_DIR, PKG_GLOBAL_PREFS_PRESET, destFor, } from "./paths.js";
7
+ function ask(question) {
8
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
9
+ return new Promise((res) => {
10
+ rl.question(question, (a) => {
11
+ rl.close();
12
+ res(a);
13
+ });
14
+ });
15
+ }
16
+ async function resolveScope(opts) {
17
+ if (opts.scope)
18
+ return opts.scope;
19
+ // Non-interactive (CI, piped) → default to global silently.
20
+ if (opts.yes || !process.stdin.isTTY)
21
+ return "global";
22
+ const cwd = process.cwd();
23
+ const { skillsDir: localSkills } = destFor("local", cwd);
24
+ const { skillsDir: globalSkills } = destFor("global");
25
+ console.log("");
26
+ console.log("pi-dev: where should the skills live?");
27
+ console.log("");
28
+ console.log(` [g] global → ${globalSkills}`);
29
+ console.log(` every pi session on this machine sees them.`);
30
+ console.log("");
31
+ console.log(` [l] local → ${localSkills}`);
32
+ console.log(` only pi sessions started from this repo see them.`);
33
+ console.log(` pi reads project-local config from .pi/ in the cwd.`);
34
+ console.log("");
35
+ const ans = (await ask("Choose [G/l] (default global): ")).trim().toLowerCase();
36
+ if (ans === "l" || ans === "local")
37
+ return "local";
38
+ return "global";
39
+ }
40
+ export async function install(opts = {}) {
41
+ const scope = await resolveScope(opts);
42
+ const { agentDir, skillsDir, prefsFile } = destFor(scope);
43
+ mkdirSync(skillsDir, { recursive: true });
44
+ const skillsToInstall = opts.includeMaintainer ? SKILLS : CONSUMER_SKILLS;
8
45
  let copied = 0;
9
- for (const skill of SKILLS) {
46
+ for (const skill of skillsToInstall) {
10
47
  const src = join(PKG_SKILLS_DIR, skill.name);
11
- const dst = join(PI_SKILLS_DIR, skill.name);
48
+ const dst = join(skillsDir, skill.name);
12
49
  if (!existsSync(src)) {
13
50
  console.warn(` skip ${skill.name} (source not found in package)`);
14
51
  continue;
15
52
  }
16
53
  if (existsSync(dst) && !opts.force) {
17
- // Replace contents but keep the directory; safer than rmSync for skills
18
- // the user may have customised lightly.
19
54
  cpSync(src, dst, { recursive: true, force: true });
20
55
  }
21
56
  else {
@@ -23,63 +58,104 @@ export function install(opts = {}) {
23
58
  }
24
59
  copied++;
25
60
  }
26
- console.log(`Installed ${copied} skill(s) into ${PI_SKILLS_DIR}`);
61
+ console.log(`Installed ${copied} skill(s) into ${skillsDir} [${scope}]`);
27
62
  if (opts.skipPrefs) {
28
- console.log("Skipped global preferences (use --include-prefs on update to merge in new keys).");
63
+ console.log("Skipped preferences (pass --include-prefs on update to merge in new keys).");
29
64
  return;
30
65
  }
31
- if (!existsSync(PI_GLOBAL_PREFS)) {
66
+ if (!existsSync(prefsFile)) {
32
67
  if (existsSync(PKG_GLOBAL_PREFS_PRESET)) {
33
- copyFileSync(PKG_GLOBAL_PREFS_PRESET, PI_GLOBAL_PREFS);
34
- console.log(`Seeded global preferences at ${PI_GLOBAL_PREFS}`);
68
+ copyFileSync(PKG_GLOBAL_PREFS_PRESET, prefsFile);
69
+ console.log(`Seeded preferences at ${prefsFile} [${scope}]`);
35
70
  }
36
71
  }
37
72
  else {
38
- console.log(`Existing global preferences kept at ${PI_GLOBAL_PREFS} (use --include-prefs to merge new keys).`);
73
+ console.log(`Existing preferences kept at ${prefsFile} (pass --include-prefs to merge new keys).`);
74
+ }
75
+ if (scope === "local") {
76
+ console.log("");
77
+ console.log("Tip: add `.pi/sessions/` to .gitignore (sessions are local-only).");
78
+ console.log(" Skills under `.pi/skills/` are safe to commit.");
39
79
  }
40
80
  }
41
- export function update(opts = {}) {
42
- // Update is install with force, but defaults to NOT overwriting global prefs.
43
- install({ ...opts, force: true, skipPrefs: !opts.skipPrefs ? true : opts.skipPrefs });
81
+ export async function update(opts = {}) {
82
+ // Update is install with force. If scope wasn't passed, infer it from what's
83
+ // already on disk: a local `.pi/skills/` in cwd wins over global.
84
+ let scope = opts.scope;
85
+ if (!scope) {
86
+ const local = destFor("local");
87
+ if (existsSync(local.skillsDir))
88
+ scope = "local";
89
+ else
90
+ scope = "global";
91
+ }
92
+ // On update, preserve the maintainer set if maintainer skills are already on
93
+ // disk — no need to make the user re-pass the flag every time.
94
+ const maintainerAlreadyInstalled = existsSync(join(destFor(scope).skillsDir, "improve-skill-flow", "SKILL.md"));
95
+ await install({
96
+ ...opts,
97
+ scope,
98
+ force: true,
99
+ skipPrefs: !opts.skipPrefs ? true : opts.skipPrefs,
100
+ yes: true,
101
+ includeMaintainer: opts.includeMaintainer || maintainerAlreadyInstalled,
102
+ });
44
103
  }
45
- export function uninstallSkill(name) {
46
- const dst = join(PI_SKILLS_DIR, name);
104
+ export function uninstallSkill(name, scope) {
105
+ const resolved = scope ?? (existsSync(destFor("local").skillsDir) ? "local" : "global");
106
+ const { skillsDir } = destFor(resolved);
107
+ const dst = join(skillsDir, name);
47
108
  if (!existsSync(dst)) {
48
- console.error(`Skill not installed: ${name}`);
109
+ console.error(`Skill not installed [${resolved}]: ${name}`);
49
110
  process.exit(1);
50
111
  }
51
- // Soft delete: rename to .removed-<ts>
52
112
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
53
- const moved = join(PI_SKILLS_DIR, `.removed-${stamp}-${name}`);
113
+ const moved = join(skillsDir, `.removed-${stamp}-${name}`);
54
114
  renameSync(dst, moved);
55
- console.log(`Uninstalled ${name} (moved to ${moved}).`);
115
+ console.log(`Uninstalled ${name} from ${resolved} (moved to ${moved}).`);
56
116
  }
57
117
  export function listInstalled() {
58
- console.log(`Skills directory: ${PI_SKILLS_DIR}\n`);
59
- for (const skill of SKILLS) {
60
- const path = join(PI_SKILLS_DIR, skill.name, "SKILL.md");
61
- const installed = existsSync(path);
62
- const tag = skill.kind === "human" ? "[user] " : "[support]";
63
- const status = installed ? "ok" : "missing";
64
- console.log(` ${tag} /${skill.name.padEnd(34)} ${status} — ${skill.summary}`);
118
+ const local = destFor("local");
119
+ const global = destFor("global");
120
+ const targets = [];
121
+ if (existsSync(local.skillsDir))
122
+ targets.push({ label: "local", ...local });
123
+ targets.push({ label: "global", ...global });
124
+ for (const t of targets) {
125
+ console.log(`Skills directory [${t.label}]: ${t.skillsDir}`);
126
+ for (const skill of SKILLS) {
127
+ const path = join(t.skillsDir, skill.name, "SKILL.md");
128
+ const installed = existsSync(path);
129
+ const tag = skill.kind === "human"
130
+ ? "[user] "
131
+ : skill.kind === "maintainer"
132
+ ? "[maintainer]"
133
+ : "[support] ";
134
+ const status = installed ? "ok " : "missing";
135
+ console.log(` ${tag} /${skill.name.padEnd(34)} ${status} — ${skill.summary}`);
136
+ }
137
+ console.log(`Preferences: ${existsSync(t.prefsFile) ? t.prefsFile : "(missing)"}`);
138
+ console.log("");
65
139
  }
66
- console.log(`\nGlobal preferences: ${existsSync(PI_GLOBAL_PREFS) ? PI_GLOBAL_PREFS : "(missing)"}`);
67
140
  }
68
141
  export function doctor() {
69
142
  const issues = [];
70
- if (!existsSync(PI_AGENT_DIR))
71
- issues.push(`~/.pi/agent missing run: pi-dev install`);
72
- if (!existsSync(PI_SKILLS_DIR))
73
- issues.push(`~/.pi/agent/skills missing run: pi-dev install`);
143
+ const local = destFor("local");
144
+ const hasLocal = existsSync(local.skillsDir);
145
+ const activeScope = hasLocal ? "local" : "global";
146
+ const { agentDir, skillsDir, prefsFile } = destFor(activeScope);
147
+ if (!existsSync(agentDir))
148
+ issues.push(`${agentDir} missing — run: pi-dev install`);
149
+ if (!existsSync(skillsDir))
150
+ issues.push(`${skillsDir} missing — run: pi-dev install`);
74
151
  for (const skill of SKILLS.filter((s) => s.kind === "human")) {
75
- if (!existsSync(join(PI_SKILLS_DIR, skill.name, "SKILL.md"))) {
76
- issues.push(`Human skill /${skill.name} not installed — run: pi-dev update`);
152
+ if (!existsSync(join(skillsDir, skill.name, "SKILL.md"))) {
153
+ issues.push(`Human skill /${skill.name} not installed [${activeScope}] — run: pi-dev update`);
77
154
  }
78
155
  }
79
- if (!existsSync(PI_GLOBAL_PREFS)) {
80
- issues.push(`Global preferences missing — run: pi-dev install (or copy presets/preferences.md manually)`);
156
+ if (!existsSync(prefsFile)) {
157
+ issues.push(`Preferences missing at ${prefsFile} — run: pi-dev install (or copy presets/preferences.md manually)`);
81
158
  }
82
- // Quick external check (best-effort)
83
159
  const checkCmd = (cmd, label) => {
84
160
  try {
85
161
  execSync(`${cmd} --version`, { stdio: "ignore" });
@@ -90,6 +166,7 @@ export function doctor() {
90
166
  };
91
167
  checkCmd("git", "git");
92
168
  checkCmd("gh", "gh CLI");
169
+ console.log(`Active scope: ${activeScope}`);
93
170
  if (issues.length === 0) {
94
171
  console.log("All checks passed.");
95
172
  return;
package/dist/manifest.js CHANGED
@@ -3,6 +3,8 @@
3
3
  *
4
4
  * `human` skills are what the user calls directly: /do, /taste, /where.
5
5
  * `support` skills are auto-invoked by /do or /migrate.
6
+ * `maintainer` skills are for pi-dev framework maintenance only; they are
7
+ * NOT installed for consumers by default (pass --include-maintainer to opt in).
6
8
  *
7
9
  * The skill names match the directory names under `skills/`.
8
10
  */
@@ -11,7 +13,8 @@ export const SKILLS = [
11
13
  { name: "do", kind: "human", summary: "Do the engineering work end-to-end." },
12
14
  { name: "taste", kind: "human", summary: "View / update / onboard preferences." },
13
15
  { name: "where", kind: "human", summary: "Recall prior pi sessions for this cwd." },
14
- { name: "improve-skill-flow", kind: "human", summary: "Audit pi session telemetry and propose evidence-based SKILL.md edits." },
16
+ // Maintainer-only pi-dev framework itself, not shipped to consumers.
17
+ { name: "improve-skill-flow", kind: "maintainer", summary: "Maintainer-only. Audit pi telemetry, propose SKILL.md edits, release." },
15
18
  // Auto-invoked support skills
16
19
  { name: "migrate", kind: "support", summary: "Strict migration gate before /do can run." },
17
20
  { name: "setup", kind: "support", summary: "Scaffold issue-tracker / triage / domain docs." },
@@ -27,3 +30,5 @@ export const SKILLS = [
27
30
  ];
28
31
  export const HUMAN_SKILLS = SKILLS.filter((s) => s.kind === "human");
29
32
  export const SUPPORT_SKILLS = SKILLS.filter((s) => s.kind === "support");
33
+ export const MAINTAINER_SKILLS = SKILLS.filter((s) => s.kind === "maintainer");
34
+ export const CONSUMER_SKILLS = SKILLS.filter((s) => s.kind !== "maintainer");
package/dist/paths.js CHANGED
@@ -12,3 +12,15 @@ export const PKG_ROOT = resolve(__dirname, "..");
12
12
  export const PKG_SKILLS_DIR = join(PKG_ROOT, "skills");
13
13
  export const PKG_PRESETS_DIR = join(PKG_ROOT, "presets");
14
14
  export const PKG_GLOBAL_PREFS_PRESET = join(PKG_PRESETS_DIR, "preferences.md");
15
+ /** Compute install destinations for a given scope. `local` resolves against `cwd`. */
16
+ export function destFor(scope, cwd = process.cwd()) {
17
+ if (scope === "global") {
18
+ return { agentDir: PI_AGENT_DIR, skillsDir: PI_SKILLS_DIR, prefsFile: PI_GLOBAL_PREFS };
19
+ }
20
+ const agentDir = join(cwd, ".pi");
21
+ return {
22
+ agentDir,
23
+ skillsDir: join(agentDir, "skills"),
24
+ prefsFile: join(agentDir, "preferences.md"),
25
+ };
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-dev",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "An autonomous engineering skill framework for the pi runtime — built on Matt Pocock's skills.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,48 +1,71 @@
1
1
  ---
2
2
  name: improve-skill-flow
3
- description: Analyse real pi-runtime session telemetry from a consumer repo to find where the engineering skills (especially /do's chain) drift from their stated contract, then propose evidence-anchored edits to the SKILL.md files. Use when the user wants to improve, audit, debug, or evolve the pi-dev skill framework itself based on what actually happened in real sessions ("스킬 개선하자", "do 가 왜 멈춰", "히스토리 보고 분석해서 스킬 고치자", "메타 스킬 작업", etc).
3
+ description: MAINTAINER-ONLY. Analyse real pi-runtime session telemetry from any consumer repo on this machine to find where the engineering skills (especially /do's chain) drift from their stated contract, then propose evidence-anchored edits to pi-dev's own SKILL.md files. Run only from inside the pi-dev repo — it edits pi-dev sources and triggers a release. Use when the maintainer wants to improve, audit, debug, or evolve the pi-dev skill framework itself based on what actually happened in real sessions ("스킬 개선하자", "do 가 왜 멈춰", "히스토리 보고 분석해서 스킬 고치자", "메타 스킬 작업", etc).
4
4
  ---
5
5
 
6
6
  # /improve-skill-flow — Meta-skill for evidence-based skill improvement
7
7
 
8
- The pi-dev skills are pure markdown. They get better by reading what real sessions did, comparing that to what the SKILL.md said *should* happen, and editing the gap closed. This skill is the canonical loop for that.
8
+ **Audience: pi-dev maintainers only.** Consumers do not get this skill installed. Consumers improve their own setup by editing `docs/agents/preferences.md` (per project) or `~/.pi/agent/preferences.md` (per machine), not by editing SKILL.md bodies. The framework is fixed for them; only the maintainer changes it.
9
+
10
+ The pi-dev skills are pure markdown. They get better when the maintainer reads what real sessions did across consumer repos, compares that to what the SKILL.md said *should* happen, and edits the gap closed. This skill is the canonical loop for that.
9
11
 
10
12
  The point: **never edit a SKILL.md from gut feeling.** Edit because session N showed phase P violated predicate Q on M occasions, and here is the line that would have prevented it.
11
13
 
14
+ ## Pre-flight (hard gate)
15
+
16
+ Refuse to run unless cwd is the pi-dev repo. Releases happen from here; nothing else makes sense.
17
+
18
+ ```bash
19
+ origin=$(git -C "$PWD" remote get-url origin 2>/dev/null || echo "")
20
+ pkg_name=$(jq -r '.name // empty' package.json 2>/dev/null)
21
+ ok=0
22
+ case "$origin" in *pi-dev*|*pi-dev.git) ok=1 ;; esac
23
+ [ "$pkg_name" = "pi-dev" ] && ok=1
24
+ if [ "$ok" != 1 ]; then
25
+ echo "this skill is maintainer-only; cd into the pi-dev repo and re-run"
26
+ exit 1
27
+ fi
28
+ ```
29
+
30
+ If the gate fails, stop. Do not proceed in a consumer repo.
31
+
12
32
  ## When to run
13
33
 
14
- - A consumer repo has accumulated at least one real day of pi sessions (≈ 3+ `.jsonl` files).
34
+ - A consumer repo on this machine has accumulated at least one real day of pi sessions (≈ 3+ `.jsonl` files).
15
35
  - A specific skill is suspected of misbehaving ("why does `/do` keep stopping?").
16
36
  - After landing a skill change, to verify the next session(s) actually follow the new wording.
17
37
  - Periodically (every N releases) as a regression sweep across all human-facing skills.
18
38
 
39
+ Always from inside the pi-dev checkout (see Pre-flight).
40
+
19
41
  ## What this skill is NOT
20
42
 
21
- - Not for analysing the *codebase* of the consumer repo — that is `improve-codebase-architecture`.
43
+ - Not for analysing the *codebase* of any repo — that is `improve-codebase-architecture`.
22
44
  - Not for shipping engineering work — it does not invoke `/do`. Findings turn into proposed SKILL.md diffs, which are committed via the normal release-please flow on `pi-dev`.
23
45
  - Not a real-time monitor — it reads completed session files.
46
+ - Not a consumer-facing tool. Consumers don't get this skill installed; they tune their setup via `preferences.md`, not by editing SKILL bodies.
24
47
 
25
48
  ## Inputs
26
49
 
27
- - **Target repo path** (or its sessions directory). Defaults: the user names a repo; you resolve it.
50
+ - **Consumer repo path** (or its sessions directory) to audit. The maintainer names a repo on this machine; you resolve its sessions dir.
28
51
  - Optional: a specific skill name to focus the audit on (`do`, `migrate`, `triage`, …).
29
52
  - Optional: a date range.
30
- - **Install scope** for any fixes that come out of the audit — `global` or `project`. Auto-detected (see Step 5.5); user can override per finding.
53
+ - **Fix scope** per finding — `framework` or `consumer-prefs`. Defaults set in Step 5.5; maintainer can flip individual rows before applying.
31
54
 
32
- ## Install scopes
55
+ ## Fix scopes
33
56
 
34
- pi-runtime today loads skill bodies from a single location: `~/.pi/agent/skills/<name>/SKILL.md`. The framework's 3-layer override is on **preferences**, not on SKILL bodies. So a finding lands in one of two places:
57
+ pi-runtime today loads skill bodies from a single location (`~/.pi/agent/skills/<name>/SKILL.md` for global installs, `<repo>/.pi/skills/<name>/SKILL.md` for local installs). The framework's 3-layer override is on **preferences**, not on SKILL bodies. So a finding lands in one of two places:
35
58
 
36
59
  | scope | lands in | reaches | propagation | when to pick |
37
60
  | --- | --- | --- | --- | --- |
38
- | **global** | `pi-dev`'s `skills/<name>/SKILL.md` | every consumer after the next `npx pi-dev update` | release-please → npm publish | the SKILL.md wording itself is wrong; gap shows up generically |
39
- | **project** | consumer repo's `docs/agents/preferences.md` (Project taboos / Diagnosis posture / Local-live playbook / Free notes — whichever section fits) | only this repo, on every `/do` bootstrap | regular consumer-repo commit | gap is the repo's domain / paths / conventions, not the SKILL.md |
61
+ | **framework** | this repo's `skills/<name>/SKILL.md` | every consumer after the next `npx pi-dev update` | release-please → npm publish | the SKILL.md wording itself is wrong; gap shows up generically |
62
+ | **consumer-prefs** | the audited consumer repo's `docs/agents/preferences.md` (Project taboos / Diagnosis posture / Local-live playbook / Free notes — whichever section fits) | only that repo, on every `/do` bootstrap | regular consumer-repo commit | gap is the consumer repo's domain / paths / conventions, not the SKILL.md |
40
63
 
41
64
  Notes:
42
65
 
43
- - A `global` apply is **always** mirrored into `~/.pi/agent/skills/<name>/` on the operator's machine so the next session picks it up immediately, without waiting for npm.
44
- - A `project` apply touches no pi-dev files. It is committed to the consumer repo only.
45
- - A single audit may produce a mix of global and project findings. Decide scope per finding, not per audit.
66
+ - A `framework` apply is **always** mirrored into `~/.pi/agent/skills/<name>/` on this machine so the next session picks it up immediately, without waiting for npm.
67
+ - A `consumer-prefs` apply touches no pi-dev files. It is committed to the consumer repo only.
68
+ - A single audit may produce a mix of framework and consumer-prefs findings. Decide scope per finding, not per audit.
46
69
 
47
70
  ## Session-data location & format
48
71
 
@@ -82,13 +105,13 @@ Timestamps on `message` records are ISO strings; some other record types use int
82
105
 
83
106
  ### 1 — Scope and load
84
107
 
85
- Ask the user (one round, only if not already specified):
108
+ Ask the maintainer (one round, only if not already specified):
86
109
 
87
- - target repo (or "all repos with sessions")
110
+ - target consumer repo (or "all repos with sessions on this machine")
88
111
  - a skill to focus on, or "everything"
89
112
  - a date range or "all"
90
113
 
91
- Resolve the sessions directory. List the `.jsonl` files with size + line count so the user can see the input scale.
114
+ Resolve the sessions directory. List the `.jsonl` files with size + line count so the maintainer can see the input scale.
92
115
 
93
116
  ### 2 — Build the raw signal table
94
117
 
@@ -116,7 +139,7 @@ Use a deterministic Python or shell script you write once and check into `/tmp`
116
139
 
117
140
  ### 3 — Cross-reference with repo state
118
141
 
119
- For the same date range, pull:
142
+ For the same date range, pull (against the **consumer repo** being audited):
120
143
 
121
144
  - `git log --since=<start> --pretty=format:"%h %ad %s"` — commit cadence vs. the predicate `auto-commit-per-slice`.
122
145
  - `gh issue list / pr list` (if GitHub) — slice/PR shape vs. `default-issue-style=vertical-slice`.
@@ -126,7 +149,7 @@ For the same date range, pull:
126
149
  Cross-reference each signal against:
127
150
 
128
151
  - The skill's **terminal predicate** in `do/SKILL.md` → "Phase contracts".
129
- - The repo's **`docs/agents/preferences.md`** taboos and `auto-*` settings.
152
+ - The consumer repo's **`docs/agents/preferences.md`** taboos and `auto-*` settings.
130
153
  - The hard rules in the skill being audited.
131
154
 
132
155
  ### 4 — Score the gaps
@@ -157,31 +180,17 @@ For every 🔴 / 🟡 row, quote the smallest piece of evidence that makes the g
157
180
 
158
181
  If a finding cannot be backed by an excerpt, it is not actionable yet — demote to a TODO and keep digging.
159
182
 
160
- ### 5.5 — Decide install scope per finding (auto + user-overridable)
161
-
162
- For each 🔴 / 🟡 finding, pick a default scope using this two-step heuristic, then show the table to the user once and let them flip individual rows before applying.
183
+ ### 5.5 — Decide fix scope per finding (auto + maintainer-overridable)
163
184
 
164
- **Step A detect operator context.** Run once at the start of this step:
185
+ For each 🔴 / 🟡 finding, pick a default scope using the heuristic below, then show the table once and let the maintainer flip individual rows before applying.
165
186
 
166
- ```bash
167
- origin=$(git -C "$PWD" remote get-url origin 2>/dev/null || echo "")
168
- pkg_name=$(jq -r '.name // empty' package.json 2>/dev/null)
169
- is_maintainer=false
170
- case "$origin" in *pi-dev*|*pi-dev.git) is_maintainer=true ;; esac
171
- [ "$pkg_name" = "pi-dev" ] && is_maintainer=true
172
- echo "operator_context=$([ \"$is_maintainer\" = true ] && echo maintainer || echo consumer)"
173
- ```
174
-
175
- - `operator_context=maintainer` → cwd is the pi-dev repo itself; the release path is available.
176
- - `operator_context=consumer` → cwd is a downstream repo; no release path. `global` findings here become "draft a patch + open an upstream PR / issue" rather than "push and release".
177
-
178
- **Step B — score each finding.** Default to `global` if the finding matches **any** of:
187
+ Default to `framework` if the finding matches **any** of:
179
188
 
180
189
  - Cites SKILL.md wording / phase / predicate / rule numbers.
181
190
  - The proposed fix is a generic anti-pattern string, a terminator literal, a runway line, or a lockout that any repo would benefit from.
182
191
  - The same gap would plausibly show up in two or more consumer repos.
183
192
 
184
- Default to `project` if the finding matches **any** of:
193
+ Default to `consumer-prefs` if the finding matches **any** of:
185
194
 
186
195
  - Cites a repo-specific path (`src/core/...`, `bin/...-smoke.ts`), brand, schema, table, or domain term.
187
196
  - The fix is a taboo, a smoke convention, an env / boot detail, or a glossary entry.
@@ -190,28 +199,28 @@ Default to `project` if the finding matches **any** of:
190
199
  Present the scope-decision table:
191
200
 
192
201
  ```
193
- | # | finding (short) | default scope | target file | flip? |
194
- | - | ---------------------------------------- | ------------- | ------------------------------------ | ----- |
195
- | 1 | /do hands flow back between phases | global | pi-dev:skills/do/SKILL.md | |
196
- | 2 | docs/handoff/ resurrected after marker | global | pi-dev:skills/migrate/SKILL.md | |
197
- | 3 | retro-action-item label still alive | project | hugn:docs/agents/preferences.md | |
198
- | 4 | smoke command name changed in S058 | project | hugn:docs/agents/preferences.md | |
202
+ | # | finding (short) | default scope | target file | flip? |
203
+ | - | ---------------------------------------- | ---------------- | ------------------------------------ | ----- |
204
+ | 1 | /do hands flow back between phases | framework | pi-dev:skills/do/SKILL.md | |
205
+ | 2 | docs/handoff/ resurrected after marker | framework | pi-dev:skills/migrate/SKILL.md | |
206
+ | 3 | retro-action-item label still alive | consumer-prefs | hugn:docs/agents/preferences.md | |
207
+ | 4 | smoke command name changed in S058 | consumer-prefs | hugn:docs/agents/preferences.md | |
199
208
  ```
200
209
 
201
- Ask the user once: "OK to proceed with these scopes? Reply with row numbers to flip, or `go`." Apply their flips and move on. If `operator_context=consumer`, any rows still marked `global` get the suffix `(via upstream PR — cannot release locally)` and the apply step adjusts accordingly.
210
+ Ask once: "OK to proceed with these scopes? Reply with row numbers to flip, or `go`." Apply the flips and move on.
202
211
 
203
212
  ### 6 — Propose edits (per-finding, scoped)
204
213
 
205
214
  For each 🔴 / 🟡 finding, draft the smallest possible edit that, **if it had been in place at session time, would have prevented the gap.** The shape of the draft depends on the scope from Step 5.5:
206
215
 
207
- **Global findings (target: pi-dev SKILL.md):**
216
+ **Framework findings (target: pi-dev SKILL.md):**
208
217
 
209
218
  - Edit a **rule** or a **step**, not a flavour sentence. The model must be able to detect the constraint in its own draft output.
210
219
  - Prefer **explicit anti-pattern strings** ("Do not say 'shall I continue?'") over abstract injunctions ("be decisive"). The hugn-2026-05 audit showed that named anti-patterns work.
211
220
  - Prefer **terminal markers** ("the summary's last line must be one of these two literals: …") over qualitative descriptions of "good wrap-up".
212
221
  - Update **at most three skills per run.** More than that means findings aren't anchored well enough.
213
222
 
214
- **Project findings (target: consumer's `docs/agents/preferences.md`):**
223
+ **Consumer-prefs findings (target: that repo's `docs/agents/preferences.md`):**
215
224
 
216
225
  - Pick the *narrowest* existing section that fits before adding a new one. Mapping:
217
226
 
@@ -228,31 +237,25 @@ For each 🔴 / 🟡 finding, draft the smallest possible edit that, **if it had
228
237
  - One bullet per finding. Reference the evidence ticket ("S058 smoke name", "#103 missing disclaimer") so the line stays auditable.
229
238
  - Do **not** invent new top-level sections unless three findings legitimately share one.
230
239
 
231
- Show all drafts as one unified diff per target file before applying. Group by target file: pi-dev's `skills/<name>/SKILL.md` first (global), then consumer's `docs/agents/preferences.md` (project).
240
+ Show all drafts as one unified diff per target file before applying. Group by target file: pi-dev's `skills/<name>/SKILL.md` first (framework), then the consumer's `docs/agents/preferences.md` (consumer-prefs).
232
241
 
233
242
  ### 7 — Apply, release, verify (branches on scope)
234
243
 
235
244
  Run both branches if the audit produced mixed-scope findings. Each branch has its own terminal state.
236
245
 
237
- **7a. Global branch** — only if any finding was approved as `global` **and** `operator_context=maintainer`:
246
+ **7a. Framework branch** — only if any finding was approved as `framework`:
238
247
 
239
- 1. From the pi-dev checkout: `git add skills/<name>/SKILL.md && git commit -m "<conventional commit anchoring the evidence>"`. Commit body must cite the signal that motivated each change.
248
+ 1. From the pi-dev checkout (this repo): `git add skills/<name>/SKILL.md && git commit -m "<conventional commit anchoring the evidence>"`. Commit body must cite the signal that motivated each change.
240
249
  2. `cp` each edited SKILL.md into `~/.pi/agent/skills/<name>/` so the **next** session anywhere picks up the change immediately (release-please takes a minute and a half).
241
250
  3. `git push origin main`; release-please opens the version-bump PR; merge it; npm publish runs automatically.
242
251
  4. Confirm `npm view pi-dev@latest version` matches the bumped tag.
243
252
 
244
- **7a'Global findings when `operator_context=consumer`:** you cannot release. Instead:
253
+ **7b. Consumer-prefs branch** only if any finding was approved as `consumer-prefs`:
245
254
 
246
- 1. Stash the proposed diffs to `/tmp/pi-dev-upstream-<date>.patch` with one file per skill.
247
- 2. Open an issue on `pi-dev` (or a PR if the operator has clone+push rights) with the evidence excerpts and the patch attached.
248
- 3. As a hotfix for this machine only, optionally `cp` the edited bodies into `~/.pi/agent/skills/<name>/` and note in the issue that the next `pi-dev update` will overwrite them — which is the desired end state once the upstream change lands.
249
-
250
- **7b. Project branch** — only if any finding was approved as `project`:
251
-
252
- 1. In the consumer repo: edit `docs/agents/preferences.md` per the drafts from Step 6. Keep the migration marker at the very end of the file undisturbed.
255
+ 1. In the audited consumer repo: edit `docs/agents/preferences.md` per the drafts from Step 6. Keep the migration marker at the very end of the file undisturbed.
253
256
  2. Bump the `last-updated` line at the top of the file to today's UTC date.
254
257
  3. `git add docs/agents/preferences.md && git commit -m "docs(agents): <one-liner per finding>"`. Conventional Commits apply.
255
- 4. Push per the repo's normal workflow. No release-please involvement — preferences are not packaged.
258
+ 4. Push per that repo's normal workflow. No release-please involvement — preferences are not packaged.
256
259
 
257
260
  **Verification (both branches).** After the next pi session in the affected repo:
258
261
 
@@ -265,9 +268,9 @@ Run both branches if the audit produced mixed-scope findings. Each branch has it
265
268
  This skill is done when **all four** are true:
266
269
 
267
270
  1. A signal table with severities and evidence excerpts has been presented.
268
- 2. Each finding has an approved scope (`global` / `project` / `defer`) on record, defaulted by Step 5.5 and confirmed by the user.
269
- 3. Either (a) zero 🔴 findings — flow is healthy, recorded as "no change this cycle", OR (b) each 🔴 finding has landed in its scope's target file (or been stashed + filed upstream when an operator-context mismatch prevents release).
270
- 4. For any landed change: if `global` and `maintainer`, the npm version has bumped (`npm view pi-dev@latest version`); if `project`, the consumer repo has the commit on its push-stream. Either way, the next-session re-audit plan is stated.
271
+ 2. Each finding has an approved scope (`framework` / `consumer-prefs` / `defer`) on record, defaulted by Step 5.5 and confirmed by the maintainer.
272
+ 3. Either (a) zero 🔴 findings — flow is healthy, recorded as "no change this cycle", OR (b) each 🔴 finding has landed in its scope's target file.
273
+ 4. For any landed change: if `framework`, the npm version has bumped (`npm view pi-dev@latest version`); if `consumer-prefs`, the consumer repo has the commit on its push-stream. Either way, the next-session re-audit plan is stated.
271
274
 
272
275
  The summary's **last line** must be one of:
273
276
 
@@ -276,17 +279,14 @@ audit complete — no changes this cycle.
276
279
  ```
277
280
 
278
281
  ```
279
- audit complete — global v<X.Y.Z> released, project commit <sha>, next re-audit after the next session.
280
- ```
281
-
282
- ```
283
- audit complete — upstream issue <#N> filed, project commit <sha>, hotfix mirrored to ~/.pi.
282
+ audit complete — framework v<X.Y.Z> released, consumer-prefs commit <sha>, next re-audit after the next session.
284
283
  ```
285
284
 
286
285
  ## What this skill does not do
287
286
 
288
- - It does not modify a consumer repo's code, issues, or preferences. It only edits **pi-dev's own `skills/`**.
287
+ - It does not modify a consumer repo's code or issues. It edits **pi-dev's own `skills/`** (framework scope) and — only when the audit demands it — the consumer's `docs/agents/preferences.md` (consumer-prefs scope).
289
288
  - It does not invent gaps from first principles. Every finding must come from a session excerpt or a repo-state probe.
289
+ - It does not run from a consumer repo. The Pre-flight gate refuses; cd into pi-dev first.
290
290
  - It does not run faster than the data allows — if there is only one session, run it but say so up front; the signal is noisy.
291
291
 
292
292
  ## Heuristics
@@ -299,4 +299,4 @@ audit complete — upstream issue <#N> filed, project commit <sha>, hotfix mirro
299
299
 
300
300
  ## Why this skill exists
301
301
 
302
- Skills are prose. Prose drifts. Without a feedback loop, the SKILL.md files become wishful thinking that the agent ignores in real sessions. This skill is the loop.
302
+ Skills are prose. Prose drifts. Without a feedback loop, the SKILL.md files become wishful thinking that the agent ignores in real sessions. This skill is the loop — and it is the maintainer's loop, not the consumer's.
@@ -1,11 +1,17 @@
1
1
  ---
2
2
  name: where
3
- description: Quickly absorb relevant prior pi sessions for the current cwd so multi-session work continues without rediscovery. Use when the user says "지난주에 뭐했지", "where were we", "이어서 가자", "다시 시작", or when /do detects continuation intent.
3
+ description: Answer "where are we?" for this cwd — what stage the work is at, what just happened, and what comes next — by reading prior pi sessions, git, and the issue tracker. Use when the user says "지금 어디야", "우리 어디까지 했지", "지난주에 뭐했지", "where were we", "이어서 가자", "다시 시작", "다음 로드맵", or when /do detects continuation intent.
4
4
  ---
5
5
 
6
- # /where — Recall pi History
6
+ # /where — Where Are We?
7
7
 
8
- This is a pi-specific skill. pi stores every session as a JSONL stream under `~/.pi/agent/sessions/<encoded-cwd>/<ts>_<sessionId>.jsonl`. This skill gives the agent a disciplined way to find and ingest only the slice of history that matters, without bloating context.
8
+ The essence of this skill: answer three questions, in this order, about the current cwd.
9
+
10
+ 1. **어디까지 왔나 (stage)** — what phase / slice / release the work is in *right now*.
11
+ 2. **최근 뭐했나 (recent)** — what the last 1–3 sessions actually did, condensed.
12
+ 3. **다음 뭐 할까 (next)** — the most plausible next action, with the file / issue / command that picks it up.
13
+
14
+ pi stores every session as a JSONL stream under `~/.pi/agent/sessions/<encoded-cwd>/<ts>_<sessionId>.jsonl`. That stream plus `git log` plus the issue tracker are the three signals this skill fuses to answer the three questions above. Without that fusion, "recall" is just history dumping; with it, the user can resume in one turn.
9
15
 
10
16
  ## When to use
11
17
 
@@ -19,18 +25,35 @@ Do not use:
19
25
  - To bypass the migration gate (it doesn't)
20
26
  - As a replacement for ADRs, CONTEXT.md, or issues (those are the durable channels)
21
27
 
28
+ ## Default action (zero-message invocation)
29
+
30
+ When `/where` is invoked with **no accompanying user text** (the SKILL block is the only thing in the user turn), the invocation itself is the request: *"지금 어디야, 최근 뭐했고, 다음은 뭐야?"*
31
+
32
+ Default behaviour, executed immediately and in the same turn:
33
+
34
+ 1. Run the full Process below with the **default relevance window** (last 3 sessions by mtime).
35
+ 2. Render the position card (the three sections: stage / recent / next).
36
+ 3. End with the next-action proposal as a question the user can confirm or correct.
37
+
38
+ Do **not** print preamble like "What would you like me to recall?" or "Please specify a date range." The user already chose the skill; the only legal opening move is to start producing the card. Ask for narrowing only if Step 1 finds zero session files **and** `git log` shows no recent commits.
39
+
22
40
  ## Process
23
41
 
42
+ Execute these steps as your **first action** after the skill loads. Do not narrate the plan, do not ask whether to proceed — run Step 1 immediately.
43
+
24
44
  ### 1. Resolve session directory
25
45
 
26
- ```
27
- cwd = $(pwd)
28
- encoded = cwd with every "/" replaced by "-", wrapped in double dashes:
29
- /Users/jason/pi/pi-mono → --Users-jason-pi-pi-mono--
30
- sessions_dir = ~/.pi/agent/sessions/$encoded
46
+ Run this bash, do not just describe it:
47
+
48
+ ```bash
49
+ cwd="$(pwd)"
50
+ encoded="--$(echo "$cwd" | sed 's|^/||; s|/|-|g')--"
51
+ sessions_dir="$HOME/.pi/agent/sessions/$encoded"
52
+ [ -d "$sessions_dir" ] || { echo "no prior pi sessions for this cwd: $cwd"; exit 0; }
53
+ ls -t "$sessions_dir"/*.jsonl 2>/dev/null | head -3
31
54
  ```
32
55
 
33
- If `sessions_dir` does not exist, exit cleanly: "no prior pi sessions for this cwd".
56
+ If the directory does not exist, print exactly `no prior pi sessions for this cwd` and stop — nothing else to do.
34
57
 
35
58
  ### 2. Pick relevance window
36
59
 
@@ -44,54 +67,64 @@ ls -t ~/.pi/agent/sessions/$encoded/*.jsonl | head -3
44
67
 
45
68
  For each candidate file, read just the **first 3 lines** to get session metadata (id, model, cwd, timestamp). Skip files older than the window.
46
69
 
47
- ### 4. Targeted extraction
70
+ ### 4. Targeted extraction (per session)
48
71
 
49
72
  Pull only these event kinds from each in-window jsonl:
50
73
 
51
- - `message` where `role=user` (intent)
52
- - `message` where `role=assistant` AND content includes one of: a heading, a final summary block, a list of changed files, a commit SHA, an issue URL
53
- - Tool calls of types: `Edit`, `Write`, `Bash` (filter by command keyword: `git commit|push|gh issue|gh pr`)
54
- - Any explicit handoff strings (`flow complete`, `Final summary`, `[flow] complete`)
74
+ - `message` where `message.role == "user"` (intent)
75
+ - `message` where `message.role == "assistant"` AND content includes one of: a heading, a final summary block, a list of changed files, a commit SHA, an issue URL
76
+ - pi tool blocks of `name in ("edit", "write")` and lower-case `name == "bash"` whose `input.command` matches `git commit|git push|gh issue|gh pr`
77
+ - Any explicit handoff strings (`chain complete`, `Final summary`, `flow complete`, `audit complete`)
55
78
 
56
- Implementation hint (jq):
79
+ Implementation hint (jq, pi format — messages are nested under `.message`):
57
80
 
58
81
  ```bash
59
82
  jq -c 'select(
60
- (.type == "message" and .role == "user") or
61
- (.type == "tool_use" and (.name == "Edit" or .name == "Write")) or
62
- (.type == "tool_use" and .name == "Bash" and (.input.command | test("git commit|git push|gh issue|gh pr"))) or
63
- (.type == "message" and .role == "assistant" and (.content | tostring | test("Final summary|flow complete|## Summary")))
83
+ (.type == "message" and .message.role == "user") or
84
+ (.type == "message" and .message.role == "assistant" and ((.message.content // []) | tostring | test("chain complete|audit complete|Final summary|## Summary|flow complete")))
64
85
  )' <file>
65
86
  ```
66
87
 
67
88
  If `jq` is unavailable or the file format does not match, fall back to `grep`-based filters on the raw file. Keep extraction under 200 lines per session.
68
89
 
69
- ### 5. Synthesise a recall card
90
+ ### 4b. Cross-reference live state
91
+
92
+ The session log alone says "what was discussed". To answer **stage** and **next** you also need what actually landed:
93
+
94
+ ```bash
95
+ git log --since="3 days ago" --pretty=format:"%h %ad %s" --date=short | head -10
96
+ git status -sb
97
+ # if GitHub is the tracker:
98
+ gh pr list --state open --limit 5 2>/dev/null
99
+ gh issue list --state open --limit 5 2>/dev/null
100
+ ```
101
+
102
+ Reconcile: a session that ended with a commit + merged PR → stage = shipped; a session that ended with `git status` dirty or an open PR → stage = in flight; a session whose last assistant turn proposed a follow-up command → that command *is* the next action.
103
+
104
+ ### 5. Synthesise a position card
70
105
 
71
- Output exactly this shape, no more:
106
+ Output exactly this three-section shape, no more. Each section answers one of the three questions.
72
107
 
73
108
  ```markdown
74
- ## Recall: <cwd>last <N> sessions
109
+ ## Where we are — <cwd>
75
110
 
76
- ### <YYYY-MM-DD HH:MM> — session <short-id>
77
- - **Intent**: <one line drawn from the first user message>
78
- - **Touched**: <comma-separated file paths from Edit/Write tools>
79
- - **Side effects**: <commit SHAs / issue URLs / PR URLs>
80
- - **State**: <complete | incomplete: <reason>>
111
+ ### Stage (어디까지 왔나)
112
+ <2–4 lines: current branch, last shipped version / merged PR, any open Release PR, any in-flight slice. One sentence per fact, no narration.>
81
113
 
82
- ### <YYYY-MM-DD HH:MM>session <short-id>
83
- ...
114
+ ### Recent (최근 뭐했나 last <N> sessions)
84
115
 
85
- ## Continuation hypothesis
116
+ - **<YYYY-MM-DD HH:MM> — <short-id>**: <intent in one line> → <files touched, condensed> → <side effects: commit SHA / PR / issue URL> — <complete | incomplete: <reason>>
117
+ - **<YYYY-MM-DD HH:MM> — <short-id>**: …
86
118
 
87
- <one paragraph: what was likely left undone, which files/issues to look at first>
119
+ ### Next (다음 할까)
120
+ <one paragraph: the single most plausible next action, named with the exact file / issue # / command that picks it up. If multiple candidates, rank them and pick #1; list #2–3 in one line.>
88
121
  ```
89
122
 
90
- Stop here. Do not start work. The hypothesis is the bridge to `/do`.
123
+ Stop here. Do not start work. The Next section is the bridge to `/do` — it states an action, not a menu.
91
124
 
92
125
  ### 6. Hand off
93
126
 
94
- If the user confirms the hypothesis, invoke `/do` with the resolved intent + scope. If the hypothesis is wrong, ask one clarifying question and try once more.
127
+ End with one short question: "이걸로 갈까?" (or English equivalent). If the user confirms the Next action, invoke `/do` with the resolved intent + scope. If the user picks a different candidate or corrects the framing, run Step 1 again with the narrower window and try once more.
95
128
 
96
129
  ## Privacy / safety
97
130