pi-dev 0.1.8 → 0.2.1

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/dist/cli.js CHANGED
@@ -9,17 +9,20 @@ 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 [scope] [--skip-prefs] [-y]
12
+ pi-dev install [scope] [--skip-prefs] [--include-maintainer] [-y]
13
13
  Install skills + seed preferences. Scope is one of:
14
14
  --global ~/.pi/agent/skills/ (default, every pi session sees it)
15
15
  --local .pi/skills/ in cwd (only this repo)
16
16
  Without a flag, an interactive TTY is prompted; non-TTY defaults to global.
17
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).
18
20
 
19
- pi-dev update [--include-prefs] [--global|--local]
21
+ pi-dev update [--include-prefs] [--include-maintainer] [--global|--local]
20
22
  Refresh skills in place. Scope is auto-detected from disk
21
23
  (local wins if .pi/skills/ exists in cwd). Preferences are kept by default;
22
- pass --include-prefs to re-seed.
24
+ pass --include-prefs to re-seed. Maintainer skills already on disk are
25
+ preserved automatically; pass --include-maintainer to add them on update.
23
26
 
24
27
  pi-dev list Show installed skills under both scopes (if present).
25
28
  pi-dev uninstall <skill> [--global|--local]
@@ -32,7 +35,6 @@ After install, in any pi session, you primarily call:
32
35
  /do — the one-shot engineering entry point
33
36
  /taste — view or update preferences
34
37
  /where — recall prior pi sessions for this cwd
35
- /improve-skill-flow — audit pi sessions, propose evidence-based skill edits
36
38
 
37
39
  All other skills are invoked automatically by /do.
