@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
package/dist/core/sdk.js CHANGED
@@ -9,20 +9,11 @@
9
9
  * // Minimal - everything auto-discovered
10
10
  * const session = await createAgentSession();
11
11
  *
12
- * // With custom hooks
13
- * const session = await createAgentSession({
14
- * hooks: [
15
- * ...await discoverHooks(),
16
- * { factory: myHookFactory },
17
- * ],
18
- * });
19
- *
20
12
  * // Full control
21
13
  * const session = await createAgentSession({
22
14
  * model: myModel,
23
15
  * getApiKey: async () => process.env.MY_KEY,
24
16
  * tools: [readTool, bashTool],
25
- * hooks: [],
26
17
  * skills: [],
27
18
  * sessionFile: false,
28
19
  * });
@@ -33,14 +24,14 @@ import { join } from "path";
33
24
  import { getAgentDir } from "../config.js";
34
25
  import { AgentSession } from "./agent-session.js";
35
26
  import { AuthStorage } from "./auth-storage.js";
36
- import { discoverAndLoadCustomTools, wrapCustomTools, } from "./custom-tools/index.js";
37
- import { discoverAndLoadHooks, HookRunner, wrapToolsWithHooks } from "./hooks/index.js";
27
+ import { createEventBus } from "./event-bus.js";
28
+ import { discoverAndLoadExtensions, ExtensionRunner, loadExtensionFromFactory, wrapRegisteredTools, wrapToolsWithExtensions, } from "./extensions/index.js";
38
29
  import { convertToLlm } from "./messages.js";
39
30
  import { ModelRegistry } from "./model-registry.js";
31
+ import { loadPromptTemplates as loadPromptTemplatesInternal } from "./prompt-templates.js";
40
32
  import { SessionManager } from "./session-manager.js";
41
33
  import { SettingsManager } from "./settings-manager.js";
42
34
  import { loadSkills as loadSkillsInternal } from "./skills.js";
43
- import { loadSlashCommands as loadSlashCommandsInternal } from "./slash-commands.js";
44
35
  import { buildSystemPrompt as buildSystemPromptInternal, loadProjectContextFiles as loadContextFilesInternal, } from "./system-prompt.js";
45
36
  import { time } from "./timings.js";
46
37
  import { allTools, bashTool, codingTools, createAllTools, createBashTool, createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createReadOnlyTools, createReadTool, createWriteTool, editTool, findTool, grepTool, lsTool, readOnlyTools, readTool, writeTool, } from "./tools/index.js";
@@ -67,36 +58,20 @@ export function discoverModels(authStorage, agentDir = getDefaultAgentDir()) {
67
58
  return new ModelRegistry(authStorage, join(agentDir, "models.json"));
68
59
  }
69
60
  /**
70
- * Discover hooks from cwd and agentDir.
61
+ * Discover extensions from cwd and agentDir.
62
+ * @param eventBus - Shared event bus for extension communication. Pass to createAgentSession too.
63
+ * @param cwd - Current working directory
64
+ * @param agentDir - Agent configuration directory
71
65
  */
