@a-canary/pi-upskill 1.0.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 ADDED
@@ -0,0 +1,143 @@
1
+ # pi-upskill
2
+
3
+ Learn from failures. Reduce token waste. Improve automatically.
4
+
5
+ ## Overview
6
+
7
+ pi-upskill tracks corrections (failures → fixes) and generates skills/rules to prevent future mistakes.
8
+
9
+ **Core flow:**
10
+ 1. Log corrections during conversation (`upskill-log` tool)
11
+ 2. Backfill from past sessions (`/upskill-backfill`)
12
+ 3. At threshold, analyze and generate ONE high-impact edit (`/upskill-analyze`)
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ # From git (recommended)
18
+ pi install git:github.com/a-canary/pi-upskill
19
+
20
+ # Or from npm after publishing
21
+ pi install npm:pi-upskill
22
+
23
+ # Try without installing
24
+ pi -e git:github.com/a-canary/pi-upskill
25
+ ```
26
+
27
+ After install, reload: `/reload`
28
+
29
+ ## Usage
30
+
31
+ ### During conversation: log corrections
32
+
33
+ The agent uses the `upskill-log` tool:
34
+
35
+ ```
36
+ Agent: [uses upskill-log tool]
37
+ failure: "Committed without running tests"
38
+ correction: "Always run tests before commit"
39
+ strength: "strong"
40
+ tokens_wasted: 3000
41
+
42
+ Result: Logged correction #5 to .pi/corrections.jsonl
43
+ Progress: 5/20 corrections
44
+ ```
45
+
46
+ **Strength levels:**
47
+ - `strong` — User said "always/never/remember" → single occurrence = skill
48
+ - `pattern` — Self-correction or repeated issue → needs 3x occurrences
49
+
50
+ ### One-time: scan past sessions
51
+
52
+ ```
53
+ /upskill-backfill
54
+ ```
55
+
56
+ Scans session files from pi, claude, opencode, codex. Extracts corrections for review.
57
+
58
+ ### At threshold: analyze and improve
59
+
60
+ ```
61
+ /upskill-analyze
62
+ ```
63
+
64
+ When 20+ corrections logged, triggers background analysis:
65
+ 1. LLM reviews all corrections
66
+ 2. Selects ONE edit for maximum token impact
67
+ 3. Applies surgical edit (skill/AGENTS.md/MEMORY.md)
68
+ 4. Removes addressed corrections
69
+
70
+ ### Check progress
71
+
72
+ ```
73
+ /upskill-status
74
+ ```
75
+
76
+ Shows: count, threshold, strong vs pattern, total tokens wasted.
77
+
78
+ ## Data Format
79
+
80
+ `.pi/corrections.jsonl` — one JSON object per line:
81
+
82
+ ```json
83
+ {"timestamp":"2025-03-13T01:30:00Z","failure":"Committed without tests","correction":"Always run tests first","context":"User reminder after broken CI","tokens_wasted":3000,"source":"user","strength":"strong"}
84
+ ```
85
+
86
+ **Required fields (max 30 words each):**
87
+ - `timestamp` — ISO 8601
88
+ - `failure` — What went wrong
89
+ - `correction` — How to fix / what to do instead
90
+ - `source` — "user" or "self"
91
+ - `strength` — "strong" or "pattern"
92
+
93
+ **Optional:**
94
+ - `context` — Relevant context
95
+ - `tokens_wasted` — Estimated tokens
96
+
97
+ ## Configuration
98
+
99
+ `.pi/settings.json`:
100
+
101
+ ```json
102
+ {
103
+ "upskill": {
104
+ "threshold": 20,
105
+ "autoAnalyze": false,
106
+ "lookbackDays": 7
107
+ }
108
+ }
109
+ ```
110
+
111
+ ## Architecture
112
+
113
+ ```
114
+ ~/pi-upskill/
115
+ ├── CHOICES.md # Decision record
116
+ ├── PLAN.md # Implementation phases
117
+ ├── README.md # This file
118
+ ├── extension/
119
+ │ └── index.ts # upskill-log tool, commands
120
+ └── skills/
121
+ ├── analyze/SKILL.md # Pattern analysis workflow
122
+ └── backfill/SKILL.md # Historical scan workflow
123
+ ```
124
+
125
+ **Hybrid interface:**
126
+ - Extension provides `upskill-log` tool (inline during conversation)
127
+ - Skills provide `/upskill-backfill` and `/upskill-analyze` commands
128
+
129
+ ## Key Decisions
130
+
131
+ See [CHOICES.md](CHOICES.md) for full decision record.
132
+
133
+ | ID | Decision |
134
+ |----|----------|
135
+ | UX-0001 | User corrections with "always/never/remember" → immediate skill |
136
+ | UX-0002 | Self-corrections → need 3x pattern before skill |
137
+ | F-0003 | At 20 corrections → background analysis, ONE edit for max impact |
138
+ | D-0003 | Processed corrections removed after edit applied |
139
+
140
+ ## Inspiration
141
+
142
+ - [upskill.md](https://github.com/claude-admin/cc-plugins) — Pattern extraction from memory
143
+ - [pi-reflect](https://github.com/jo-inc/pi-reflect) — Iterative self-improvement
@@ -0,0 +1,308 @@
1
+ /**
2
+ * pi-upskill — Learn from failures, reduce token waste
3
+ *
4
+ * Tools:
5
+ * upskill-log — Log a correction during conversation
6
+ *
7
+ * Commands:
8
+ * /upskill-status — Show corrections count and threshold progress
9
+ * /upskill-analyze — Trigger pattern analysis (runs in background)
10
+ *
11
+ * Configuration (.pi/settings.json):
12
+ * {
13
+ * "upskill": {
14
+ * "threshold": 20,
15
+ * "autoAnalyze": false
16
+ * }
17
+ * }
18
+ */
19
+
20
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
21
+ import { Type } from "@sinclair/typebox";
22
+ import * as fs from "node:fs";
23
+ import * as path from "node:path";
24
+ import { spawn } from "node:child_process";
25
+
26
+ // ── Types ────────────────────────────────────────
27
+
28
+ interface Correction {
29
+ timestamp: string;
30
+ failure: string;
31
+ correction: string;
32
+ context?: string;
33
+ tokens_wasted?: number;
34
+ source: "user" | "self";
35
+ strength: "strong" | "pattern";
36
+ }
37
+
38
+ interface UpskillSettings {
39
+ threshold: number;
40
+ autoAnalyze: boolean;
41
+ }
42
+
43
+ const DEFAULT_SETTINGS: UpskillSettings = {
44
+ threshold: 20,
45
+ autoAnalyze: false,
46
+ };
47
+
48
+ // ── Helpers ──────────────────────────────────────
49
+
50
+ function getCorrectionsPath(cwd: string): string {
51
+ return path.join(cwd, ".pi", "corrections.jsonl");
52
+ }
53
+
54
+ function loadCorrections(filepath: string): Correction[] {
55
+ if (!fs.existsSync(filepath)) return [];
56
+ const content = fs.readFileSync(filepath, "utf-8");
57
+ return content
58
+ .trim()
59
+ .split("\n")
60
+ .filter((line) => line.trim())
61
+ .map((line) => JSON.parse(line));
62
+ }
63
+
64
+ function appendCorrection(filepath: string, correction: Correction): void {
65
+ const dir = path.dirname(filepath);
66
+ if (!fs.existsSync(dir)) {
67
+ fs.mkdirSync(dir, { recursive: true });
68
+ }
69
+ fs.appendFileSync(filepath, JSON.stringify(correction) + "\n", "utf-8");
70
+ }
71
+
72
+ function getSettings(ctx: any): UpskillSettings {
73
+ const projectSettings = ctx.projectSettings?.upskill || {};
74
+ return { ...DEFAULT_SETTINGS, ...projectSettings };
75
+ }
76
+
77
+ function countWords(text: string): number {
78
+ return text.trim().split(/\s+/).filter(Boolean).length;
79
+ }
80
+
81
+ // ── Extension ────────────────────────────────────
82
+
83
+ export default function (pi: ExtensionAPI) {
84
+ // ── upskill-log Tool ───────────────────────────
85
+
86
+ pi.registerTool({
87
+ name: "upskill-log",
88
+ label: "Log Correction",
89
+ description: `Log a failure → correction to .pi/corrections.jsonl. Use when:
90
+ 1. User corrects you with "always", "never", or "remember" (strength: strong)
91
+ 2. You self-correct after multiple failed attempts (strength: pattern, needs 3x)
92
+
93
+ After logging, check if threshold reached (default 20 entries). If so, suggest running /upskill-analyze.
94
+
95
+ Each field must be 30 words or less. Estimate tokens_wasted based on conversation length from mistake to correction.`,
96
+
97
+ parameters: Type.Object({
98
+ failure: Type.String({
99
+ description: "What went wrong (max 30 words)",
100
+ maxLength: 300,
101
+ }),
102
+ correction: Type.String({
103
+ description: "How it was fixed / what to do instead (max 30 words)",
104
+ maxLength: 300,
105
+ }),
106
+ context: Type.Optional(
107
+ Type.String({
108
+ description: "Relevant context (max 30 words)",
109
+ maxLength: 300,
110
+ }),
111
+ ),
112
+ tokens_wasted: Type.Optional(
113
+ Type.Number({
114
+ description: "Estimated tokens wasted (in + out) from mistake to correction",
115
+ }),
116
+ ),
117
+ strength: Type.Optional(
118
+ Type.String({
119
+ description: "strong = always/never/remember (single occurrence sufficient), pattern = needs 3x",
120
+ enum: ["strong", "pattern"],
121
+ }),
122
+ ),
123
+ }),
124
+
125
+ async execute(toolCallId, params, _signal, _onUpdate, ctx) {
126
+ const { failure, correction, context, tokens_wasted, strength = "pattern" } = params;
127
+
128
+ // Validate word counts
129
+ const failureWords = countWords(failure);
130
+ const correctionWords = countWords(correction);
131
+ const contextWords = context ? countWords(context) : 0;
132
+
133
+ if (failureWords > 30 || correctionWords > 30 || contextWords > 30) {
134
+ return {
135
+ content: [
136
+ {
137
+ type: "text",
138
+ text: `Error: Fields must be 30 words or less. Got: failure=${failureWords}, correction=${correctionWords}, context=${contextWords}`,
139
+ },
140
+ ],
141
+ isError: true,
142
+ };
143
+ }
144
+
145
+ const entry: Correction = {
146
+ timestamp: new Date().toISOString(),
147
+ failure,
148
+ correction,
149
+ context,
150
+ tokens_wasted,
151
+ source: "user", // Could be inferred from context
152
+ strength: strength as "strong" | "pattern",
153
+ };
154
+
155
+ const correctionsPath = getCorrectionsPath(ctx.cwd);
156
+ appendCorrection(correctionsPath, entry);
157
+
158
+ const corrections = loadCorrections(correctionsPath);
159
+ const settings = getSettings(ctx);
160
+ const count = corrections.length;
161
+
162
+ let message = `Logged correction #${count} to .pi/corrections.jsonl`;
163
+
164
+ if (count >= settings.threshold) {
165
+ message += `\n\n**Threshold reached!** (${count}/${settings.threshold})\nRun /upskill-analyze to generate skills from patterns.`;
166
+ } else {
167
+ message += `\n\nProgress: ${count}/${settings.threshold} corrections`;
168
+ }
169
+
170
+ return {
171
+ content: [{ type: "text", text: message }],
172
+ details: { count, threshold: settings.threshold },
173
+ };
174
+ },
175
+
176
+ renderCall(args, theme) {
177
+ const strength = args.strength || "pattern";
178
+ const strengthColor = strength === "strong" ? "warning" : "muted";
179
+ return theme.fg("toolTitle", "upskill-log ") + theme.fg(strengthColor, `[${strength}]`);
180
+ },
181
+
182
+ renderResult(result, _options, theme) {
183
+ const details = result.details as { count: number; threshold: number } | undefined;
184
+ if (!details) {
185
+ const text = result.content[0];
186
+ return theme.fg("success", text?.type === "text" ? text.text : "Logged");
187
+ }
188
+ const pct = Math.round((details.count / details.threshold) * 100);
189
+ const bar = "█".repeat(Math.min(10, Math.floor(pct / 10))) + "░".repeat(10 - Math.min(10, Math.floor(pct / 10)));
190
+ return theme.fg("success", `✓ Logged #${details.count} `) + theme.fg("dim", `[${bar}] ${details.count}/${details.threshold}`);
191
+ },
192
+ });
193
+
194
+ // ── /upskill-status Command ───────────────────
195
+
196
+ pi.registerCommand("upskill-status", {
197
+ description: "Show corrections count and threshold progress",
198
+ handler: async (_args, ctx) => {
199
+ const correctionsPath = getCorrectionsPath(ctx.cwd);
200
+ const corrections = loadCorrections(correctionsPath);
201
+ const settings = getSettings(ctx);
202
+ const count = corrections.length;
203
+
204
+ const strong = corrections.filter((c) => c.strength === "strong").length;
205
+ const pattern = corrections.filter((c) => c.strength === "pattern").length;
206
+ const totalTokens = corrections.reduce((sum, c) => sum + (c.tokens_wasted || 0), 0);
207
+
208
+ const status =
209
+ count >= settings.threshold
210
+ ? "🟢 Ready to analyze"
211
+ : `🟡 ${settings.threshold - count} more needed`;
212
+
213
+ ctx.ui.notify(
214
+ `**Upskill Status**\n\n` +
215
+ `Corrections: ${count}/${settings.threshold}\n` +
216
+ `Strong: ${strong} | Pattern: ${pattern}\n` +
217
+ `Tokens wasted (est): ${totalTokens.toLocaleString()}\n` +
218
+ `Status: ${status}\n\n` +
219
+ `File: ${correctionsPath}`,
220
+ "info",
221
+ );
222
+ },
223
+ });
224
+
225
+ // ── /upskill-analyze Command ──────────────────
226
+
227
+ pi.registerCommand("upskill-analyze", {
228
+ description: "Analyze corrections and generate skills (runs in background)",
229
+ handler: async (_args, ctx) => {
230
+ const correctionsPath = getCorrectionsPath(ctx.cwd);
231
+ const corrections = loadCorrections(correctionsPath);
232
+
233
+ if (corrections.length === 0) {
234
+ ctx.ui.notify("No corrections logged. Use upskill-log tool or /upskill-backfill first.", "warning");
235
+ return;
236
+ }
237
+
238
+ const settings = getSettings(ctx);
239
+
240
+ if (corrections.length < settings.threshold) {
241
+ const proceed = await ctx.ui.confirm(
242
+ "Below threshold",
243
+ `Only ${corrections.length} corrections (threshold: ${settings.threshold}). Analyze anyway?`,
244
+ );
245
+ if (!proceed) return;
246
+ }
247
+
248
+ // Read the analyze skill prompt
249
+ const skillPath = path.join(path.dirname(new URL(import.meta.url).pathname), "..", "skills", "analyze", "SKILL.md");
250
+
251
+ let analyzePrompt = "";
252
+ try {
253
+ const skillContent = fs.readFileSync(skillPath, "utf-8");
254
+ // Extract content after frontmatter
255
+ const match = skillContent.match(/^---\n[\s\S]*?\n---\n([\s\S]*)$/);
256
+ if (match) analyzePrompt = match[1].trim();
257
+ } catch {
258
+ analyzePrompt = "Analyze the corrections and propose one high-impact edit.";
259
+ }
260
+
261
+ // Build the prompt with corrections
262
+ const correctionsBlock = corrections.map((c, i) => {
263
+ const strength = c.strength === "strong" ? "⬤" : "○";
264
+ const tokens = c.tokens_wasted ? ` [${c.tokens_wasted} tokens]` : "";
265
+ return `${strength} [${i + 1}] ${c.failure} → ${c.correction}${tokens}\n Context: ${c.context || "none"}`;
266
+ });
267
+
268
+ const fullPrompt =
269
+ `${analyzePrompt}\n\n` +
270
+ `## Corrections to Analyze (${corrections.length} entries)\n\n` +
271
+ `Legend: ⬤ = strong (single occurrence), ○ = pattern (needs 3x)\n\n` +
272
+ correctionsBlock.join("\n") +
273
+ `\n\n## Instructions\n\n` +
274
+ `1. Identify patterns in the corrections\n` +
275
+ `2. Select ONE edit with the largest impact on token usage\n` +
276
+ `3. Apply the surgical edit to the appropriate file\n` +
277
+ `4. Report what was changed and which corrections it addresses\n` +
278
+ `5. DO NOT remove the corrections file — user will review`;
279
+
280
+ // Spawn background process
281
+ const logPath = path.join(ctx.cwd, ".pi", "upskill-analysis.log");
282
+ const args = [
283
+ "-p",
284
+ "--no-session",
285
+ "--model",
286
+ ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "anthropic/claude-sonnet-4-5",
287
+ fullPrompt,
288
+ ];
289
+
290
+ ctx.ui.notify(`Spawning background analysis...\nLog: ${logPath}`, "info");
291
+
292
+ const proc = spawn("pi", args, {
293
+ detached: true,
294
+ stdio: ["ignore", fs.openSync(logPath, "w"), fs.openSync(logPath, "a")],
295
+ });
296
+
297
+ proc.unref();
298
+
299
+ ctx.ui.notify(
300
+ `Background analysis started.\n` +
301
+ `- Corrections: ${corrections.length}\n` +
302
+ `- Log: ${logPath}\n\n` +
303
+ `Check log for results. Use /upskill-status to see progress.`,
304
+ "info",
305
+ );
306
+ },
307
+ });
308
+ }
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@a-canary/pi-upskill",
3
+ "version": "1.0.0",
4
+ "description": "Learn from failures, reduce token waste, improve automatically",
5
+ "keywords": ["pi-package"],
6
+ "author": "a-canary",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/a-canary/pi-upskill.git"
11
+ },
12
+ "peerDependencies": {
13
+ "@mariozechner/pi-coding-agent": "*",
14
+ "@mariozechner/pi-tui": "*",
15
+ "@sinclair/typebox": "*"
16
+ },
17
+ "pi": {
18
+ "extensions": ["./extension"],
19
+ "skills": ["./skills"]
20
+ }
21
+ }
@@ -0,0 +1,187 @@
1
+ ---
2
+ name: analyze
3
+ description: Analyze logged corrections and generate one high-impact skill or rule edit. Use when threshold reached or user requests improvement.
4
+ ---
5
+
6
+ # /upskill-analyze — Pattern Analysis & Skill Generation
7
+
8
+ Review corrections, find patterns, apply ONE surgical edit for maximum token savings.
9
+
10
+ ## When to Use
11
+
12
+ - Threshold reached (default: 20 corrections)
13
+ - User explicitly requests analysis
14
+ - After backfill to process historical corrections
15
+
16
+ ## Process
17
+
18
+ ### Step 1: Read Corrections
19
+
20
+ Load all entries from `.pi/corrections.jsonl`:
21
+
22
+ ```bash
23
+ cat .pi/corrections.jsonl
24
+ ```
25
+
26
+ ### Step 2: Identify Patterns
27
+
28
+ Group corrections by:
29
+
30
+ **Type:**
31
+ - Same failure, same correction → strong pattern
32
+ - Same failure, different correction → needs synthesis
33
+ - Related failures → cluster by keyword/theme
34
+
35
+ **Priority:**
36
+ 1. `strength: "strong"` entries — single occurrence sufficient
37
+ 2. High `tokens_wasted` — prioritize for impact
38
+ 3. Frequency — 3+ pattern entries = solid pattern
39
+
40
+ ### Step 3: Select ONE Edit
41
+
42
+ Ask: "Which single edit would have the largest impact on token usage?"
43
+
44
+ Consider:
45
+ - How many corrections would this prevent?
46
+ - What's the total tokens wasted across those corrections?
47
+ - Is this a new skill or an edit to existing file?
48
+
49
+ **Edit targets (in order):**
50
+ 1. `.pi/skills/` — Add new skill for workflow/pattern
51
+ 2. `AGENTS.md` — Strengthen or add rule
52
+ 3. `MEMORY.md` — Add durable fact
53
+ 4. Existing skill — Strengthen wording
54
+
55
+ ### Step 4: Generate Edit
56
+
57
+ For new skills, create `.pi/skills/{name}/SKILL.md`:
58
+
59
+ ```markdown
60
+ ---
61
+ name: {name}
62
+ description: {when to use}
63
+ ---
64
+
65
+ # /{name} — {purpose}
66
+
67
+ ## Pattern Source
68
+
69
+ {why this skill exists}
70
+
71
+ ## Process
72
+
73
+ 1. {step}
74
+ 2. {step}
75
+
76
+ ## Verification
77
+
78
+ {testable gate}
79
+ ```
80
+
81
+ For existing files, propose surgical edit:
82
+ - Add bullet point to existing section
83
+ - Strengthen wording with "ALWAYS" / "NEVER"
84
+ - Add concrete example
85
+
86
+ ### Step 5: Apply Edit
87
+
88
+ Use `edit` tool to apply the change. Record:
89
+ - File edited
90
+ - Lines changed
91
+ - Corrections addressed
92
+
93
+ ### Step 6: Clean Up Corrections
94
+
95
+ Remove the corrections that contributed to this edit:
96
+
97
+ ```bash
98
+ # Keep only unprocessed corrections
99
+ # (those not matching the pattern we just addressed)
100
+ ```
101
+
102
+ Leave a marker comment for traceability:
103
+
104
+ ```json
105
+ {"timestamp":"2025-03-13T02:00:00Z","event":"upskill","action":"added_skill","name":"run-tests-first","corrections_addressed":3,"tokens_saved":9000}
106
+ ```
107
+
108
+ ### Step 7: Report Results
109
+
110
+ ```
111
+ Analysis complete.
112
+
113
+ **Edit Applied:**
114
+ - File: .pi/skills/run-tests-first/SKILL.md (created)
115
+ - Action: Added new skill
116
+
117
+ **Impact:**
118
+ - Corrections addressed: 3
119
+ - Tokens to save: ~9,000 per occurrence
120
+
121
+ **Removed from corrections.jsonl:**
122
+ - #4 "Committed without tests"
123
+ - #7 "Forgot to run tests"
124
+ - #12 "Skipped test step"
125
+
126
+ Remaining: 17 corrections
127
+ ```
128
+
129
+ ## Example Analysis Prompt
130
+
131
+ ```
132
+ You are analyzing logged corrections to improve agent behavior.
133
+
134
+ ## Corrections (12 entries)
135
+
136
+ ⬤ [1] Committed without running tests → Always run tests first
137
+ Context: User reminder after broken CI
138
+ [3000 tokens]
139
+
140
+ ○ [2] Used deprecated API → Check docs for current method
141
+ Context: Self-corrected after error
142
+ [1500 tokens]
143
+
144
+ ○ [5] Used deprecated API → Check docs for current method
145
+ Context: Same pattern as #2
146
+ [2000 tokens]
147
+
148
+ ...
149
+
150
+ Legend: ⬤ = strong (single = skill), ○ = pattern (needs 3x)
151
+
152
+ ## Task
153
+
154
+ 1. Find patterns in these corrections
155
+ 2. Select ONE edit with largest token impact
156
+ 3. Apply surgical edit to appropriate file
157
+ 4. Report what was changed
158
+ 5. List which corrections this addresses
159
+
160
+ Constraints:
161
+ - Only ONE edit
162
+ - Maximum impact on future token usage
163
+ - Prefer adding skills for workflows
164
+ - Prefer strengthening AGENTS.md for behavioral rules
165
+ ```
166
+
167
+ ## Verification
168
+
169
+ After edit:
170
+ ```bash
171
+ # Skill exists and loads
172
+ pi --skill .pi/skills/{name} -p "help"
173
+
174
+ # Corrections reduced
175
+ cat .pi/corrections.jsonl | wc -l
176
+ ```
177
+
178
+ ## Configuration
179
+
180
+ ```json
181
+ {
182
+ "upskill": {
183
+ "maxEditsPerAnalysis": 1,
184
+ "removeAddressedCorrections": true
185
+ }
186
+ }
187
+ ```
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: backfill
3
+ description: Scan past sessions and extract corrections to .pi/corrections.jsonl. Use for one-time historical analysis of failures.
4
+ ---
5
+
6
+ # /upskill-backfill — Historical Correction Extraction
7
+
8
+ Scan past conversation sessions to identify and log failures → corrections.
9
+
10
+ ## When to Use
11
+
12
+ - First-time setup: extract patterns from past sessions
13
+ - After realizing a pattern of mistakes
14
+ - User requests historical analysis
15
+
16
+ ## Process
17
+
18
+ ### Step 1: Locate Session Files
19
+
20
+ Search for session files in common locations:
21
+
22
+ ```bash
23
+ # Pi sessions
24
+ ls ~/.pi/agent/sessions/**/*.jsonl
25
+
26
+ # Claude sessions (if available)
27
+ ls ~/.claude/sessions/**/*.jsonl 2>/dev/null
28
+
29
+ # OpenCode sessions
30
+ ls ~/.opencode/sessions/**/* 2>/dev/null
31
+
32
+ # Codex sessions
33
+ ls ~/.codex/sessions/**/* 2>/dev/null
34
+ ```
35
+
36
+ ### Step 2: Filter by Date
37
+
38
+ Default lookback: 7 days. Ask user to confirm or adjust.
39
+
40
+ ```bash
41
+ # Find files from last N days
42
+ find ~/.pi/agent/sessions -name "*.jsonl" -mtime -7
43
+ ```
44
+
45
+ ### Step 3: Extract Corrections
46
+
47
+ For each session file, look for:
48
+
49
+ **Explicit user corrections:**
50
+ - "no", "not that", "wrong", "actually", "I meant"
51
+ - "stop", "don't", "never", "always"
52
+ - "remember", "make sure to", "from now on"
53
+
54
+ **Strong signals (always/never/remember):**
55
+ - Mark as `strength: "strong"` — single occurrence sufficient for skill
56
+
57
+ **Self-corrections:**
58
+ - Multiple tool calls attempting the same thing
59
+ - Agent says "let me try again" or "that didn't work"
60
+ - Mark as `strength: "pattern"` — needs 3x for skill
61
+
62
+ ### Step 4: Estimate Token Waste
63
+
64
+ For each correction:
65
+ 1. Find where the mistake started
66
+ 2. Find where correction was applied
67
+ 3. Count messages/turns between
68
+ 4. Rough estimate: ~500-2000 tokens per turn (varies by model)
69
+
70
+ ### Step 5: Present Candidates
71
+
72
+ Show extracted corrections for review:
73
+
74
+ ```
75
+ Found 12 potential corrections:
76
+
77
+ [1] STRONG: "Always run tests before committing"
78
+ Context: User reminded after broken commit
79
+ Tokens: ~3000
80
+
81
+ [2] PATTERN: "Don't use deprecated API"
82
+ Context: Self-corrected after error
83
+ Tokens: ~1500
84
+
85
+ ...
86
+
87
+ Review? [y/n/select]
88
+ ```
89
+
90
+ ### Step 6: Log Approved Corrections
91
+
92
+ For each approved correction, use the `upskill-log` tool or write directly:
93
+
94
+ ```json
95
+ {"timestamp":"2025-03-13T00:00:00Z","failure":"Committed without tests","correction":"Always run tests first","context":"User reminder after broken CI","tokens_wasted":3000,"source":"user","strength":"strong"}
96
+ ```
97
+
98
+ ### Step 7: Report Summary
99
+
100
+ ```
101
+ Backfill complete:
102
+ - Sessions scanned: 47
103
+ - Corrections found: 12
104
+ - Logged: 8 (4 skipped)
105
+ - Total tokens wasted: ~15,000
106
+
107
+ Run /upskill-status to see progress.
108
+ ```
109
+
110
+ ## Output
111
+
112
+ Appends to `.pi/corrections.jsonl`.
113
+
114
+ ## Verification
115
+
116
+ ```bash
117
+ cat .pi/corrections.jsonl | wc -l
118
+ ```
119
+
120
+ ## Session Parsing Details
121
+
122
+ ### Pi JSONL Format
123
+
124
+ Each line is a JSON object. Look for:
125
+
126
+ ```json
127
+ {"type":"message","message":{"role":"user","content":[{"type":"text","text":"no, that's wrong"}]}}
128
+ {"type":"message","message":{"role":"assistant","content":[{"type":"text","text":"Let me try again..."}]}}
129
+ ```
130
+
131
+ User messages with corrections → extract failure/correction pair.
132
+
133
+ ### Other Formats
134
+
135
+ Claude/OpenCode/Codex: Parse similarly, looking for user correction signals and agent self-corrections.
136
+
137
+ ## Options
138
+
139
+ User can specify:
140
+ - `--lookback N` — Days to look back (default: 7)
141
+ - `--source pi|claude|all` — Which session sources to scan
142
+ - `--auto` — Skip review, log all found corrections