toenobu-agent 0.1.4 → 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/.pi/research-handoff.md +64 -0
- package/.pi/todos/49a97d86.md +40 -0
- package/.pi/todos/67d010fe.md +11 -0
- package/.pi/todos/done/2b2baa06.md +23 -0
- package/Makefile +4 -0
- package/extensions/handoff.ts +153 -0
- package/package.json +6 -2
- package/skills/session-renamer/SKILL.md +35 -34
- package/skills/code-simplifier/SKILL.md +0 -44
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Here's a thorough analysis of the `handoff.ts` extension:
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## 📋 Summary: `handoff.ts` Extension
|
|
6
|
+
|
|
7
|
+
### 1. What It Does
|
|
8
|
+
|
|
9
|
+
**Handoff** is a session-transfer extension for a coding agent (part of the `pi-mono` monorepo). Its purpose is to **intelligently transfer context from one conversation session to a new, focused one** — without the information loss that comes with simple context compaction.
|
|
10
|
+
|
|
11
|
+
When you invoke `/handoff <goal>`, it:
|
|
12
|
+
1. Reads the current conversation history
|
|
13
|
+
2. Uses an LLM to generate a concise, self-contained "handoff prompt" summarizing relevant context + your stated goal
|
|
14
|
+
3. Lets you **edit the generated prompt** before committing
|
|
15
|
+
4. Creates a **new session** (with a reference to the parent session)
|
|
16
|
+
5. Pre-fills the editor with the prompt, ready for submission
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
### 2. Key Features & Functionality
|
|
21
|
+
|
|
22
|
+
| Feature | Details |
|
|
23
|
+
|---|---|
|
|
24
|
+
| **`/handoff <goal>` command** | Registered via `pi.registerCommand("handoff", ...)` |
|
|
25
|
+
| **LLM-powered summarization** | Calls `complete()` with a carefully crafted system prompt to distill the conversation |
|
|
26
|
+
| **Structured output format** | Generated prompt follows a `## Context / ## Task` format — concise but self-contained |
|
|
27
|
+
| **Abortable loader UI** | Shows a `BorderedLoader` while generating; supports abort/cancel mid-flight via `AbortSignal` |
|
|
28
|
+
| **Interactive prompt editing** | Opens an editor UI (`ctx.ui.editor(...)`) for the user to refine the generated prompt before proceeding |
|
|
29
|
+
| **Parent session tracking** | New session is created with `parentSession` linked to the current one, preserving lineage |
|
|
30
|
+
| **Error handling** | Guards for: no UI, no model, empty goal, empty conversation, auth failures, generation failures, user cancellations |
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
### 3. Dependencies & Requirements
|
|
35
|
+
|
|
36
|
+
**Runtime Dependencies:**
|
|
37
|
+
- `@mariozechner/pi-ai` — provides `complete()` (LLM call) and the `Message` type
|
|
38
|
+
- `@mariozechner/pi-coding-agent` — provides:
|
|
39
|
+
- `ExtensionAPI` / `SessionEntry` — extension host contract
|
|
40
|
+
- `BorderedLoader` — terminal UI loading indicator
|
|
41
|
+
- `convertToLlm` / `serializeConversation` — conversation format utilities
|
|
42
|
+
|
|
43
|
+
**Runtime Requirements:**
|
|
44
|
+
- Must run in **interactive (UI) mode** — `ctx.hasUI` is checked first
|
|
45
|
+
- A **model must be selected** (`ctx.model`)
|
|
46
|
+
- A valid **API key** must be available via `ctx.modelRegistry.getApiKeyAndHeaders()`
|
|
47
|
+
- An existing **non-empty conversation** to hand off
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
### 4. Implementation Complexity
|
|
52
|
+
|
|
53
|
+
**Overall: Low-to-Medium** ⚙️
|
|
54
|
+
|
|
55
|
+
| Aspect | Assessment |
|
|
56
|
+
|---|---|
|
|
57
|
+
| **Code size** | ~130 lines — compact and focused |
|
|
58
|
+
| **Control flow** | Linear with clear early-exit guards; async/await throughout |
|
|
59
|
+
| **LLM integration** | Single `complete()` call with a static system prompt — no streaming, no multi-turn |
|
|
60
|
+
| **UI interaction** | Uses 3 UI primitives: `custom()` (loader), `editor()`, `setEditorText()` — all provided by the host |
|
|
61
|
+
| **State management** | Minimal — reads session branch, creates a new session, no persistent state |
|
|
62
|
+
| **Error paths** | Well-handled (null checks, try/catch, cancellation signals) |
|
|
63
|
+
|
|
64
|
+
The extension is a clean example of the **extension pattern** in `pi-coding-agent`: it relies heavily on the host `ExtensionAPI` for UI, auth, and session management, keeping its own logic thin — just orchestrating a generate → edit → create-session → pre-fill workflow.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "49a97d86",
|
|
3
|
+
"title": "Implement handoff extension for session context transfer",
|
|
4
|
+
"tags": [
|
|
5
|
+
"extension",
|
|
6
|
+
"feature",
|
|
7
|
+
"pi",
|
|
8
|
+
"high-priority"
|
|
9
|
+
],
|
|
10
|
+
"status": "completed",
|
|
11
|
+
"created_at": "2026-03-29T12:10:10.662Z",
|
|
12
|
+
"assigned_to_session": "e5898ce7-3b44-45b0-b50e-f8273d6419a4"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
## Overview
|
|
16
|
+
Create a 1:1 copy of the [pi-mono handoff.ts](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/examples/extensions/handoff.ts) extension.
|
|
17
|
+
|
|
18
|
+
**Priority:** High
|
|
19
|
+
|
|
20
|
+
## Implementation Steps
|
|
21
|
+
1. **Setup dependencies** — Add `@mariozechner/pi-coding-agent` and `@mariozechner/pi-ai` to the project
|
|
22
|
+
2. **Create extension folder** — `./extensions/`
|
|
23
|
+
3. **Implement `handoff.ts`** — Copy reference implementation with:
|
|
24
|
+
- `/handoff <goal>` command
|
|
25
|
+
- LLM-powered conversation summarization
|
|
26
|
+
- `## Context` / `## Task` output format
|
|
27
|
+
- BorderedLoader UI + editor flow
|
|
28
|
+
- Parent session linking
|
|
29
|
+
|
|
30
|
+
## Acceptance Criteria
|
|
31
|
+
- [x] Dependencies installed
|
|
32
|
+
- [x] `./extensions/handoff.ts` created
|
|
33
|
+
- [x] `/handoff <goal>` command works in interactive mode
|
|
34
|
+
- [x] Generates summarized context via LLM
|
|
35
|
+
- [x] Opens editor for prompt refinement
|
|
36
|
+
- [x] Creates new session with parent linkage
|
|
37
|
+
|
|
38
|
+
## Reference
|
|
39
|
+
- Source: https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/examples/extensions/handoff.ts
|
|
40
|
+
- Analysis: `.pi/research-handoff.md`
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "2b2baa06",
|
|
3
|
+
"title": "Update session-renamer skill to allow spaces and reference todo titles",
|
|
4
|
+
"tags": [
|
|
5
|
+
"skill",
|
|
6
|
+
"session-renamer",
|
|
7
|
+
"urgent"
|
|
8
|
+
],
|
|
9
|
+
"status": "done",
|
|
10
|
+
"created_at": "2026-03-18T06:58:54.041Z"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
## Task
|
|
14
|
+
Update the session-renamer skill file with:
|
|
15
|
+
|
|
16
|
+
1. **Naming format** - Allow spaces instead of kebab-case
|
|
17
|
+
2. **Todo reference** - If session relates to a todo, use the todo's title as session name
|
|
18
|
+
|
|
19
|
+
## File
|
|
20
|
+
`/Users/toenobu/homebrew/lib/node_modules/toenobu-agent/skills/session-renamer/SKILL.md`
|
|
21
|
+
|
|
22
|
+
## Done when
|
|
23
|
+
Skill file updated and saved
|
package/Makefile
CHANGED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handoff extension - transfer context to a new focused session
|
|
3
|
+
*
|
|
4
|
+
* Instead of compacting (which is lossy), handoff extracts what matters
|
|
5
|
+
* for your next task and creates a new session with a generated prompt.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* /handoff now implement this for teams as well
|
|
9
|
+
* /handoff execute phase one of the plan
|
|
10
|
+
* /handoff check other places that need this fix
|
|
11
|
+
*
|
|
12
|
+
* The generated prompt appears as a draft in the editor for review/editing.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { complete, type Message } from "@mariozechner/pi-ai";
|
|
16
|
+
import type { ExtensionAPI, SessionEntry } from "@mariozechner/pi-coding-agent";
|
|
17
|
+
import { BorderedLoader, convertToLlm, serializeConversation } from "@mariozechner/pi-coding-agent";
|
|
18
|
+
|
|
19
|
+
const SYSTEM_PROMPT = `You are a context transfer assistant. Given a conversation history and the user's goal for a new thread, generate a focused prompt that:
|
|
20
|
+
|
|
21
|
+
1. Summarizes relevant context from the conversation (decisions made, approaches taken, key findings)
|
|
22
|
+
2. Lists any relevant files that were discussed or modified
|
|
23
|
+
3. Clearly states the next task based on the user's goal
|
|
24
|
+
4. Is self-contained - the new thread should be able to proceed without the old conversation
|
|
25
|
+
|
|
26
|
+
Format your response as a prompt the user can send to start the new thread. Be concise but include all necessary context. Do not include any preamble like "Here's the prompt" - just output the prompt itself.
|
|
27
|
+
|
|
28
|
+
Example output format:
|
|
29
|
+
## Context
|
|
30
|
+
We've been working on X. Key decisions:
|
|
31
|
+
- Decision 1
|
|
32
|
+
- Decision 2
|
|
33
|
+
|
|
34
|
+
Files involved:
|
|
35
|
+
- path/to/file1.ts
|
|
36
|
+
- path/to/file2.ts
|
|
37
|
+
|
|
38
|
+
## Task
|
|
39
|
+
[Clear description of what to do next based on user's goal]`;
|
|
40
|
+
|
|
41
|
+
export default function (pi: ExtensionAPI) {
|
|
42
|
+
pi.registerCommand("handoff", {
|
|
43
|
+
description: "Transfer context to a new focused session",
|
|
44
|
+
handler: async (args, ctx) => {
|
|
45
|
+
if (!ctx.hasUI) {
|
|
46
|
+
ctx.ui.notify("handoff requires interactive mode", "error");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!ctx.model) {
|
|
51
|
+
ctx.ui.notify("No model selected", "error");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const goal = args.trim();
|
|
56
|
+
if (!goal) {
|
|
57
|
+
ctx.ui.notify("Usage: /handoff <goal for new thread>", "error");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Gather conversation context from current branch
|
|
62
|
+
const branch = ctx.sessionManager.getBranch();
|
|
63
|
+
const messages = branch
|
|
64
|
+
.filter((entry): entry is SessionEntry & { type: "message" } => entry.type === "message")
|
|
65
|
+
.map((entry) => entry.message);
|
|
66
|
+
|
|
67
|
+
if (messages.length === 0) {
|
|
68
|
+
ctx.ui.notify("No conversation to hand off", "error");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Convert to LLM format and serialize
|
|
73
|
+
const llmMessages = convertToLlm(messages);
|
|
74
|
+
const conversationText = serializeConversation(llmMessages);
|
|
75
|
+
const currentSessionFile = ctx.sessionManager.getSessionFile();
|
|
76
|
+
|
|
77
|
+
// Generate the handoff prompt with loader UI
|
|
78
|
+
const result = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
|
|
79
|
+
const loader = new BorderedLoader(tui, theme, `Generating handoff prompt...`);
|
|
80
|
+
loader.onAbort = () => done(null);
|
|
81
|
+
|
|
82
|
+
const doGenerate = async () => {
|
|
83
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model!);
|
|
84
|
+
if (!auth.ok || !auth.apiKey) {
|
|
85
|
+
throw new Error(auth.ok ? `No API key for ${ctx.model!.provider}` : auth.error);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const userMessage: Message = {
|
|
89
|
+
role: "user",
|
|
90
|
+
content: [
|
|
91
|
+
{
|
|
92
|
+
type: "text",
|
|
93
|
+
text: `## Conversation History\n\n${conversationText}\n\n## User's Goal for New Thread\n\n${goal}`,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
timestamp: Date.now(),
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const response = await complete(
|
|
100
|
+
ctx.model!,
|
|
101
|
+
{ systemPrompt: SYSTEM_PROMPT, messages: [userMessage] },
|
|
102
|
+
{ apiKey: auth.apiKey, headers: auth.headers, signal: loader.signal },
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
if (response.stopReason === "aborted") {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return response.content
|
|
110
|
+
.filter((c): c is { type: "text"; text: string } => c.type === "text")
|
|
111
|
+
.map((c) => c.text)
|
|
112
|
+
.join("\n");
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
doGenerate()
|
|
116
|
+
.then(done)
|
|
117
|
+
.catch((err) => {
|
|
118
|
+
console.error("Handoff generation failed:", err);
|
|
119
|
+
done(null);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return loader;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (result === null) {
|
|
126
|
+
ctx.ui.notify("Cancelled", "info");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Let user edit the generated prompt
|
|
131
|
+
const editedPrompt = await ctx.ui.editor("Edit handoff prompt", result);
|
|
132
|
+
|
|
133
|
+
if (editedPrompt === undefined) {
|
|
134
|
+
ctx.ui.notify("Cancelled", "info");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Create new session with parent tracking
|
|
139
|
+
const newSessionResult = await ctx.newSession({
|
|
140
|
+
parentSession: currentSessionFile,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (newSessionResult.cancelled) {
|
|
144
|
+
ctx.ui.notify("New session cancelled", "info");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Set the edited prompt in the main editor for submission
|
|
149
|
+
ctx.ui.setEditorText(editedPrompt);
|
|
150
|
+
ctx.ui.notify("Handoff ready. Submit when ready.", "info");
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toenobu-agent",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "toenobu's pi coding agent skills and extensions",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,10 +15,14 @@
|
|
|
15
15
|
"pi": {
|
|
16
16
|
"skills": [
|
|
17
17
|
"./skills"
|
|
18
|
+
],
|
|
19
|
+
"extensions": [
|
|
20
|
+
"./extensions"
|
|
18
21
|
]
|
|
19
22
|
},
|
|
20
23
|
"peerDependencies": {
|
|
21
|
-
"@mariozechner/pi-coding-agent": "*"
|
|
24
|
+
"@mariozechner/pi-coding-agent": "*",
|
|
25
|
+
"@mariozechner/pi-ai": "*"
|
|
22
26
|
},
|
|
23
27
|
"license": "MIT"
|
|
24
28
|
}
|
|
@@ -1,53 +1,54 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Analyzes session content
|
|
2
|
+
description: Analyzes session content to generate a descriptive name or suggest deletion if meaningless
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Session Renamer
|
|
6
6
|
|
|
7
|
-
Use
|
|
7
|
+
Use when the user asks to name/rename the current session.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Process
|
|
10
10
|
|
|
11
|
-
Analyze the conversation history
|
|
11
|
+
1. **Analyze** the conversation history
|
|
12
|
+
2. **Decide**: meaningful content or not?
|
|
13
|
+
- **Meaningless** → suggest `/delete`
|
|
14
|
+
- **Meaningful** → generate 2-3 name candidates
|
|
15
|
+
3. **Present** options to user
|
|
16
|
+
4. **Apply** with `/name <chosen-name>` or `/delete`
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
2. **Is Concise**: Keep it short (3-6 words, under 50 characters)
|
|
15
|
-
3. **Uses Descriptive Words**: Include key technologies, actions, or domains
|
|
16
|
-
4. **Uses kebab-case**: Lowercase with hyphens for consistency
|
|
18
|
+
## Meaningless Sessions
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
Suggest deletion if the session:
|
|
21
|
+
- Contains only greetings or small talk (e.g., just "hello" with no follow-up)
|
|
22
|
+
- Has abandoned/incomplete queries with no outcome
|
|
23
|
+
- Consists of test messages or accidental inputs
|
|
24
|
+
- Has no code changes, file operations, or real discussion
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
- `debug-api-rate-limiting`
|
|
25
|
-
- `setup-docker-compose`
|
|
26
|
-
- `create-session-renamer-skill`
|
|
26
|
+
**Output:**
|
|
27
|
+
```
|
|
28
|
+
This session doesn't have meaningful content:
|
|
29
|
+
- [reason]
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- Too long names (over 50 characters)
|
|
31
|
-
- Names that don't reflect actual content
|
|
32
|
-
- CamelCase or spaces
|
|
31
|
+
Delete with `/delete`? Or suggest a name to keep it.
|
|
32
|
+
```
|
|
33
33
|
|
|
34
|
-
##
|
|
34
|
+
## Meaningful Sessions
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
Generate names that:
|
|
37
|
+
- Capture the main topic (3-6 words, <50 chars)
|
|
38
|
+
- Use spaces (not kebab-case)
|
|
39
|
+
- Include key technologies, actions, or domains
|
|
40
|
+
- **If related to a todo**: use the todo's title as the session name
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
**Good:** `Refactor auth middleware`, `Fix TypeScript build errors`, `Setup Docker Compose`
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
**Avoid:** Generic (`coding session`), too long, doesn't reflect actual content
|
|
45
45
|
|
|
46
|
+
**Output:**
|
|
46
47
|
```
|
|
47
|
-
Suggested
|
|
48
|
-
1. `
|
|
49
|
-
2. `
|
|
50
|
-
3. `
|
|
48
|
+
Suggested names:
|
|
49
|
+
1. `Primary topic name`
|
|
50
|
+
2. `Alternative name`
|
|
51
|
+
3. `Another option`
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
Pick one or suggest your own.
|
|
53
54
|
```
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Simplifies and refines code for clarity, consistency, and maintainability while preserving all functionality
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Code Simplifier
|
|
6
|
-
|
|
7
|
-
Use this skill when the user asks to simplify, refine, or clean up code.
|
|
8
|
-
|
|
9
|
-
## Instructions
|
|
10
|
-
|
|
11
|
-
You are an expert code simplification specialist focused on enhancing code clarity, consistency, and maintainability while preserving exact functionality.
|
|
12
|
-
|
|
13
|
-
Analyze the code and apply refinements that:
|
|
14
|
-
|
|
15
|
-
1. **Preserve Functionality**: Never change what the code does - only how it does it. All original features, outputs, and behaviors must remain intact.
|
|
16
|
-
|
|
17
|
-
2. **Enhance Clarity**: Simplify code structure by:
|
|
18
|
-
- Reducing unnecessary complexity and nesting
|
|
19
|
-
- Eliminating redundant code and abstractions
|
|
20
|
-
- Improving readability through clear variable and function names
|
|
21
|
-
- Consolidating related logic
|
|
22
|
-
- Removing unnecessary comments that describe obvious code
|
|
23
|
-
- Avoid nested ternary operators - prefer switch statements or if/else chains
|
|
24
|
-
- Choose clarity over brevity - explicit code is often better than overly compact code
|
|
25
|
-
|
|
26
|
-
3. **Maintain Balance**: Avoid over-simplification that could:
|
|
27
|
-
- Reduce code clarity or maintainability
|
|
28
|
-
- Create overly clever solutions that are hard to understand
|
|
29
|
-
- Combine too many concerns into single functions
|
|
30
|
-
- Prioritize "fewer lines" over readability
|
|
31
|
-
|
|
32
|
-
4. **Apply Best Practices**:
|
|
33
|
-
- Follow project coding standards from AGENTS.md/CLAUDE.md if present
|
|
34
|
-
- Use proper error handling patterns
|
|
35
|
-
- Maintain consistent naming conventions
|
|
36
|
-
- Use appropriate language idioms
|
|
37
|
-
|
|
38
|
-
## Process
|
|
39
|
-
|
|
40
|
-
1. Identify the code sections to simplify
|
|
41
|
-
2. Analyze for opportunities to improve elegance and consistency
|
|
42
|
-
3. Apply best practices and coding standards
|
|
43
|
-
4. Ensure all functionality remains unchanged
|
|
44
|
-
5. Verify the refined code is simpler and more maintainable
|