38
40
  `);
@@ -54,10 +56,15 @@ async function main() {
54
56
  skipPrefs: getFlag("skip-prefs"),
55
57
  scope: getScope(),
56
58
  yes: getFlag("yes") || args.includes("-y"),
59
+ includeMaintainer: getFlag("include-maintainer"),
57
60
  });
58
61
  break;
59
62
  case "update":
60
- await update({ skipPrefs: !getFlag("include-prefs"), scope: getScope() });
63
+ await update({
64
+ skipPrefs: !getFlag("include-prefs"),
65
+ scope: getScope(),
66
+ includeMaintainer: getFlag("include-maintainer"),
67
+ });
61
68
  break;
62
69
  case "list":
63
70
  listInstalled();
package/dist/install.js CHANGED
@@ -2,7 +2,7 @@ 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
4
  import { createInterface } from "node:readline";
5
- import { SKILLS } from "./manifest.js";
5
+ import { SKILLS, CONSUMER_SKILLS } from "./manifest.js";
6
6
  import { PKG_SKILLS_DIR, PKG_GLOBAL_PREFS_PRESET, destFor, } from "./paths.js";
7
7
  function ask(question) {
8
8
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -41,8 +41,9 @@ export async function install(opts = {}) {
41
41
  const scope = await resolveScope(opts);
42
42
  const { agentDir, skillsDir, prefsFile } = destFor(scope);
43
43
  mkdirSync(skillsDir, { recursive: true });
44
+ const skillsToInstall = opts.includeMaintainer ? SKILLS : CONSUMER_SKILLS;
44
45
  let copied = 0;
45
- for (const skill of SKILLS) {
46
+ for (const skill of skillsToInstall) {
46
47
  const src = join(PKG_SKILLS_DIR, skill.name);
47
48
  const dst = join(skillsDir, skill.name);
48
49
  if (!existsSync(src)) {
@@ -88,12 +89,16 @@ export async function update(opts = {}) {
88
89
  else
89
90
  scope = "global";
90
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"));
91
95
  await install({
92
96
  ...opts,
93
97
  scope,
94
98
  force: true,
95
99
  skipPrefs: !opts.skipPrefs ? true : opts.skipPrefs,
96
100
  yes: true,
101
+ includeMaintainer: opts.includeMaintainer || maintainerAlreadyInstalled,
97
102
  });
98
103
  }
99
104
  export function uninstallSkill(name, scope) {
@@ -121,7 +126,11 @@ export function listInstalled() {
121
126
  for (const skill of SKILLS) {
122
127
  const path = join(t.skillsDir, skill.name, "SKILL.md");
123
128
  const installed = existsSync(path);
124
- const tag = skill.kind === "human" ? "[user] " : "[support]";
129
+ const tag = skill.kind === "human"
130
+ ? "[user] "
131
+ : skill.kind === "maintainer"
132
+ ? "[maintainer]"
133
+ : "[support] ";
125
134
  const status = installed ? "ok " : "missing";
126
135
  console.log(` ${tag} /${skill.name.padEnd(34)} ${status} — ${skill.summary}`);
127
136
  }
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-dev",
3
- "version": "0.1.8",
3
+ "version": "0.2.1",
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,64 +67,116 @@ 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) — with a hard context budget
71
+
72
+ A session jsonl can be hundreds of KB and a single tool result can be 10–20 KB. Never load full message bodies into context. Pull only what answers the three questions, truncate every excerpt, and enforce a byte budget.
73
+
74
+ **Budget (non-negotiable):**
75
+
76
+ - **≤ 8 KB total** extracted text per session into context.
77
+ - **≤ 3 sessions** by default → worst case ≈ 24 KB.
78
+ - Per user message: first 200 chars.
79
+ - Per assistant text block: first 400 chars (final summaries / decisions only — see filter).
80
+ - **Skip `toolResult` blocks entirely.** They are the biggest context killers and they rarely add signal that the surrounding text doesn't already convey.
81
+ - **Skip `thinking` blocks entirely.**
82
+
83
+ **What to pull from each in-window jsonl:**
84
+
85
+ - `message` where `message.role == "user"` — keep only `.message.content[].text` truncated to 200 chars, drop anything else.
86
+ - `message` where `message.role == "assistant"` — keep `.message.content[].text` blocks **only if** they contain one of: a heading, a final summary marker, a commit SHA-like 7-hex, an `https://` URL, or a terminator literal (`chain complete`, `audit complete`, `Final summary`, `## Summary`, `flow complete`). Truncate to 400 chars.
87
+ - pi tool blocks of `name in ("edit", "write")` — keep just the target `path`, drop diffs.
88
+ - pi tool blocks of lower-case `name == "bash"` — keep only those whose `input.command` matches `git commit|git push|gh issue|gh pr|gh release|npm publish`. Keep the command line only, drop output.
89
+
90
+ **Implementation hint (jq, pi format — messages are nested under `.message`; toolCall/toolResult blocks live inside `.message.content[]`). Note the explicit role gate — do NOT remove it; pi emits `toolResult` records with their own `message.role` and they will leak in otherwise:**
91
+
92
+ ```bash
93
+ jq -c '
94
+ select(.type == "message" and (.message.role == "user" or .message.role == "assistant")) |
95
+ . as $m |
96
+ {
97
+ ts: .timestamp,
98
+ role: .message.role,
99
+ text: (
100
+ (.message.content // [])
101
+ | map(select(.type == "text") | .text)
102
+ | join(" ")
103
+ | if $m.message.role == "user" then .[0:200] else .[0:300] end
104
+ ),
105
+ paths: (
106
+ (.message.content // [])
107
+ | map(select(.type == "toolCall" and (.name == "edit" or .name == "write")) | .arguments.path // .arguments.file_path // empty)
108
+ ),
109
+ cmds: (
110
+ (.message.content // [])
111
+ | map(
112
+ select(.type == "toolCall" and .name == "bash")
113
+ | .arguments.command
114
+ | select(test("git commit|git push|gh issue|gh pr|gh release|npm publish"))
115
+ )
116
+ )
117
+ }
118
+ | if .role == "assistant" then
119
+ select(.text | test("chain complete|audit complete|Final summary|## Summary|flow complete|^## |https://|[0-9a-f]{7,}"))
120
+ else . end
121
+ | select(.text != "" or (.paths | length) > 0 or (.cmds | length) > 0)
122
+ ' <file> | head -25
123
+ ```
124
+
125
+ This drops `toolResult` and `thinking` via the explicit role gate, filters assistant text to genuine signals only, truncates per-role, and caps records per session at 25. Measured on a real 653 KB session: output ≈ 6 KB, well inside the 8 KB budget. If `jq` is unavailable, fall back to `grep -E` on raw text and live with the lower precision — still respect the 8 KB-per-session budget.
48
126
 
