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 +19 -6
- package/dist/cli.js +84 -50
- package/dist/install.js +117 -40
- package/dist/manifest.js +6 -1
- package/dist/paths.js +12 -0
- package/package.json +1 -1
- package/skills/improve-skill-flow/SKILL.md +67 -67
- package/skills/where/SKILL.md +66 -33
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
|
-
#
|
|
45
|
+
# Interactive: pick global vs project-local, then install + seed preferences
|
|
46
46
|
npx pi-dev@latest install
|
|
47
47
|
|
|
48
|
-
#
|
|
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
|
|
58
|
+
# Verify the active scope's layout
|
|
55
59
|
npx pi-dev doctor
|
|
56
60
|
```
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
|
|
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]
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
24
|
-
/taste
|
|
25
|
-
/where
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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 {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
46
|
+
for (const skill of skillsToInstall) {
|
|
10
47
|
const src = join(PKG_SKILLS_DIR, skill.name);
|
|
11
|
-
const dst = join(
|
|
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 ${
|
|
61
|
+
console.log(`Installed ${copied} skill(s) into ${skillsDir} [${scope}]`);
|
|
27
62
|
if (opts.skipPrefs) {
|
|
28
|
-
console.log("Skipped
|
|
63
|
+
console.log("Skipped preferences (pass --include-prefs on update to merge in new keys).");
|
|
29
64
|
return;
|
|
30
65
|
}
|
|
31
|
-
if (!existsSync(
|
|
66
|
+
if (!existsSync(prefsFile)) {
|
|
32
67
|
if (existsSync(PKG_GLOBAL_PREFS_PRESET)) {
|
|
33
|
-
copyFileSync(PKG_GLOBAL_PREFS_PRESET,
|
|
34
|
-
console.log(`Seeded
|
|
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
|
|
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
|
|
43
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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(
|
|
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(
|
|
80
|
-
issues.push(`
|
|
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
|
-
|
|
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,48 +1,71 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: improve-skill-flow
|
|
3
|
-
description: Analyse real pi-runtime session telemetry from
|
|
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
|
-
|
|
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
|
|
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
|
-
- **
|
|
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
|
-
- **
|
|
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
|
-
##
|
|
55
|
+
## Fix scopes
|
|
33
56
|
|
|
34
|
-
pi-runtime today loads skill bodies from a single location
|
|
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
|
-
| **
|
|
39
|
-
| **
|
|
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 `
|
|
44
|
-
- A `
|
|
45
|
-
- A single audit may produce a mix of
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 `
|
|
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
|
|
194
|
-
| - | ---------------------------------------- |
|
|
195
|
-
| 1 | /do hands flow back between phases |
|
|
196
|
-
| 2 | docs/handoff/ resurrected after marker |
|
|
197
|
-
| 3 | retro-action-item label still alive |
|
|
198
|
-
| 4 | smoke command name changed in S058 |
|
|
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
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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 (
|
|
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.
|
|
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
|
-
**
|
|
253
|
+
**7b. Consumer-prefs branch** — only if any finding was approved as `consumer-prefs`:
|
|
245
254
|
|
|
246
|
-
1.
|
|
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
|
|
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 (`
|
|
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
|
|
270
|
-
4. For any landed change: if `
|
|
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 —
|
|
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
|
|
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.
|
package/skills/where/SKILL.md
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: where
|
|
3
|
-
description:
|
|
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 —
|
|
6
|
+
# /where — Where Are We?
|
|
7
7
|
|
|
8
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
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
|
|
52
|
-
- `message` where `role
|
|
53
|
-
-
|
|
54
|
-
- Any explicit handoff strings (`
|
|
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 == "
|
|
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
|
-
###
|
|
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
|
-
##
|
|
109
|
+
## Where we are — <cwd>
|
|
75
110
|
|
|
76
|
-
###
|
|
77
|
-
|
|
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
|
-
###
|
|
83
|
-
...
|
|
114
|
+
### Recent (최근 뭐했나 — last <N> sessions)
|
|
84
115
|
|
|
85
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|