72
- export async function discoverHooks(cwd, agentDir) {
66
+ export async function discoverExtensions(eventBus, cwd, agentDir) {
73
67
  const resolvedCwd = cwd ?? process.cwd();
74
68
  const resolvedAgentDir = agentDir ?? getDefaultAgentDir();
75
- const { hooks, errors } = await discoverAndLoadHooks([], resolvedCwd, resolvedAgentDir);
69
+ const result = await discoverAndLoadExtensions([], resolvedCwd, resolvedAgentDir, eventBus);
76
70
  // Log errors but don't fail
77
- for (const { path, error } of errors) {
78
- console.error(`Failed to load hook "${path}": ${error}`);
71
+ for (const { path, error } of result.errors) {
72
+ console.error(`Failed to load extension "${path}": ${error}`);
79
73
  }
80
- return hooks.map((h) => ({
81
- path: h.path,
82
- factory: createFactoryFromLoadedHook(h),
83
- }));
84
- }
85
- /**
86
- * Discover custom tools from cwd and agentDir.
87
- */
88
- export async function discoverCustomTools(cwd, agentDir) {
89
- const resolvedCwd = cwd ?? process.cwd();
90
- const resolvedAgentDir = agentDir ?? getDefaultAgentDir();
91
- const { tools, errors } = await discoverAndLoadCustomTools([], resolvedCwd, Object.keys(allTools), resolvedAgentDir);
92
- // Log errors but don't fail
93
- for (const { path, error } of errors) {
94
- console.error(`Failed to load custom tool "${path}": ${error}`);
95
- }
96
- return tools.map((t) => ({
97
- path: t.path,
98
- tool: t.tool,
99
- }));
74
+ return result;
100
75
  }
101
76
  /**
102
77
  * Discover skills from cwd and agentDir.
@@ -119,10 +94,10 @@ export function discoverContextFiles(cwd, agentDir) {
119
94
  });
120
95
  }
121
96
  /**
122
- * Discover slash commands from cwd and agentDir.
97
+ * Discover prompt templates from cwd and agentDir.
123
98
  */
124
- export function discoverSlashCommands(cwd, agentDir) {
125
- return loadSlashCommandsInternal({
99
+ export function discoverPromptTemplates(cwd, agentDir) {
100
+ return loadPromptTemplatesInternal({
126
101
  cwd: cwd ?? process.cwd(),
127
102
  agentDir: agentDir ?? getDefaultAgentDir(),
128
103
  });
@@ -156,123 +131,11 @@ export function loadSettings(cwd, agentDir) {
156
131
  hideThinkingBlock: manager.getHideThinkingBlock(),
157
132
  shellPath: manager.getShellPath(),
158
133
  collapseChangelog: manager.getCollapseChangelog(),
159
- hooks: manager.getHookPaths(),
160
- customTools: manager.getCustomToolPaths(),
134
+ extensions: manager.getExtensionPaths(),
161
135
  skills: manager.getSkillsSettings(),
162
136
  terminal: { showImages: manager.getShowImages() },
163
137
  };
164
138
  }
165
- // Internal Helpers
166
- /**
167
- * Create a HookFactory from a LoadedHook.
168
- * This allows mixing discovered hooks with inline hooks.
169
- */
170
- function createFactoryFromLoadedHook(loaded) {
171
- return (api) => {
172
- for (const [eventType, handlers] of loaded.handlers) {
173
- for (const handler of handlers) {
174
- api.on(eventType, handler);
175
- }
176
- }
177
- };
178
- }
179
- /**
180
- * Convert hook definitions to LoadedHooks for the HookRunner.
181
- */
182
- function createLoadedHooksFromDefinitions(definitions) {
183
- return definitions.map((def) => {
184
- const hookPath = def.path ?? "<inline>";
185
- const handlers = new Map();
186
- const messageRenderers = new Map();
187
- const commands = new Map();
188
- const flags = new Map();
189
- const flagValues = new Map();
190
- const shortcuts = new Map();
191
- let sendMessageHandler = () => { };
192
- let appendEntryHandler = () => { };
193
- let getActiveToolsHandler = () => [];
194
- let getAllToolsHandler = () => [];
195
- let setActiveToolsHandler = () => { };
196
- let newSessionHandler = async () => ({ cancelled: false });
197
- let branchHandler = async () => ({ cancelled: false });
198
- let navigateTreeHandler = async () => ({
199
- cancelled: false,
200
- });
201
- const api = {
202
- on: (event, handler) => {
203
- const list = handlers.get(event) ?? [];
204
- list.push(handler);
205
- handlers.set(event, list);
206
- },
207
- sendMessage: (message, options) => {
208
- sendMessageHandler(message, options);
209
- },
210
- appendEntry: (customType, data) => {
211
- appendEntryHandler(customType, data);
212
- },
213
- registerMessageRenderer: (customType, renderer) => {
214
- messageRenderers.set(customType, renderer);
215
- },
216
- registerCommand: (name, options) => {
217
- commands.set(name, { name, ...options });
218
- },
219
- registerFlag: (name, options) => {
220
- flags.set(name, { name, hookPath, ...options });
221
- if (options.default !== undefined) {
222
- flagValues.set(name, options.default);
223
- }
224
- },
225
- getFlag: (name) => flagValues.get(name),
226
- registerShortcut: (shortcut, options) => {
227
- shortcuts.set(shortcut, { shortcut, hookPath, ...options });
228
- },
229
- newSession: (options) => newSessionHandler(options),
230
- branch: (entryId) => branchHandler(entryId),
231
- navigateTree: (targetId, options) => navigateTreeHandler(targetId, options),
232
- getActiveTools: () => getActiveToolsHandler(),
233
- getAllTools: () => getAllToolsHandler(),
234
- setActiveTools: (toolNames) => setActiveToolsHandler(toolNames),
235
- };
236
- def.factory(api);
237
- return {
238
- path: hookPath,
239
- resolvedPath: hookPath,
240
- handlers,
241
- messageRenderers,
242
- commands,
243
- flags,
244
- flagValues,
245
- shortcuts,
246
- setSendMessageHandler: (handler) => {
247
- sendMessageHandler = handler;
248
- },
249
- setAppendEntryHandler: (handler) => {
250
- appendEntryHandler = handler;
251
- },
252
- setNewSessionHandler: (handler) => {
253
- newSessionHandler = handler;
254
- },
255
- setBranchHandler: (handler) => {
256
- branchHandler = handler;
257
- },
258
- setNavigateTreeHandler: (handler) => {
259
- navigateTreeHandler = handler;
260
- },
261
- setGetActiveToolsHandler: (handler) => {
262
- getActiveToolsHandler = handler;
263
- },
264
- setGetAllToolsHandler: (handler) => {
265
- getAllToolsHandler = handler;
266
- },
267
- setSetActiveToolsHandler: (handler) => {
268
- setActiveToolsHandler = handler;
269
- },
270
- setFlagValue: (name, value) => {
271
- flagValues.set(name, value);
272
- },
273
- };
274
- });
275
- }
276
139
  // Factory
277
140
  /**
278
141
  * Create an AgentSession with the specified options.
@@ -300,7 +163,6 @@ function createLoadedHooksFromDefinitions(definitions) {
300
163
  * getApiKey: async () => process.env.MY_KEY,
301
164
  * systemPrompt: 'You are helpful.',
302
165
  * tools: [readTool, bashTool],
303
- * hooks: [],
304
166
  * skills: [],
305
167
  * sessionManager: SessionManager.inMemory(),
306
168
  * });
@@ -309,6 +171,7 @@ function createLoadedHooksFromDefinitions(definitions) {
309
171
  export async function createAgentSession(options = {}) {
310
172
  const cwd = options.cwd ?? process.cwd();
311
173
  const agentDir = options.agentDir ?? getDefaultAgentDir();
174
+ const eventBus = options.eventBus ?? createEventBus();
312
175
  // Use provided or create AuthStorage and ModelRegistry
313
176
  const authStorage = options.authStorage ?? discoverAuthStorage(agentDir);
314
177
  const modelRegistry = options.modelRegistry ?? discoverModels(authStorage, agentDir);
@@ -381,7 +244,7 @@ export async function createAgentSession(options = {}) {
381
244
  const contextFiles = options.contextFiles ?? discoverContextFiles(cwd, agentDir);
382
245
  time("discoverContextFiles");
383
246
  const autoResizeImages = settingsManager.getImageAutoResize();
384
- // Create ALL built-in tools for the registry (hooks can enable any of them)
247
+ // Create ALL built-in tools for the registry (extensions can enable any of them)
385
248
  const allBuiltInToolsMap = createAllTools(cwd, { read: { autoResizeImages } });
386
249
  // Determine initially active built-in tools (default: read, bash, edit, write)
387
250
  const defaultActiveToolNames = ["read", "bash", "edit", "write"];
@@ -390,56 +253,94 @@ export async function createAgentSession(options = {}) {
390
253
  : defaultActiveToolNames;
391
254
  const initialActiveBuiltInTools = initialActiveToolNames.map((name) => allBuiltInToolsMap[name]);
392
255
  time("createAllTools");
393
- let customToolsResult;
394
- if (options.customTools !== undefined) {
395
- // Use provided custom tools
396
- const loadedTools = options.customTools.map((ct) => ({
397
- path: ct.path ?? "<inline>",
398
- resolvedPath: ct.path ?? "<inline>",
399
- tool: ct.tool,
400
- }));
401
- customToolsResult = {
402
- tools: loadedTools,
256
+ // Load extensions (discovers from standard locations + configured paths)
257
+ let extensionsResult;
258
+ if (options.preloadedExtensions !== undefined && options.preloadedExtensions.length > 0) {
259
+ // Use pre-loaded extensions (from early CLI flag discovery)
260
+ extensionsResult = {
261
+ extensions: options.preloadedExtensions,
403
262
  errors: [],
404
263
  setUIContext: () => { },
405
264
  };
406
265
  }
407
266
  else {
408
- // Discover custom tools, merging with additional paths
409
- const configuredPaths = [...settingsManager.getCustomToolPaths(), ...(options.additionalCustomToolPaths ?? [])];
410
- customToolsResult = await discoverAndLoadCustomTools(configuredPaths, cwd, Object.keys(allTools), agentDir);
411
- time("discoverAndLoadCustomTools");
412
- for (const { path, error } of customToolsResult.errors) {
413
- console.error(`Failed to load custom tool "${path}": ${error}`);
267
+ // Discover extensions, merging with additional paths
268
+ const configuredPaths = [...settingsManager.getExtensionPaths(), ...(options.additionalExtensionPaths ?? [])];
269
+ extensionsResult = await discoverAndLoadExtensions(configuredPaths, cwd, agentDir, eventBus);
270
+ time("discoverAndLoadExtensions");
271
+ for (const { path, error } of extensionsResult.errors) {
272
+ console.error(`Failed to load extension "${path}": ${error}`);
414
273
  }
415
274
  }
416
- let hookRunner;
417
- if (options.preloadedHooks !== undefined && options.preloadedHooks.length > 0) {
418
- // Use pre-loaded hooks (from early CLI flag discovery)
419
- hookRunner = new HookRunner(options.preloadedHooks, cwd, sessionManager, modelRegistry);
420
- }
421
- else if (options.hooks !== undefined) {
422
- if (options.hooks.length > 0) {
423
- const loadedHooks = createLoadedHooksFromDefinitions(options.hooks);
424
- hookRunner = new HookRunner(loadedHooks, cwd, sessionManager, modelRegistry);
275
+ // Load inline extensions from factories
276
+ if (options.extensions && options.extensions.length > 0) {
277
+ // Create shared UI context holder that will be set later
278
+ const uiHolder = {
279
+ ui: {
280
+ select: async () => undefined,
281
+ confirm: async () => false,
282
+ input: async () => undefined,
283
+ notify: () => { },
284
+ setStatus: () => { },
285
+ setWidget: () => { },
286
+ setTitle: () => { },
287
+ custom: async () => undefined,
288
+ setEditorText: () => { },
289
+ getEditorText: () => "",
290
+ editor: async () => undefined,
291
+ get theme() {
292
+ return {};
293
+ },
294
+ },
295
+ hasUI: false,
296
+ };
297
+ for (let i = 0; i < options.extensions.length; i++) {
298
+ const factory = options.extensions[i];
299
+ const loaded = loadExtensionFromFactory(factory, cwd, eventBus, uiHolder, `<inline-${i}>`);
300
+ extensionsResult.extensions.push(loaded);
425
301
  }
302
+ // Extend setUIContext to update inline extensions too
303
+ const originalSetUIContext = extensionsResult.setUIContext;
304
+ extensionsResult.setUIContext = (uiContext, hasUI) => {
305
+ originalSetUIContext(uiContext, hasUI);
306
+ uiHolder.ui = uiContext;
307
+ uiHolder.hasUI = hasUI;
308
+ };
426
309
  }
427
- else {
428
- // Discover hooks, merging with additional paths
429
- const configuredPaths = [...settingsManager.getHookPaths(), ...(options.additionalHookPaths ?? [])];
430
- const { hooks, errors } = await discoverAndLoadHooks(configuredPaths, cwd, agentDir);
431
- time("discoverAndLoadHooks");
432
- for (const { path, error } of errors) {
433
- console.error(`Failed to load hook "${path}": ${error}`);
434
- }
435
- if (hooks.length > 0) {
436
- hookRunner = new HookRunner(hooks, cwd, sessionManager, modelRegistry);
437
- }
310
+ // Create extension runner if we have extensions
311
+ let extensionRunner;
312
+ if (extensionsResult.extensions.length > 0) {
313
+ extensionRunner = new ExtensionRunner(extensionsResult.extensions, cwd, sessionManager, modelRegistry);
438
314
  }
439
- // Wrap custom tools with context getter (agent/session assigned below, accessed at execute time)
315
+ // Wrap extension-registered tools and SDK-provided custom tools with context getter
316
+ // (agent/session assigned below, accessed at execute time)
440
317
  let agent;
441
318
  let session;
442
- const wrappedCustomTools = wrapCustomTools(customToolsResult.tools, () => ({
319
+ const registeredTools = extensionRunner?.getAllRegisteredTools() ?? [];
320
+ // Combine extension-registered tools with SDK-provided custom tools
321
+ const allCustomTools = [
322
+ ...registeredTools,
323
+ ...(options.customTools?.map((def) => ({ definition: def, extensionPath: "<sdk>" })) ?? []),
324
+ ];
325
+ const wrappedExtensionTools = wrapRegisteredTools(allCustomTools, () => ({
326
+ ui: extensionRunner?.getUIContext() ?? {
327
+ select: async () => undefined,
328
+ confirm: async () => false,
329
+ input: async () => undefined,
330
+ notify: () => { },
331
+ setStatus: () => { },
332
+ setWidget: () => { },
333
+ setTitle: () => { },
334
+ custom: async () => undefined,
335
+ setEditorText: () => { },
336
+ getEditorText: () => "",
337
+ editor: async () => undefined,
338
+ get theme() {
339
+ return {};
340
+ },
341
+ },
342
+ hasUI: extensionRunner?.getHasUI() ?? false,
343
+ cwd,
443
344
  sessionManager,
444
345
  modelRegistry,
445
346
  model: agent.state.model,
@@ -449,25 +350,25 @@ export async function createAgentSession(options = {}) {
449
350
  session.abort();
450
351
  },
451
352
  }));
452
- // Create tool registry mapping name -> tool (for hook getTools/setTools)
453
- // Registry contains ALL built-in tools so hooks can enable any of them
353
+ // Create tool registry mapping name -> tool (for extension getTools/setTools)
354
+ // Registry contains ALL built-in tools so extensions can enable any of them
454
355
  const toolRegistry = new Map();
455
356
  for (const [name, tool] of Object.entries(allBuiltInToolsMap)) {
456
357
  toolRegistry.set(name, tool);
457
358
  }
458
- for (const tool of wrappedCustomTools) {
359
+ for (const tool of wrappedExtensionTools) {
459
360
  toolRegistry.set(tool.name, tool);
460
361
  }
461
- // Initially active tools = active built-in + custom
462
- let activeToolsArray = [...initialActiveBuiltInTools, ...wrappedCustomTools];
362
+ // Initially active tools = active built-in + extension tools
363
+ let activeToolsArray = [...initialActiveBuiltInTools, ...wrappedExtensionTools];
463
364
  time("combineTools");
464
- // Wrap tools with hooks if available
365
+ // Wrap tools with extensions if available
465
366
  let wrappedToolRegistry;
466
- if (hookRunner) {
467
- activeToolsArray = wrapToolsWithHooks(activeToolsArray, hookRunner);
468
- // Wrap ALL registry tools (not just active) so hooks can enable any
367
+ if (extensionRunner) {
368
+ activeToolsArray = wrapToolsWithExtensions(activeToolsArray, extensionRunner);
369
+ // Wrap ALL registry tools (not just active) so extensions can enable any
469
370
  const allRegistryTools = Array.from(toolRegistry.values());
470
- const wrappedAllTools = wrapToolsWithHooks(allRegistryTools, hookRunner);
371
+ const wrappedAllTools = wrapToolsWithExtensions(allRegistryTools, extensionRunner);
471
372
  wrappedToolRegistry = new Map();
472
373
  for (const tool of wrappedAllTools) {
473
374
  wrappedToolRegistry.set(tool.name, tool);
@@ -504,8 +405,8 @@ export async function createAgentSession(options = {}) {
504
405
  };
505
406
  const systemPrompt = rebuildSystemPrompt(initialActiveToolNames);
506
407
  time("buildSystemPrompt");
507
- const slashCommands = options.slashCommands ?? discoverSlashCommands(cwd, agentDir);
508
- time("discoverSlashCommands");
408
+ const promptTemplates = options.promptTemplates ?? discoverPromptTemplates(cwd, agentDir);
409
+ time("discoverPromptTemplates");
509
410
  agent = new Agent({
510
411
  initialState: {
511
412
  systemPrompt,
@@ -514,9 +415,9 @@ export async function createAgentSession(options = {}) {
514
415
  tools: activeToolsArray,
515
416
  },
516
417
  convertToLlm,
517
- transformContext: hookRunner
418
+ transformContext: extensionRunner
518
419
  ? async (messages) => {
519
- return hookRunner.emitContext(messages);
420
+ return extensionRunner.emitContext(messages);
520
421
  }
521
422
  : undefined,
522
423
  steeringMode: settingsManager.getSteeringMode(),
@@ -550,9 +451,8 @@ export async function createAgentSession(options = {}) {
550
451
  sessionManager,
551
452
  settingsManager,
552
453
  scopedModels: options.scopedModels,
553
- fileCommands: slashCommands,
554
- hookRunner,
555
- customTools: customToolsResult.tools,
454
+ promptTemplates: promptTemplates,
455
+ extensionRunner,
556
456
  skillsSettings: settingsManager.getSkillsSettings(),
557
457
  modelRegistry,
558
458
  toolRegistry: wrappedToolRegistry ?? toolRegistry,
@@ -561,7 +461,7 @@ export async function createAgentSession(options = {}) {
561
461
  time("createAgentSession");
562
462
  return {
563
463
  session,
564
- customToolsResult,
464
+ extensionsResult,
565
465
  modelFallbackMessage,
566
466
  };
567
467
  }