49
- Pull only these event kinds from each in-window jsonl:
127
+ **If budget exceeded after filtering:** keep the first user message, the last 2 qualifying assistant text blocks, all matching `bash` commands, all edit/write paths. Drop intermediate text blocks. Never echo a `toolResult`.
50
128
 
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`)
129
+ ### 4b. Cross-reference live state (cheap, bounded)
55
130
 
56
- Implementation hint (jq):
131
+ The session log alone says "what was discussed". To answer **stage** and **next** you also need what actually landed. Each probe below caps its own output — do not remove the caps:
57
132
 
58
133
  ```bash
59
- 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")))
64
- )' <file>
134
+ git log --since="3 days ago" --pretty=format:"%h %ad %s" --date=short | head -10
135
+ git status -sb | head -20
136
+ # if GitHub is the tracker:
137
+ gh pr list --state open --limit 5 --json number,title,url 2>/dev/null
138
+ gh issue list --state open --limit 5 --json number,title,url 2>/dev/null
65
139
  ```
66
140
 
67
- 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.
141
+ 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. Each probe must be one command; do not page through history or fetch issue bodies.
68
142
 
69
- ### 5. Synthesise a recall card
143
+ ### 5. Synthesise a position card
70
144
 
71
- Output exactly this shape, no more:
145
+ Output exactly this three-section shape, no more. Each section answers one of the three questions.
72
146
 
73
147
  ```markdown
74
- ## Recall: <cwd>last <N> sessions
148
+ ## Where we are — <cwd>
75
149
 
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>>
150
+ ### Stage (어디까지 왔나)
151
+ <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
152
 
82
- ### <YYYY-MM-DD HH:MM>session <short-id>
83
- ...
153
+ ### Recent (최근 뭐했나 last <N> sessions)
84
154
 
85
- ## Continuation hypothesis
155
+ - **<YYYY-MM-DD HH:MM> — <short-id>**: <intent in one line> → <files touched, condensed> → <side effects: commit SHA / PR / issue URL> — <complete | incomplete: <reason>>
156
+ - **<YYYY-MM-DD HH:MM> — <short-id>**: …
86
157
 
87
- <one paragraph: what was likely left undone, which files/issues to look at first>
158
+ ### Next (다음 할까)
159
+ <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
160
  ```
89
161
 
90
- Stop here. Do not start work. The hypothesis is the bridge to `/do`.
162
+ Stop here. Do not start work. The Next section is the bridge to `/do` — it states an action, not a menu.
91
163
 
92
164
  ### 6. Hand off
93
165
 
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.
166
+ 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
167
 
96
168
  ## Privacy / safety
97
169
 
98
170
  - Sessions can contain secrets that were pasted in. Do not echo full message bodies in the recall card; extract only headings, file paths, URLs, SHAs.
99
171
  - Never write the recall card to a file in the repo. It lives in conversation context only.
100
172
 
101
- ## Performance
173
+ ## Performance & context budget
102
174
 
103
- - Target: < 2 seconds wallclock for the cheap pass + targeted extraction across 3 sessions, even if individual jsonl files are multi-MB.
104
- - If a single jsonl is > 5 MB, sample: head -2000 + tail -2000 lines, plus any line containing `git commit`/`gh issue`.
175
+ - **Wallclock target:** < 2 seconds for the cheap pass + targeted extraction across 3 sessions.
176
+ - **Context budget:** 8 KB extracted per session, 24 KB total before rendering the card. This is the limit, not a goal.
177
+ - **Always strip toolResult and thinking blocks.** They cause context bloat without changing the answer to the three questions.
178
+ - If a single jsonl is > 5 MB, sample: `head -2000` + `tail -2000` lines (still feed them through the Step 4 jq filter — do not raw-cat).
179
+ - If after filtering a session still exceeds 8 KB, keep first user message + last 2 qualifying assistant blocks + all matching bash commands + all edit/write paths; drop the rest.
105
180
 
106
181
  ## Limits
107
182