@mariozechner/pi-coding-agent 0.34.1 → 0.35.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.
Files changed (251) hide show
  1. package/CHANGELOG.md +224 -18
  2. package/README.md +233 -105
  3. package/dist/cli/args.d.ts +3 -4
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +13 -18
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/config.d.ts +2 -2
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +3 -3
  10. package/dist/config.js.map +1 -1
  11. package/dist/core/agent-session.d.ts +39 -50
  12. package/dist/core/agent-session.d.ts.map +1 -1
  13. package/dist/core/agent-session.js +166 -197
  14. package/dist/core/agent-session.js.map +1 -1
  15. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  16. package/dist/core/compaction/branch-summarization.js +3 -3
  17. package/dist/core/compaction/branch-summarization.js.map +1 -1
  18. package/dist/core/compaction/compaction.d.ts +1 -1
  19. package/dist/core/compaction/compaction.d.ts.map +1 -1
  20. package/dist/core/compaction/compaction.js +6 -5
  21. package/dist/core/compaction/compaction.js.map +1 -1
  22. package/dist/core/event-bus.d.ts +9 -0
  23. package/dist/core/event-bus.d.ts.map +1 -0
  24. package/dist/core/event-bus.js +25 -0
  25. package/dist/core/event-bus.js.map +1 -0
  26. package/dist/core/exec.d.ts +1 -1
  27. package/dist/core/exec.d.ts.map +1 -1
  28. package/dist/core/exec.js +1 -1
  29. package/dist/core/exec.js.map +1 -1
  30. package/dist/core/extensions/index.d.ts +10 -0
  31. package/dist/core/extensions/index.d.ts.map +1 -0
  32. package/dist/core/extensions/index.js +9 -0
  33. package/dist/core/extensions/index.js.map +1 -0
  34. package/dist/core/extensions/loader.d.ts +21 -0
  35. package/dist/core/extensions/loader.d.ts.map +1 -0
  36. package/dist/core/extensions/loader.js +400 -0
  37. package/dist/core/extensions/loader.js.map +1 -0
  38. package/dist/core/extensions/runner.d.ts +88 -0
  39. package/dist/core/extensions/runner.d.ts.map +1 -0
  40. package/dist/core/{hooks → extensions}/runner.js +52 -141
  41. package/dist/core/extensions/runner.js.map +1 -0
  42. package/dist/core/extensions/types.d.ts +461 -0
  43. package/dist/core/extensions/types.d.ts.map +1 -0
  44. package/dist/core/{hooks → extensions}/types.js +7 -4
  45. package/dist/core/extensions/types.js.map +1 -0
  46. package/dist/core/extensions/wrapper.d.ts +25 -0
  47. package/dist/core/extensions/wrapper.d.ts.map +1 -0
  48. package/dist/core/{hooks/tool-wrapper.js → extensions/wrapper.js} +39 -24
  49. package/dist/core/extensions/wrapper.js.map +1 -0
  50. package/dist/core/index.d.ts +2 -2
  51. package/dist/core/index.d.ts.map +1 -1
  52. package/dist/core/index.js +3 -2
  53. package/dist/core/index.js.map +1 -1
  54. package/dist/core/messages.d.ts +7 -7
  55. package/dist/core/messages.d.ts.map +1 -1
  56. package/dist/core/messages.js +4 -4
  57. package/dist/core/messages.js.map +1 -1
  58. package/dist/core/prompt-templates.d.ts +40 -0
  59. package/dist/core/prompt-templates.d.ts.map +1 -0
  60. package/dist/core/{slash-commands.js → prompt-templates.js} +31 -31
  61. package/dist/core/prompt-templates.js.map +1 -0
  62. package/dist/core/sdk.d.ts +29 -52
  63. package/dist/core/sdk.d.ts.map +1 -1
  64. package/dist/core/sdk.js +111 -211
  65. package/dist/core/sdk.js.map +1 -1
  66. package/dist/core/session-manager.d.ts +17 -17
  67. package/dist/core/session-manager.d.ts.map +1 -1
  68. package/dist/core/session-manager.js +25 -10
  69. package/dist/core/session-manager.js.map +1 -1
  70. package/dist/core/settings-manager.d.ts +3 -6
  71. package/dist/core/settings-manager.d.ts.map +1 -1
  72. package/dist/core/settings-manager.js +4 -11
  73. package/dist/core/settings-manager.js.map +1 -1
  74. package/dist/core/system-prompt.d.ts.map +1 -1
  75. package/dist/core/system-prompt.js +4 -2
  76. package/dist/core/system-prompt.js.map +1 -1
  77. package/dist/index.d.ts +4 -5
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +5 -6
  80. package/dist/index.js.map +1 -1
  81. package/dist/main.d.ts.map +1 -1
  82. package/dist/main.js +36 -33
  83. package/dist/main.js.map +1 -1
  84. package/dist/migrations.d.ts +7 -2
  85. package/dist/migrations.d.ts.map +1 -1
  86. package/dist/migrations.js +93 -4
  87. package/dist/migrations.js.map +1 -1
  88. package/dist/modes/interactive/components/bordered-loader.d.ts +1 -1
  89. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  90. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  91. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  92. package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
  93. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  94. package/dist/modes/interactive/components/branch-summary-message.js +1 -1
  95. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  96. package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
  97. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  98. package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
  99. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  100. package/dist/modes/interactive/components/custom-editor.d.ts +2 -2
  101. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  102. package/dist/modes/interactive/components/custom-editor.js +4 -4
  103. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  104. package/dist/modes/interactive/components/custom-message.d.ts +18 -0
  105. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
  106. package/dist/modes/interactive/components/{hook-message.js → custom-message.js} +3 -3
  107. package/dist/modes/interactive/components/custom-message.js.map +1 -0
  108. package/dist/modes/interactive/components/dynamic-border.d.ts +2 -2
  109. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  110. package/dist/modes/interactive/components/dynamic-border.js +2 -2
  111. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  112. package/dist/modes/interactive/components/{hook-editor.d.ts → extension-editor.d.ts} +3 -3
  113. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
  114. package/dist/modes/interactive/components/{hook-editor.js → extension-editor.js} +4 -4
  115. package/dist/modes/interactive/components/extension-editor.js.map +1 -0
  116. package/dist/modes/interactive/components/{hook-input.d.ts → extension-input.d.ts} +3 -3
  117. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
  118. package/dist/modes/interactive/components/{hook-input.js → extension-input.js} +3 -3
  119. package/dist/modes/interactive/components/extension-input.js.map +1 -0
  120. package/dist/modes/interactive/components/{hook-selector.d.ts → extension-selector.d.ts} +3 -3
  121. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
  122. package/dist/modes/interactive/components/{hook-selector.js → extension-selector.js} +3 -3
  123. package/dist/modes/interactive/components/extension-selector.js.map +1 -0
  124. package/dist/modes/interactive/components/footer.d.ts +3 -3
  125. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  126. package/dist/modes/interactive/components/footer.js +8 -8
  127. package/dist/modes/interactive/components/footer.js.map +1 -1
  128. package/dist/modes/interactive/components/tool-execution.d.ts +3 -3
  129. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  130. package/dist/modes/interactive/components/tool-execution.js +9 -9
  131. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  132. package/dist/modes/interactive/interactive-mode.d.ts +37 -44
  133. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  134. package/dist/modes/interactive/interactive-mode.js +143 -189
  135. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  136. package/dist/modes/print-mode.d.ts.map +1 -1
  137. package/dist/modes/print-mode.js +10 -33
  138. package/dist/modes/print-mode.js.map +1 -1
  139. package/dist/modes/rpc/rpc-client.d.ts +3 -3
  140. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  141. package/dist/modes/rpc/rpc-client.js +3 -3
  142. package/dist/modes/rpc/rpc-client.js.map +1 -1
  143. package/dist/modes/rpc/rpc-mode.d.ts +2 -2
  144. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  145. package/dist/modes/rpc/rpc-mode.js +33 -57
  146. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  147. package/dist/modes/rpc/rpc-types.d.ts +16 -16
  148. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  149. package/dist/modes/rpc/rpc-types.js.map +1 -1
  150. package/docs/extensions.md +1053 -0
  151. package/docs/rpc.md +4 -4
  152. package/docs/sdk.md +62 -93
  153. package/docs/session.md +22 -19
  154. package/docs/skills.md +1 -1
  155. package/docs/tui.md +1 -1
  156. package/examples/README.md +9 -15
  157. package/examples/extensions/README.md +141 -0
  158. package/examples/{hooks → extensions}/auto-commit-on-exit.ts +3 -3
  159. package/examples/extensions/chalk-logger.ts +26 -0
  160. package/examples/{hooks → extensions}/confirm-destructive.ts +3 -3
  161. package/examples/{hooks → extensions}/custom-compaction.ts +6 -6
  162. package/examples/{hooks → extensions}/dirty-repo-guard.ts +8 -4
  163. package/examples/{hooks → extensions}/file-trigger.ts +3 -3
  164. package/examples/{hooks → extensions}/git-checkpoint.ts +3 -3
  165. package/examples/{hooks → extensions}/handoff.ts +3 -3
  166. package/examples/extensions/hello.ts +25 -0
  167. package/examples/{hooks → extensions}/permission-gate.ts +3 -3
  168. package/examples/{hooks → extensions}/pirate.ts +5 -5
  169. package/examples/{hooks → extensions}/plan-mode.ts +6 -6
  170. package/examples/{hooks → extensions}/protected-paths.ts +3 -3
  171. package/examples/{hooks → extensions}/qna.ts +3 -3
  172. package/examples/{custom-tools/question/index.ts → extensions/question.ts} +13 -17
  173. package/examples/{hooks → extensions}/snake.ts +3 -3
  174. package/examples/{hooks → extensions}/status-line.ts +3 -3
  175. package/examples/{custom-tools → extensions}/subagent/README.md +15 -15
  176. package/examples/{custom-tools → extensions}/subagent/index.ts +22 -43
  177. package/examples/{custom-tools/todo/index.ts → extensions/todo.ts} +122 -39
  178. package/examples/{hooks → extensions}/tools.ts +5 -5
  179. package/examples/extensions/with-deps/index.ts +40 -0
  180. package/examples/extensions/with-deps/package-lock.json +31 -0
  181. package/examples/extensions/with-deps/package.json +16 -0
  182. package/examples/sdk/01-minimal.ts +1 -1
  183. package/examples/sdk/05-tools.ts +7 -41
  184. package/examples/sdk/06-extensions.ts +81 -0
  185. package/examples/sdk/08-prompt-templates.ts +42 -0
  186. package/examples/sdk/12-full-control.ts +10 -29
  187. package/examples/sdk/README.md +5 -5
  188. package/package.json +4 -4
  189. package/dist/core/custom-tools/index.d.ts +0 -7
  190. package/dist/core/custom-tools/index.d.ts.map +0 -1
  191. package/dist/core/custom-tools/index.js +0 -6
  192. package/dist/core/custom-tools/index.js.map +0 -1
  193. package/dist/core/custom-tools/loader.d.ts +0 -30
  194. package/dist/core/custom-tools/loader.d.ts.map +0 -1
  195. package/dist/core/custom-tools/loader.js +0 -276
  196. package/dist/core/custom-tools/loader.js.map +0 -1
  197. package/dist/core/custom-tools/types.d.ts +0 -144
  198. package/dist/core/custom-tools/types.d.ts.map +0 -1
  199. package/dist/core/custom-tools/types.js +0 -8
  200. package/dist/core/custom-tools/types.js.map +0 -1
  201. package/dist/core/custom-tools/wrapper.d.ts +0 -15
  202. package/dist/core/custom-tools/wrapper.d.ts.map +0 -1
  203. package/dist/core/custom-tools/wrapper.js +0 -23
  204. package/dist/core/custom-tools/wrapper.js.map +0 -1
  205. package/dist/core/hooks/index.d.ts +0 -6
  206. package/dist/core/hooks/index.d.ts.map +0 -1
  207. package/dist/core/hooks/index.js +0 -6
  208. package/dist/core/hooks/index.js.map +0 -1
  209. package/dist/core/hooks/loader.d.ts +0 -146
  210. package/dist/core/hooks/loader.d.ts.map +0 -1
  211. package/dist/core/hooks/loader.js +0 -275
  212. package/dist/core/hooks/loader.js.map +0 -1
  213. package/dist/core/hooks/runner.d.ts +0 -173
  214. package/dist/core/hooks/runner.d.ts.map +0 -1
  215. package/dist/core/hooks/runner.js.map +0 -1
  216. package/dist/core/hooks/tool-wrapper.d.ts +0 -17
  217. package/dist/core/hooks/tool-wrapper.d.ts.map +0 -1
  218. package/dist/core/hooks/tool-wrapper.js.map +0 -1
  219. package/dist/core/hooks/types.d.ts +0 -767
  220. package/dist/core/hooks/types.d.ts.map +0 -1
  221. package/dist/core/hooks/types.js.map +0 -1
  222. package/dist/core/slash-commands.d.ts +0 -40
  223. package/dist/core/slash-commands.d.ts.map +0 -1
  224. package/dist/core/slash-commands.js.map +0 -1
  225. package/dist/modes/interactive/components/hook-editor.d.ts.map +0 -1
  226. package/dist/modes/interactive/components/hook-editor.js.map +0 -1
  227. package/dist/modes/interactive/components/hook-input.d.ts.map +0 -1
  228. package/dist/modes/interactive/components/hook-input.js.map +0 -1
  229. package/dist/modes/interactive/components/hook-message.d.ts +0 -18
  230. package/dist/modes/interactive/components/hook-message.d.ts.map +0 -1
  231. package/dist/modes/interactive/components/hook-message.js.map +0 -1
  232. package/dist/modes/interactive/components/hook-selector.d.ts.map +0 -1
  233. package/dist/modes/interactive/components/hook-selector.js.map +0 -1
  234. package/docs/custom-tools.md +0 -514
  235. package/docs/extension-loading.md +0 -1004
  236. package/docs/hooks.md +0 -979
  237. package/docs/session-tree-plan.md +0 -441
  238. package/examples/custom-tools/README.md +0 -114
  239. package/examples/custom-tools/hello/index.ts +0 -21
  240. package/examples/hooks/README.md +0 -60
  241. package/examples/hooks/todo/index.ts +0 -134
  242. package/examples/sdk/06-hooks.ts +0 -61
  243. package/examples/sdk/08-slash-commands.ts +0 -42
  244. /package/examples/{custom-tools → extensions}/subagent/agents/planner.md +0 -0
  245. /package/examples/{custom-tools → extensions}/subagent/agents/reviewer.md +0 -0
  246. /package/examples/{custom-tools → extensions}/subagent/agents/scout.md +0 -0
  247. /package/examples/{custom-tools → extensions}/subagent/agents/worker.md +0 -0
  248. /package/examples/{custom-tools → extensions}/subagent/agents.ts +0 -0
  249. /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/implement-and-review.md +0 -0
  250. /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/implement.md +0 -0
  251. /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/scout-and-plan.md +0 -0
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Example extension that uses a 3rd party dependency (chalk).
3
+ * Tests that jiti can resolve npm modules correctly.
4
+ */
5
+
6
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
7
+ import chalk from "chalk";
8
+
9
+ export default function (pi: ExtensionAPI) {
10
+ // Log with colors using chalk
11
+ console.log(`${chalk.green("✓")} ${chalk.bold("chalk-logger extension loaded")}`);
12
+
13
+ pi.on("agent_start", async () => {
14
+ console.log(`${chalk.blue("[chalk-logger]")} Agent starting`);
15
+ });
16
+
17
+ pi.on("tool_call", async (event) => {
18
+ console.log(`${chalk.yellow("[chalk-logger]")} Tool: ${chalk.cyan(event.toolName)}`);
19
+ return undefined;
20
+ });
21
+
22
+ pi.on("agent_end", async (event) => {
23
+ const count = event.messages.length;
24
+ console.log(`${chalk.green("[chalk-logger]")} Done with ${chalk.bold(String(count))} messages`);
25
+ });
26
+ }
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Confirm Destructive Actions Hook
2
+ * Confirm Destructive Actions Extension
3
3
  *
