mitsupi 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/LICENSE +201 -0
- package/README.md +95 -0
- package/TODO.md +11 -0
- package/commands/handoff.md +100 -0
- package/commands/make-release.md +75 -0
- package/commands/pickup.md +30 -0
- package/commands/update-changelog.md +78 -0
- package/package.json +22 -0
- package/pi-extensions/answer.ts +527 -0
- package/pi-extensions/codex-tuning.ts +632 -0
- package/pi-extensions/commit.ts +248 -0
- package/pi-extensions/cwd-history.ts +237 -0
- package/pi-extensions/issues.ts +548 -0
- package/pi-extensions/loop.ts +446 -0
- package/pi-extensions/qna.ts +167 -0
- package/pi-extensions/reveal.ts +689 -0
- package/pi-extensions/review.ts +807 -0
- package/pi-themes/armin.json +81 -0
- package/pi-themes/nightowl.json +82 -0
- package/skills/anachb/SKILL.md +183 -0
- package/skills/anachb/departures.sh +79 -0
- package/skills/anachb/disruptions.sh +53 -0
- package/skills/anachb/route.sh +87 -0
- package/skills/anachb/search.sh +43 -0
- package/skills/ghidra/SKILL.md +254 -0
- package/skills/ghidra/scripts/find-ghidra.sh +54 -0
- package/skills/ghidra/scripts/ghidra-analyze.sh +239 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportAll.java +278 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportCalls.java +148 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportDecompiled.java +84 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportFunctions.java +114 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportStrings.java +123 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportSymbols.java +135 -0
- package/skills/github/SKILL.md +47 -0
- package/skills/improve-skill/SKILL.md +155 -0
- package/skills/improve-skill/scripts/extract-session.js +349 -0
- package/skills/oebb-scotty/SKILL.md +429 -0
- package/skills/oebb-scotty/arrivals.sh +83 -0
- package/skills/oebb-scotty/departures.sh +83 -0
- package/skills/oebb-scotty/disruptions.sh +33 -0
- package/skills/oebb-scotty/search-station.sh +36 -0
- package/skills/oebb-scotty/trip.sh +119 -0
- package/skills/openscad/SKILL.md +232 -0
- package/skills/openscad/examples/parametric_box.scad +92 -0
- package/skills/openscad/examples/phone_stand.scad +95 -0
- package/skills/openscad/tools/common.sh +50 -0
- package/skills/openscad/tools/export-stl.sh +56 -0
- package/skills/openscad/tools/extract-params.sh +147 -0
- package/skills/openscad/tools/multi-preview.sh +68 -0
- package/skills/openscad/tools/preview.sh +74 -0
- package/skills/openscad/tools/render-with-params.sh +91 -0
- package/skills/openscad/tools/validate.sh +46 -0
- package/skills/pi-share/SKILL.md +105 -0
- package/skills/pi-share/fetch-session.mjs +322 -0
- package/skills/sentry/SKILL.md +239 -0
- package/skills/sentry/lib/auth.js +99 -0
- package/skills/sentry/scripts/fetch-event.js +329 -0
- package/skills/sentry/scripts/fetch-issue.js +356 -0
- package/skills/sentry/scripts/list-issues.js +239 -0
- package/skills/sentry/scripts/search-events.js +291 -0
- package/skills/sentry/scripts/search-logs.js +240 -0
- package/skills/tmux/SKILL.md +105 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/web-browser/SKILL.md +91 -0
- package/skills/web-browser/scripts/cdp.js +210 -0
- package/skills/web-browser/scripts/dismiss-cookies.js +373 -0
- package/skills/web-browser/scripts/eval.js +68 -0
- package/skills/web-browser/scripts/logs-tail.js +69 -0
- package/skills/web-browser/scripts/nav.js +65 -0
- package/skills/web-browser/scripts/net-summary.js +94 -0
- package/skills/web-browser/scripts/package-lock.json +33 -0
- package/skills/web-browser/scripts/package.json +6 -0
- package/skills/web-browser/scripts/pick.js +165 -0
- package/skills/web-browser/scripts/screenshot.js +52 -0
- package/skills/web-browser/scripts/start.js +80 -0
- package/skills/web-browser/scripts/watch.js +266 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract session transcript from Claude Code, Pi, or Codex session files.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* ./extract-session.js [session-path]
|
|
8
|
+
* ./extract-session.js --agent claude|pi|codex [--cwd /path/to/dir]
|
|
9
|
+
*
|
|
10
|
+
* If no arguments, auto-detects based on current working directory.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const os = require('os');
|
|
16
|
+
|
|
17
|
+
// Parse arguments
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
let sessionPath = null;
|
|
20
|
+
let agent = null;
|
|
21
|
+
let cwd = process.cwd();
|
|
22
|
+
|
|
23
|
+
for (let i = 0; i < args.length; i++) {
|
|
24
|
+
if (args[i] === '--agent' && args[i + 1]) {
|
|
25
|
+
agent = args[++i];
|
|
26
|
+
} else if (args[i] === '--cwd' && args[i + 1]) {
|
|
27
|
+
cwd = args[++i];
|
|
28
|
+
} else if (!args[i].startsWith('-')) {
|
|
29
|
+
sessionPath = args[i];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Encode CWD for session path lookup
|
|
35
|
+
*/
|
|
36
|
+
function encodeCwd(cwd, style) {
|
|
37
|
+
if (style === 'pi') {
|
|
38
|
+
// Pi uses: --<cwd-without-leading-slash-with-slashes-as-dashes>--
|
|
39
|
+
// e.g., /Users/mitsuhiko/Development/myproject -> --Users-mitsuhiko-Development-myproject--
|
|
40
|
+
const safePath = `--${cwd.replace(/^[/\\]/, '').replace(/[/\\:]/g, '-')}--`;
|
|
41
|
+
return safePath;
|
|
42
|
+
}
|
|
43
|
+
// Claude Code: just replace / with -
|
|
44
|
+
return cwd.replace(/\//g, '-');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Find the most recent session file in a directory
|
|
49
|
+
*/
|
|
50
|
+
function findMostRecentSession(dir) {
|
|
51
|
+
if (!fs.existsSync(dir)) return null;
|
|
52
|
+
|
|
53
|
+
const files = fs.readdirSync(dir)
|
|
54
|
+
.filter(f => f.endsWith('.jsonl'))
|
|
55
|
+
.map(f => ({
|
|
56
|
+
name: f,
|
|
57
|
+
path: path.join(dir, f),
|
|
58
|
+
mtime: fs.statSync(path.join(dir, f)).mtime
|
|
59
|
+
}))
|
|
60
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
61
|
+
|
|
62
|
+
return files.length > 0 ? files[0].path : null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Find Codex session matching CWD
|
|
67
|
+
*/
|
|
68
|
+
function findCodexSession(targetCwd) {
|
|
69
|
+
const baseDir = path.join(os.homedir(), '.codex', 'sessions');
|
|
70
|
+
if (!fs.existsSync(baseDir)) return null;
|
|
71
|
+
|
|
72
|
+
// Find all session files, sorted by mtime
|
|
73
|
+
const allSessions = [];
|
|
74
|
+
|
|
75
|
+
function walkDir(dir) {
|
|
76
|
+
if (!fs.existsSync(dir)) return;
|
|
77
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
78
|
+
const fullPath = path.join(dir, entry.name);
|
|
79
|
+
if (entry.isDirectory()) {
|
|
80
|
+
walkDir(fullPath);
|
|
81
|
+
} else if (entry.name.endsWith('.jsonl')) {
|
|
82
|
+
allSessions.push({
|
|
83
|
+
path: fullPath,
|
|
84
|
+
mtime: fs.statSync(fullPath).mtime
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
walkDir(baseDir);
|
|
91
|
+
allSessions.sort((a, b) => b.mtime - a.mtime);
|
|
92
|
+
|
|
93
|
+
// Find most recent matching CWD
|
|
94
|
+
for (const session of allSessions.slice(0, 50)) { // Check last 50
|
|
95
|
+
try {
|
|
96
|
+
const firstLine = fs.readFileSync(session.path, 'utf8').split('\n')[0];
|
|
97
|
+
const data = JSON.parse(firstLine);
|
|
98
|
+
if (data.payload?.cwd === targetCwd) {
|
|
99
|
+
return session.path;
|
|
100
|
+
}
|
|
101
|
+
} catch (e) {
|
|
102
|
+
// Skip invalid files
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Auto-detect session based on CWD
|
|
111
|
+
*/
|
|
112
|
+
function autoDetectSession(cwd) {
|
|
113
|
+
// Try Claude Code first
|
|
114
|
+
const claudePath = path.join(os.homedir(), '.claude', 'projects', encodeCwd(cwd, 'claude'));
|
|
115
|
+
let session = findMostRecentSession(claudePath);
|
|
116
|
+
if (session) return { agent: 'claude', path: session };
|
|
117
|
+
|
|
118
|
+
// Try Pi
|
|
119
|
+
const piPath = path.join(os.homedir(), '.pi', 'agent', 'sessions', encodeCwd(cwd, 'pi'));
|
|
120
|
+
session = findMostRecentSession(piPath);
|
|
121
|
+
if (session) return { agent: 'pi', path: session };
|
|
122
|
+
|
|
123
|
+
// Try Codex
|
|
124
|
+
session = findCodexSession(cwd);
|
|
125
|
+
if (session) return { agent: 'codex', path: session };
|
|
126
|
+
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Parse Claude Code session format
|
|
132
|
+
*/
|
|
133
|
+
function parseClaudeSession(content) {
|
|
134
|
+
const messages = [];
|
|
135
|
+
const lines = content.trim().split('\n');
|
|
136
|
+
|
|
137
|
+
for (const line of lines) {
|
|
138
|
+
try {
|
|
139
|
+
const entry = JSON.parse(line);
|
|
140
|
+
if (entry.message?.role && entry.message?.content) {
|
|
141
|
+
const msg = entry.message;
|
|
142
|
+
messages.push({
|
|
143
|
+
role: msg.role,
|
|
144
|
+
content: extractContent(msg.content),
|
|
145
|
+
timestamp: entry.timestamp
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
} catch (e) {
|
|
149
|
+
// Skip invalid lines
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return messages;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Parse Pi session format
|
|
158
|
+
*/
|
|
159
|
+
function parsePiSession(content) {
|
|
160
|
+
const messages = [];
|
|
161
|
+
const lines = content.trim().split('\n');
|
|
162
|
+
|
|
163
|
+
for (const line of lines) {
|
|
164
|
+
try {
|
|
165
|
+
const entry = JSON.parse(line);
|
|
166
|
+
if (entry.type === 'message' && entry.message?.role) {
|
|
167
|
+
messages.push({
|
|
168
|
+
role: entry.message.role,
|
|
169
|
+
content: extractContent(entry.message.content),
|
|
170
|
+
timestamp: entry.timestamp
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
} catch (e) {
|
|
174
|
+
// Skip invalid lines
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return messages;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Parse Codex session format
|
|
183
|
+
*/
|
|
184
|
+
function parseCodexSession(content) {
|
|
185
|
+
const messages = [];
|
|
186
|
+
const lines = content.trim().split('\n');
|
|
187
|
+
|
|
188
|
+
for (const line of lines) {
|
|
189
|
+
try {
|
|
190
|
+
const entry = JSON.parse(line);
|
|
191
|
+
if (entry.type === 'response_item' && entry.payload?.role) {
|
|
192
|
+
const payload = entry.payload;
|
|
193
|
+
messages.push({
|
|
194
|
+
role: payload.role,
|
|
195
|
+
content: extractContent(payload.content),
|
|
196
|
+
timestamp: entry.timestamp
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
} catch (e) {
|
|
200
|
+
// Skip invalid lines
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return messages;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Extract text content from various content formats
|
|
209
|
+
*/
|
|
210
|
+
function extractContent(content) {
|
|
211
|
+
if (typeof content === 'string') return content;
|
|
212
|
+
if (!Array.isArray(content)) return JSON.stringify(content);
|
|
213
|
+
|
|
214
|
+
const parts = [];
|
|
215
|
+
for (const item of content) {
|
|
216
|
+
if (typeof item === 'string') {
|
|
217
|
+
parts.push(item);
|
|
218
|
+
} else if (item.type === 'text') {
|
|
219
|
+
parts.push(item.text);
|
|
220
|
+
} else if (item.type === 'input_text') {
|
|
221
|
+
parts.push(item.text);
|
|
222
|
+
} else if (item.type === 'tool_use') {
|
|
223
|
+
parts.push(`[Tool: ${item.name}]\n${JSON.stringify(item.input, null, 2)}`);
|
|
224
|
+
} else if (item.type === 'tool_result') {
|
|
225
|
+
const result = typeof item.content === 'string'
|
|
226
|
+
? item.content
|
|
227
|
+
: JSON.stringify(item.content);
|
|
228
|
+
// Truncate long tool results
|
|
229
|
+
const truncated = result.length > 500
|
|
230
|
+
? result.slice(0, 500) + '\n[... truncated ...]'
|
|
231
|
+
: result;
|
|
232
|
+
parts.push(`[Tool Result]\n${truncated}`);
|
|
233
|
+
} else {
|
|
234
|
+
parts.push(`[${item.type}]`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return parts.join('\n');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Format messages as readable transcript
|
|
243
|
+
*/
|
|
244
|
+
function formatTranscript(messages, maxMessages = 100) {
|
|
245
|
+
const recent = messages.slice(-maxMessages);
|
|
246
|
+
const lines = [];
|
|
247
|
+
|
|
248
|
+
for (const msg of recent) {
|
|
249
|
+
const role = msg.role.toUpperCase();
|
|
250
|
+
lines.push(`\n### ${role}:\n`);
|
|
251
|
+
lines.push(msg.content);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (messages.length > maxMessages) {
|
|
255
|
+
lines.unshift(`\n[... ${messages.length - maxMessages} earlier messages omitted ...]\n`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return lines.join('\n');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Main
|
|
262
|
+
async function main() {
|
|
263
|
+
let result;
|
|
264
|
+
|
|
265
|
+
if (sessionPath) {
|
|
266
|
+
// Explicit path provided
|
|
267
|
+
if (!fs.existsSync(sessionPath)) {
|
|
268
|
+
console.error(`Session file not found: ${sessionPath}`);
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
// Guess agent from path
|
|
272
|
+
if (sessionPath.includes('.claude')) {
|
|
273
|
+
result = { agent: 'claude', path: sessionPath };
|
|
274
|
+
} else if (sessionPath.includes('.pi')) {
|
|
275
|
+
result = { agent: 'pi', path: sessionPath };
|
|
276
|
+
} else if (sessionPath.includes('.codex')) {
|
|
277
|
+
result = { agent: 'codex', path: sessionPath };
|
|
278
|
+
} else {
|
|
279
|
+
// Default to Claude format
|
|
280
|
+
result = { agent: 'claude', path: sessionPath };
|
|
281
|
+
}
|
|
282
|
+
} else if (agent) {
|
|
283
|
+
// Agent specified, find session for that agent
|
|
284
|
+
if (agent === 'claude') {
|
|
285
|
+
const dir = path.join(os.homedir(), '.claude', 'projects', encodeCwd(cwd, 'claude'));
|
|
286
|
+
const session = findMostRecentSession(dir);
|
|
287
|
+
if (!session) {
|
|
288
|
+
console.error(`No Claude Code session found for: ${cwd}`);
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
result = { agent: 'claude', path: session };
|
|
292
|
+
} else if (agent === 'pi') {
|
|
293
|
+
const dir = path.join(os.homedir(), '.pi', 'agent', 'sessions', encodeCwd(cwd, 'pi'));
|
|
294
|
+
const session = findMostRecentSession(dir);
|
|
295
|
+
if (!session) {
|
|
296
|
+
console.error(`No Pi session found for: ${cwd}`);
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
result = { agent: 'pi', path: session };
|
|
300
|
+
} else if (agent === 'codex') {
|
|
301
|
+
const session = findCodexSession(cwd);
|
|
302
|
+
if (!session) {
|
|
303
|
+
console.error(`No Codex session found for: ${cwd}`);
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
result = { agent: 'codex', path: session };
|
|
307
|
+
} else {
|
|
308
|
+
console.error(`Unknown agent: ${agent}`);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
// Auto-detect
|
|
313
|
+
result = autoDetectSession(cwd);
|
|
314
|
+
if (!result) {
|
|
315
|
+
console.error(`No session found for: ${cwd}`);
|
|
316
|
+
console.error('Try specifying --agent claude|pi|codex or provide a session path directly.');
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Read and parse session
|
|
322
|
+
const content = fs.readFileSync(result.path, 'utf8');
|
|
323
|
+
|
|
324
|
+
let messages;
|
|
325
|
+
switch (result.agent) {
|
|
326
|
+
case 'claude':
|
|
327
|
+
messages = parseClaudeSession(content);
|
|
328
|
+
break;
|
|
329
|
+
case 'pi':
|
|
330
|
+
messages = parsePiSession(content);
|
|
331
|
+
break;
|
|
332
|
+
case 'codex':
|
|
333
|
+
messages = parseCodexSession(content);
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Output metadata and transcript
|
|
338
|
+
console.log(`# Session Transcript`);
|
|
339
|
+
console.log(`Agent: ${result.agent}`);
|
|
340
|
+
console.log(`File: ${result.path}`);
|
|
341
|
+
console.log(`Messages: ${messages.length}`);
|
|
342
|
+
console.log('');
|
|
343
|
+
console.log(formatTranscript(messages));
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
main().catch(e => {
|
|
347
|
+
console.error(e.message);
|
|
348
|
+
process.exit(1);
|
|
349
|
+
});
|