@aliou/pi-dev-kit 0.6.0 → 0.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliou/pi-dev-kit",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "private": false,
@@ -39,14 +39,14 @@
39
39
  "@sinclair/typebox": "^0.34.41"
40
40
  },
41
41
  "peerDependencies": {
42
- "@mariozechner/pi-coding-agent": "0.64.0",
43
- "@mariozechner/pi-tui": "0.64.0"
42
+ "@mariozechner/pi-coding-agent": "0.65.2",
43
+ "@mariozechner/pi-tui": "0.65.2"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@biomejs/biome": "^2.3.13",
47
47
  "@changesets/cli": "^2.27.11",
48
- "@mariozechner/pi-coding-agent": "0.64.0",
49
- "@mariozechner/pi-tui": "0.64.0",
48
+ "@mariozechner/pi-coding-agent": "0.65.2",
49
+ "@mariozechner/pi-tui": "0.65.2",
50
50
  "@types/node": "^25.0.10",
51
51
  "husky": "^9.1.7",
52
52
  "typescript": "^5.9.3"
@@ -11,10 +11,10 @@ Guide for creating and maintaining Pi extensions. Read the relevant reference fi
11
11
 
12
12
  Pi injects these packages via jiti at runtime. Extensions do not need to install them — they are available as peer dependencies:
13
13
 
14
- - `@mariozechner/pi-coding-agent` — core types, utilities, `Type`/`Static` (re-exported from TypeBox)
14
+ - `@mariozechner/pi-coding-agent` — core types, utilities, and extension APIs
15
15
  - `@mariozechner/pi-tui` — TUI components
16
16
  - `@mariozechner/pi-ai` — AI utilities (`StringEnum`, etc.)
17
- - `@sinclair/typebox` — schema definitions (also re-exported from `pi-coding-agent`, prefer the re-export)
17
+ - `@sinclair/typebox` — schema definitions for tool parameters and related types
18
18
 
19
19
  ```typescript
20
20
  // Tool UI components (from @aliou/pi-utils-ui)
@@ -109,7 +109,7 @@ When implementing, look at these existing extensions for patterns:
109
109
  13. **Output truncation**: For tools returning large text, use `truncateHead()` from `@mariozechner/pi-coding-agent`. Write full content to temp file. Append footer with line/byte counts and temp file path.
110
110
  14. **Core/lib pattern**: Extract domain logic into modules (`client.ts`, `manager.ts`) that don't import from Pi. Tools are thin wrappers. Core modules are unit-testable with vitest.
111
111
  15. **Humanize messages**: Show display names first, IDs in dim/parens. `"Started \"backend\" (proc_42)"` not `"Started proc_42"`.
112
- 16. **peerDependencies**: Pi injects `@mariozechner/pi-coding-agent`, `@mariozechner/pi-tui`, `@mariozechner/pi-ai`, and `@sinclair/typebox` via jiti at runtime. Any of these that your extension imports must be listed in `peerDependencies` with `optional: true` in `peerDependenciesMeta`. Without `optional: true`, npm 7+ auto-installs peers, adding hundreds of packages on every install even though Pi already provides them. Keep them in `devDependencies` too for local type checking — `pnpm install` installs peers, so development is unaffected. Use `>=CURRENT_VERSION` range, not `*`. Prefer importing `Type`/`Static` from `@mariozechner/pi-coding-agent` rather than `@sinclair/typebox` directly.
112
+ 16. **peerDependencies**: Pi injects `@mariozechner/pi-coding-agent`, `@mariozechner/pi-tui`, `@mariozechner/pi-ai`, and `@sinclair/typebox` via jiti at runtime. Any of these that your extension imports must be listed in `peerDependencies` with `optional: true` in `peerDependenciesMeta`. Without `optional: true`, npm 7+ auto-installs peers, adding hundreds of packages on every install even though Pi already provides them. Keep them in `devDependencies` too for local type checking — `pnpm install` installs peers, so development is unaffected. Use `>=CURRENT_VERSION` range, not `*`.
113
113
  17. **Check existing components**: Before creating a new TUI component, check if `pi-tui` or `pi-coding-agent` already exports one that fits.
114
114
  18. **Forward abort signals**: Always pass `signal` through to `fetch()`, `pi.exec()`, and API client methods. A tool that ignores its signal prevents cancellation from reaching the underlying operation. Never prefix with `_signal` unless the tool truly has no async work to cancel.
115
115
  19. **Never use Node child_process APIs**: Do not use `child_process.exec`, `execSync`, `spawn`, `spawnSync`, `execFile`, or `execFileSync` to run binaries or shell scripts. Always use `pi.exec()`. `pi.exec` handles CWD, signal propagation, and output capture consistently. The only exception is if you need a long-lived streaming process with stdin/stdout piping that `pi.exec` cannot support — document the reason in code comments.
@@ -8,13 +8,11 @@ Hooks let extensions react to lifecycle events. They are registered with `pi.on(
8
8
 
9
9
  | Event | When | Can Cancel | Payload |
10
10
  |---|---|---|---|
11
- | `session_start` | New session created | No | `{}` |
12
- | `session_switch` | Switched to different session | No | `{ reason: "new" \| "switch" \| "fork" }` |
13
- | `session_before_switch` | Before switching sessions | Yes (`{ cancel: true }`) | `{ reason: "new" \| "switch" \| "fork" }` |
14
- | `session_before_fork` | Before forking a session | Yes (`{ cancel: true }`) | `{}` |
15
- | `session_fork` | After session was forked | No | `{}` |
16
- | `session_shutdown` | Pi is shutting down | No | `{}` |
17
- | `session_before_compact` | Before compaction | Yes (return custom summary string) | `{ summary: string }` |
11
+ | `session_start` | Session starts, reloads, or is replaced | No | `{ reason: "startup" \| "reload" \| "new" \| "resume" \| "fork", previousSessionFile? }` |
12
+ | `session_before_switch` | Before `/new` or `/resume` replaces the current session | Yes (`{ cancel: true }`) | `{ reason: "new" \| "resume", targetSessionFile? }` |
13
+ | `session_before_fork` | Before forking a session | Yes (`{ cancel: true }`) | `{ entryId }` |
14
+ | `session_shutdown` | Current session runtime is shutting down or being replaced | No | `{}` |
15
+ | `session_before_compact` | Before compaction | Yes (cancel or provide custom compaction) | event-specific compaction data |
18
16
 
19
17
  ### Agent Events
20
18
 
@@ -98,8 +96,13 @@ pi.on("session_before_switch", async (event, ctx) => {
98
96
 
99
97
  ```typescript
100
98
  pi.on("session_before_compact", async (event, ctx) => {
101
- // Return a custom summary string to replace the default compaction
102
- return `Custom summary: ${event.summary.slice(0, 200)}...`;
99
+ return {
100
+ compaction: {
101
+ summary: "Custom summary",
102
+ firstKeptEntryId: event.preparation.firstKeptEntryId,
103
+ tokensBefore: event.preparation.tokensBefore,
104
+ },
105
+ };
103
106
  });
104
107
  ```
105
108
 
@@ -1,42 +1,53 @@
1
1
  # State Management
2
2
 
3
- Extensions can persist state in the session history using `appendEntry`. State is reconstructed by replaying entries when a session is loaded.
3
+ Extensions can persist state in the session history. In modern Pi extensions, the usual pattern is to store reconstructible state in tool result `details` and rebuild it from the current branch on `session_start`.
4
4
 
5
- ## appendEntry
5
+ ## Recommended Pattern: Store State in Tool Result Details
6
6
 
7
- Adds an entry to the session conversation. Unlike `sendMessage`, entries from `appendEntry` are explicitly for state tracking and are rendered via the tool result rendering system.
7
+ When a tool changes extension state, return the latest state in `details`. That keeps the state aligned with normal tool history, branching, and reconstruction.
8
8
 
9
9
  ```typescript
10
- pi.appendEntry({
11
- toolName: "todo",
12
- toolCallId: `todo-${Date.now()}`,
13
- input: { action: "add", text: "Buy groceries" },
14
- output: "Added: Buy groceries",
15
- display: true,
16
- details: { items: ["Buy groceries"] },
17
- });
18
- ```
10
+ export default function (pi: ExtensionAPI) {
11
+ let items: string[] = [];
19
12
 
20
- | Field | Type | Description |
21
- |---|---|---|
22
- | `toolName` | `string` | Which tool this entry is associated with. Used for rendering. |
23
- | `toolCallId` | `string` | Unique ID for this entry. |
24
- | `input` | `object` | The "input" shown in the entry (as if the tool was called with these params). |
25
- | `output` | `string` | The text output (what the LLM sees). |
26
- | `display` | `boolean` | Whether to show in TUI. |
27
- | `details` | `object` | Rich data for the tool's `renderResult`. |
13
+ pi.on("session_start", async (_event, ctx) => {
14
+ items = [];
15
+ for (const entry of ctx.sessionManager.getBranch()) {
16
+ if (entry.type === "message" && entry.message.role === "toolResult") {
17
+ if (entry.message.toolName === "todo") {
18
+ items = entry.message.details?.items ?? [];
19
+ }
20
+ }
21
+ }
22
+ });
23
+
24
+ pi.registerTool({
25
+ name: "todo",
26
+ // ...
27
+ async execute() {
28
+ items.push("Buy groceries");
29
+ return {
30
+ content: [{ type: "text", text: "Added todo item" }],
31
+ details: { items: [...items] },
32
+ };
33
+ },
34
+ });
35
+ }
36
+ ```
28
37
 
29
38
  ## Reconstructing State from Session
30
39
 
31
- When a session loads, you can reconstruct state by iterating over existing entries. This is typically done in a `session_start` or `session_switch` handler:
40
+ When a session loads, reconstruct state in `session_start` by iterating over the current branch or full session through `ctx.sessionManager`:
32
41
 
33
42
  ```typescript
34
43
  pi.on("session_start", async (_event, ctx) => {
35
- // Rebuild state from session entries
36
- const entries = ctx.getEntries();
37
- for (const entry of entries) {
38
- if (entry.toolName === "todo" && entry.details) {
39
- todoItems = entry.details.items;
44
+ todoItems = [];
45
+
46
+ for (const entry of ctx.sessionManager.getBranch()) {
47
+ if (entry.type === "message" && entry.message.role === "toolResult") {
48
+ if (entry.message.toolName === "todo") {
49
+ todoItems = entry.message.details?.items ?? [];
50
+ }
40
51
  }
41
52
  }
42
53
  });
@@ -53,4 +64,4 @@ This pattern makes state survive session reloads, forks, and compactions (as lon
53
64
  | Use for | State changes, action logs | Information display, command results |
54
65
  | LLM sees | The `output` field | The `content` field |
55
66
 
56
- Use `appendEntry` when you are tracking state changes that need to be replayed. Use `sendMessage` when you are displaying a one-time result.
67
+ Use tool result `details` when the state naturally belongs to a tool call and should follow normal conversation branching. Use `appendEntry` for extension-specific state/history that does not fit a normal tool result. Use `sendMessage` when you are displaying a one-time result.
@@ -135,10 +135,10 @@ Only include `pi` sub-fields that are actually used. `skills`, `themes`, `prompt
135
135
 
136
136
  **`peerDependencies`**: Declares the minimum pi version required. Pi ships these packages and injects them via jiti at runtime, so extensions never need to install them:
137
137
 
138
- - `@mariozechner/pi-coding-agent` — core types, utilities, `Type` (re-exported from TypeBox)
138
+ - `@mariozechner/pi-coding-agent` — core types, utilities, and extension APIs
139
139
  - `@mariozechner/pi-tui` — TUI components
140
140
  - `@mariozechner/pi-ai` — AI utilities (`StringEnum`, etc.)
141
- - `@sinclair/typebox` — schema definitions (also re-exported from `pi-coding-agent`)
141
+ - `@sinclair/typebox` — schema definitions for tool parameters and related types
142
142
 
143
143
  List any of these you import at runtime in `peerDependencies` as optional peers. This prevents npm from installing duplicate copies when a user installs your extension. Use `>=` with the current version when creating.
144
144