4
4
  * Prompts for confirmation before destructive session actions (clear, switch, branch).
5
5
  * Demonstrates how to cancel session events using the before_* events.
6
6
  */
7
7
 
8
- import type { HookAPI, SessionBeforeSwitchEvent, SessionMessageEntry } from "@mariozechner/pi-coding-agent";
8
+ import type { ExtensionAPI, SessionBeforeSwitchEvent, SessionMessageEntry } from "@mariozechner/pi-coding-agent";
9
9
 
10
- export default function (pi: HookAPI) {
10
+ export default function (pi: ExtensionAPI) {
11
11
  pi.on("session_before_switch", async (event: SessionBeforeSwitchEvent, ctx) => {
12
12
  if (!ctx.hasUI) return;
13
13
 
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Custom Compaction Hook
2
+ * Custom Compaction Extension
3
3
  *
4
4
  * Replaces the default compaction behavior with a full summary of the entire context.
5
- * Instead of keeping the last 20k tokens of conversation turns, this hook:
5
+ * Instead of keeping the last 20k tokens of conversation turns, this extension:
6
6
  * 1. Summarizes ALL messages (messagesToSummarize + turnPrefixMessages)
7
7
  * 2. Discards all old turns completely, keeping only the summary
8
8
  *
@@ -10,16 +10,16 @@
10
10
  * which can be cheaper/faster than the main conversation model.
11
11
  *
12
12
  * Usage:
13
- * pi --hook examples/hooks/custom-compaction.ts
13
+ * pi --extension examples/extensions/custom-compaction.ts
14
14
  */
15
15
 
16
16
  import { complete, getModel } from "@mariozechner/pi-ai";
17
- import type { HookAPI } from "@mariozechner/pi-coding-agent";
17
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
18
18
  import { convertToLlm, serializeConversation } from "@mariozechner/pi-coding-agent";
19
19
 
20
- export default function (pi: HookAPI) {
20
+ export default function (pi: ExtensionAPI) {
21
21
  pi.on("session_before_compact", async (event, ctx) => {
22
- ctx.ui.notify("Custom compaction hook triggered", "info");
22
+ ctx.ui.notify("Custom compaction extension triggered", "info");
23
23
 
24
24
  const { preparation, branchEntries: _, signal } = event;
25
25
  const { messagesToSummarize, turnPrefixMessages, tokensBefore, firstKeptEntryId, previousSummary } = preparation;
@@ -1,13 +1,17 @@
1
1
  /**
2
- * Dirty Repo Guard Hook
2
+ * Dirty Repo Guard Extension
3
3
  *
4
4
  * Prevents session changes when there are uncommitted git changes.
5
5
  * Useful to ensure work is committed before switching context.
6
6
  */
7
7
 
8
- import type { HookAPI, HookContext } from "@mariozechner/pi-coding-agent";
8
+ import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
9
9
 
10
- async function checkDirtyRepo(pi: HookAPI, ctx: HookContext, action: string): Promise<{ cancel: boolean } | undefined> {
10
+ async function checkDirtyRepo(
11
+ pi: ExtensionAPI,
12
+ ctx: ExtensionContext,
13
+ action: string,
14
+ ): Promise<{ cancel: boolean } | undefined> {
11
15
  // Check for uncommitted changes
12
16
  const { stdout, code } = await pi.exec("git", ["status", "--porcelain"]);
13
17
 
@@ -40,7 +44,7 @@ async function checkDirtyRepo(pi: HookAPI, ctx: HookContext, action: string): Pr
40
44
  }
41
45
  }
42
46
 
43
- export default function (pi: HookAPI) {
47
+ export default function (pi: ExtensionAPI) {
44
48
  pi.on("session_before_switch", async (event, ctx) => {
45
49
  const action = event.reason === "new" ? "new session" : "switch session";
46
50
  return checkDirtyRepo(pi, ctx, action);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * File Trigger Hook
2
+ * File Trigger Extension
3
3
  *
4
4
  * Watches a trigger file and injects its contents into the conversation.
5
5
  * Useful for external systems to send messages to the agent.
@@ -9,9 +9,9 @@
9
9
  */
10
10
 
11
11
  import * as fs from "node:fs";
12
- import type { HookAPI } from "@mariozechner/pi-coding-agent";
12
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
13
13
 
14
- export default function (pi: HookAPI) {
14
+ export default function (pi: ExtensionAPI) {
15
15
  pi.on("session_start", async (_event, ctx) => {
16
16
  const triggerFile = "/tmp/agent-trigger.txt";
17
17
 
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Git Checkpoint Hook
2
+ * Git Checkpoint Extension
3
3
  *
4
4
  * Creates git stash checkpoints at each turn so /branch can restore code state.
5
5
  * When branching, offers to restore code to that point in history.
6
6
  */
7
7
 
8
- import type { HookAPI } from "@mariozechner/pi-coding-agent";
8
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
9
9
 
10
- export default function (pi: HookAPI) {
10
+ export default function (pi: ExtensionAPI) {
11
11
  const checkpoints = new Map<string, string>();
12
12
  let currentEntryId: string | undefined;
13
13
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Handoff hook - transfer context to a new focused session
2
+ * Handoff extension - transfer context to a new focused session
3
3
  *
4
4
  * Instead of compacting (which is lossy), handoff extracts what matters
5
5
  * for your next task and creates a new session with a generated prompt.
@@ -13,7 +13,7 @@
13
13
  */
14
14
 
15
15
  import { complete, type Message } from "@mariozechner/pi-ai";
16
- import type { HookAPI, SessionEntry } from "@mariozechner/pi-coding-agent";
16
+ import type { ExtensionAPI, SessionEntry } from "@mariozechner/pi-coding-agent";
17
17
  import { BorderedLoader, convertToLlm, serializeConversation } from "@mariozechner/pi-coding-agent";
18
18
 
19
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:
@@ -38,7 +38,7 @@ Files involved:
38
38
  ## Task
39
39
  [Clear description of what to do next based on user's goal]`;
40
40
 
41
- export default function (pi: HookAPI) {
41
+ export default function (pi: ExtensionAPI) {
42
42
  pi.registerCommand("handoff", {
43
43
  description: "Transfer context to a new focused session",
44
44
  handler: async (args, ctx) => {
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Hello Tool - Minimal custom tool example
3
+ */
4
+
5
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
6
+ import { Type } from "@sinclair/typebox";
7
+
8
+ export default function (pi: ExtensionAPI) {
9
+ pi.registerTool({
10
+ name: "hello",
11
+ label: "Hello",
12
+ description: "A simple greeting tool",
13
+ parameters: Type.Object({
14
+ name: Type.String({ description: "Name to greet" }),
15
+ }),
16
+
17
+ async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
18
+ const { name } = params as { name: string };
19
+ return {
20
+ content: [{ type: "text", text: `Hello, ${name}!` }],
21
+ details: { greeted: name },
22
+ };
23
+ },
24
+ });
25
+ }
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Permission Gate Hook
2
+ * Permission Gate Extension
3
3
  *
4
4
  * Prompts for confirmation before running potentially dangerous bash commands.
5
5
  * Patterns checked: rm -rf, sudo, chmod/chown 777
6
6
  */
7
7
 
8
- import type { HookAPI } from "@mariozechner/pi-coding-agent";
8
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
9
9
 
10
- export default function (pi: HookAPI) {
10
+ export default function (pi: ExtensionAPI) {
11
11
  const dangerousPatterns = [/\brm\s+(-rf?|--recursive)/i, /\bsudo\b/i, /\b(chmod|chown)\b.*777/i];
12
12
 
13
13
  pi.on("tool_call", async (event, ctx) => {
@@ -1,18 +1,18 @@
1
1
  /**
2
- * Pirate Hook
2
+ * Pirate Extension
3
3
  *
4
4
  * Demonstrates using systemPromptAppend in before_agent_start to dynamically
5
- * modify the system prompt based on hook state.
5
+ * modify the system prompt based on extension state.
6
6
  *
7
7
  * Usage:
8
- * 1. Copy this file to ~/.pi/agent/hooks/ or your project's .pi/hooks/
8
+ * 1. Copy this file to ~/.pi/agent/extensions/ or your project's .pi/extensions/
9
9
  * 2. Use /pirate to toggle pirate mode
10
10
  * 3. When enabled, the agent will respond like a pirate
11
11
  */
12
12
 
13
- import type { HookAPI } from "@mariozechner/pi-coding-agent/hooks";
13
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
14
14
 
15
- export default function pirateHook(pi: HookAPI) {
15
+ export default function pirateExtension(pi: ExtensionAPI) {
16
16
  let pirateMode = false;
17
17
 
18
18
  // Register /pirate command to toggle pirate mode
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Plan Mode Hook
2
+ * Plan Mode Extension
3
3
  *
4
4
  * Provides a Claude Code-style "plan mode" for safe code exploration.
5
5
  * When enabled, the agent can only use read-only tools and cannot modify files.
@@ -14,12 +14,12 @@
14
14
  * - Uses ID-based tracking: agent outputs [DONE:id] to mark steps complete
15
15
  *
16
16
  * Usage:
17
- * 1. Copy this file to ~/.pi/agent/hooks/ or your project's .pi/hooks/
17
+ * 1. Copy this file to ~/.pi/agent/extensions/ or your project's .pi/extensions/
18
18
  * 2. Use /plan to toggle plan mode on/off
19
19
  * 3. Or start in plan mode with --plan flag
20
20
  */
21
21
 
22
- import type { HookAPI, HookContext } from "@mariozechner/pi-coding-agent/hooks";
22
+ import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
23
23
  import { Key } from "@mariozechner/pi-tui";
24
24
 
25
25
  // Read-only tools for plan mode
@@ -207,7 +207,7 @@ function extractTodoItems(message: string): TodoItem[] {
207
207
  return items;
208
208
  }
209
209
 
210
- export default function planModeHook(pi: HookAPI) {
210
+ export default function planModeExtension(pi: ExtensionAPI) {
211
211
  let planModeEnabled = false;
212
212
  let toolsCalledThisTurn = false;
213
213
  let executionMode = false;
@@ -221,7 +221,7 @@ export default function planModeHook(pi: HookAPI) {
221
221
  });
222
222
 
223
223
  // Helper to update status displays
224
- function updateStatus(ctx: HookContext) {
224
+ function updateStatus(ctx: ExtensionContext) {
225
225
  if (executionMode && todoItems.length > 0) {
226
226
  const completed = todoItems.filter((t) => t.completed).length;
227
227
  ctx.ui.setStatus("plan-mode", ctx.ui.theme.fg("accent", `📋 ${completed}/${todoItems.length}`));
@@ -249,7 +249,7 @@ export default function planModeHook(pi: HookAPI) {
249
249
  }
250
250
  }
251
251
 
252
- function togglePlanMode(ctx: HookContext) {
252
+ function togglePlanMode(ctx: ExtensionContext) {
253
253
  planModeEnabled = !planModeEnabled;
254
254
  executionMode = false;
255
255
  todoItems = [];
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Protected Paths Hook
2
+ * Protected Paths Extension
3
3
  *
4
4
  * Blocks write and edit operations to protected paths.
5
5
  * Useful for preventing accidental modifications to sensitive files.
6
6
  */
7
7
 
8
- import type { HookAPI } from "@mariozechner/pi-coding-agent";
8
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
9
9
 
10
- export default function (pi: HookAPI) {
10
+ export default function (pi: ExtensionAPI) {
11
11
  const protectedPaths = [".env", ".git/", "node_modules/"];
12
12
 
13
13
  pi.on("tool_call", async (event, ctx) => {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Q&A extraction hook - extracts questions from assistant responses
2
+ * Q&A extraction extension - extracts questions from assistant responses
3
3
  *
4
4
  * Demonstrates the "prompt generator" pattern:
5
5
  * 1. /qna command gets the last assistant message
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import { complete, type UserMessage } from "@mariozechner/pi-ai";
11
- import type { HookAPI } from "@mariozechner/pi-coding-agent";
11
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
12
12
  import { BorderedLoader } from "@mariozechner/pi-coding-agent";
13
13
 
14
14
  const SYSTEM_PROMPT = `You are a question extractor. Given text from a conversation, extract any questions that need answering and format them for the user to fill in.
@@ -27,7 +27,7 @@ A:
27
27
 
28
28
  Keep questions in the order they appeared. Be concise.`;
29
29
 
30
- export default function (pi: HookAPI) {
30
+ export default function (pi: ExtensionAPI) {
31
31
  pi.registerCommand("qna", {
32
32
  description: "Extract questions from last assistant message into editor",
33
33
  handler: async (_args, ctx) => {
@@ -2,7 +2,7 @@
2
2
  * Question Tool - Let the LLM ask the user a question with options
3
3
  */
4
4
 
5
- import type { CustomTool, CustomToolFactory } from "@mariozechner/pi-coding-agent";
5
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
6
6
  import { Text } from "@mariozechner/pi-tui";
7
7
  import { Type } from "@sinclair/typebox";
8
8
 
@@ -17,40 +17,40 @@ const QuestionParams = Type.Object({
17
17
  options: Type.Array(Type.String(), { description: "Options for the user to choose from" }),
18
18
  });
19
19
 
20
- const factory: CustomToolFactory = (pi) => {
21
- const tool: CustomTool<typeof QuestionParams, QuestionDetails> = {
20
+ export default function (pi: ExtensionAPI) {
21
+ pi.registerTool({
22
22
  name: "question",
23
23
  label: "Question",
24
24
  description: "Ask the user a question and let them pick from options. Use when you need user input to proceed.",
25
25
  parameters: QuestionParams,
26
26
 
27
- async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
28
- if (!pi.hasUI) {
27
+ async execute(_toolCallId, params, _onUpdate, ctx, _signal) {
28
+ if (!ctx.hasUI) {
29
29
  return {
30
30
  content: [{ type: "text", text: "Error: UI not available (running in non-interactive mode)" }],
31
- details: { question: params.question, options: params.options, answer: null },
31
+ details: { question: params.question, options: params.options, answer: null } as QuestionDetails,
32
32
  };
33
33
  }
34
34
 
35
35
  if (params.options.length === 0) {
36
36
  return {
37
37
  content: [{ type: "text", text: "Error: No options provided" }],
38
- details: { question: params.question, options: [], answer: null },
38
+ details: { question: params.question, options: [], answer: null } as QuestionDetails,
39
39
  };
40
40
  }
41
41
 
42
- const answer = await pi.ui.select(params.question, params.options);
42
+ const answer = await ctx.ui.select(params.question, params.options);
43
43
 
44
44
  if (answer === undefined) {
45
45
  return {
46
46
  content: [{ type: "text", text: "User cancelled the selection" }],
47
- details: { question: params.question, options: params.options, answer: null },
47
+ details: { question: params.question, options: params.options, answer: null } as QuestionDetails,
48
48
  };
49
49
  }
50
50
 
51
51
  return {
52
52
  content: [{ type: "text", text: `User selected: ${answer}` }],
53
- details: { question: params.question, options: params.options, answer },
53
+ details: { question: params.question, options: params.options, answer } as QuestionDetails,
54
54
  };
55
55
  },
56
56
 
@@ -63,7 +63,7 @@ const factory: CustomToolFactory = (pi) => {
63
63
  },
64
64
 
65
65
  renderResult(result, _options, theme) {
66
- const { details } = result;
66
+ const details = result.details as QuestionDetails | undefined;
67
67
  if (!details) {
68
68
  const text = result.content[0];
69
69
  return new Text(text?.type === "text" ? text.text : "", 0, 0);
@@ -75,9 +75,5 @@ const factory: CustomToolFactory = (pi) => {
75
75
 
76
76
  return new Text(theme.fg("success", "✓ ") + theme.fg("accent", details.answer), 0, 0);
77
77
  },
78
- };
79
-
80
- return tool;
81
- };
82
-
83
- export default factory;
78
+ });
79
+ }
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Snake game hook - play snake with /snake command
2
+ * Snake game extension - play snake with /snake command
3
3
  */
4
4
 
5
- import type { HookAPI } from "@mariozechner/pi-coding-agent";
5
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
6
6
  import { matchesKey, visibleWidth } from "@mariozechner/pi-tui";
7
7
 
8
8
  const GAME_WIDTH = 40;
@@ -306,7 +306,7 @@ class SnakeComponent {
306
306
 
307
307
  const SNAKE_SAVE_TYPE = "snake-save";
308
308
 
309
- export default function (pi: HookAPI) {
309
+ export default function (pi: ExtensionAPI) {
310
310
  pi.registerCommand("snake", {
311
311
  description: "Play Snake!",
312
312
 
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Status Line Hook
2
+ * Status Line Extension
3
3
  *
4
4
  * Demonstrates ctx.ui.setStatus() for displaying persistent status text in the footer.
5
5
  * Shows turn progress with themed colors.
6
6
  */
7
7
 
8
- import type { HookAPI } from "@mariozechner/pi-coding-agent";
8
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
9
9
 
10
- export default function (pi: HookAPI) {
10
+ export default function (pi: ExtensionAPI) {
11
11
  let turnCount = 0;
12
12
 
13
13
  pi.on("session_start", async (_event, ctx) => {
@@ -16,14 +16,14 @@ Delegate tasks to specialized subagents with isolated context windows.
16
16
  ```
17
17
  subagent/
18
18
  ├── README.md # This file
19
- ├── index.ts # The custom tool (entry point)
19
+ ├── index.ts # The extension (entry point)
20
20
  ├── agents.ts # Agent discovery logic
21
21
  ├── agents/ # Sample agent definitions
22
22
  │ ├── scout.md # Fast recon, returns compressed context
23
23
  │ ├── planner.md # Creates implementation plans
24
24
  │ ├── reviewer.md # Code review
25
25
  │ └── worker.md # General-purpose (full capabilities)
26
- └── commands/ # Workflow presets
26
+ └── prompts/ # Workflow presets (prompt templates)
27
27
  ├── implement.md # scout -> planner -> worker
28
28
  ├── scout-and-plan.md # scout -> planner (no implementation)
29
29
  └── implement-and-review.md # worker -> reviewer -> worker
@@ -34,21 +34,21 @@ subagent/
34
34
  From the repository root, symlink the files:
35
35
 
36
36
  ```bash
37
- # Symlink the tool (must be in a subdirectory with index.ts)
38
- mkdir -p ~/.pi/agent/tools/subagent
39
- ln -sf "$(pwd)/packages/coding-agent/examples/custom-tools/subagent/index.ts" ~/.pi/agent/tools/subagent/index.ts
40
- ln -sf "$(pwd)/packages/coding-agent/examples/custom-tools/subagent/agents.ts" ~/.pi/agent/tools/subagent/agents.ts
37
+ # Symlink the extension (must be in a subdirectory with index.ts)
38
+ mkdir -p ~/.pi/agent/extensions/subagent
39
+ ln -sf "$(pwd)/packages/coding-agent/examples/extensions/subagent/index.ts" ~/.pi/agent/extensions/subagent/index.ts
40
+ ln -sf "$(pwd)/packages/coding-agent/examples/extensions/subagent/agents.ts" ~/.pi/agent/extensions/subagent/agents.ts
41
41
 
42
42
  # Symlink agents
43
43
  mkdir -p ~/.pi/agent/agents
44
- for f in packages/coding-agent/examples/custom-tools/subagent/agents/*.md; do
44
+ for f in packages/coding-agent/examples/extensions/subagent/agents/*.md; do
45
45
  ln -sf "$(pwd)/$f" ~/.pi/agent/agents/$(basename "$f")
46
46
  done
47
47
 
48
- # Symlink workflow commands
49
- mkdir -p ~/.pi/agent/commands
50
- for f in packages/coding-agent/examples/custom-tools/subagent/commands/*.md; do
51
- ln -sf "$(pwd)/$f" ~/.pi/agent/commands/$(basename "$f")
48
+ # Symlink workflow prompts
49
+ mkdir -p ~/.pi/agent/prompts
50
+ for f in packages/coding-agent/examples/extensions/subagent/prompts/*.md; do
51
+ ln -sf "$(pwd)/$f" ~/.pi/agent/prompts/$(basename "$f")
52
52
  done
53
53
  ```
54
54
 
@@ -81,7 +81,7 @@ Run 2 scouts in parallel: one to find models, one to find providers
81
81
  Use a chain: first have scout find the read tool, then have planner suggest improvements
82
82
  ```
83
83
 
84
- ### Workflow commands
84
+ ### Workflow prompts
85
85
  ```
86
86
  /implement add Redis caching to the session store
87
87
  /scout-and-plan refactor auth to support OAuth
@@ -150,10 +150,10 @@ Project agents override user agents with the same name when `agentScope: "both"`
150
150
  | `reviewer` | Code review | Sonnet | read, grep, find, ls, bash |
151
151
  | `worker` | General-purpose | Sonnet | (all default) |
152
152
 
153
- ## Workflow Commands
153
+ ## Workflow Prompts
154
154
 
155
- | Command | Flow |
156
- |---------|------|
155
+ | Prompt | Flow |
156
+ |--------|------|
157
157
  | `/implement <query>` | scout → planner → worker |
158
158
  | `/scout-and-plan <query>` | scout → planner |
159
159
  | `/implement-and-review <query>` | worker → reviewer → worker |