@selesai/code 0.9.10 → 0.9.11

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 (302) hide show
  1. package/CHANGELOG.md +260 -255
  2. package/dist/cli/args.js +13 -2
  3. package/dist/cli/args.test.js +8 -0
  4. package/dist/cli/credential-print.js +4 -4
  5. package/dist/cli.js +0 -0
  6. package/dist/core/agent-session-auto-handoff.test.js +24 -1
  7. package/dist/core/agent-session.d.ts +11 -4
  8. package/dist/core/agent-session.js +96 -35
  9. package/dist/core/compaction/branch-summarization.js +30 -30
  10. package/dist/core/compaction/compaction.js +81 -81
  11. package/dist/core/compaction/utils.js +2 -2
  12. package/dist/core/defaults.d.ts +1 -0
  13. package/dist/core/defaults.js +9 -0
  14. package/dist/core/export-html/template.css +1066 -1066
  15. package/dist/core/export-html/template.html +55 -55
  16. package/dist/core/export-html/template.js +1864 -1864
  17. package/dist/core/export-html/vendor/highlight.min.js +1212 -1212
  18. package/dist/core/export-html/vendor/marked.min.js +78 -78
  19. package/dist/core/extensions/loader.js +108 -35
  20. package/dist/core/extensions/loader.test.d.ts +1 -0
  21. package/dist/core/extensions/loader.test.js +21 -0
  22. package/dist/core/extensions/types.d.ts +12 -2
  23. package/dist/core/keybindings.d.ts +2 -2
  24. package/dist/core/messages.js +7 -7
  25. package/dist/core/model-resolver.d.ts +1 -0
  26. package/dist/core/model-resolver.js +10 -4
  27. package/dist/core/package-manager.js +4 -4
  28. package/dist/core/sdk.d.ts +5 -5
  29. package/dist/core/sdk.js +16 -4
  30. package/dist/core/settings-manager.d.ts +6 -0
  31. package/dist/core/settings-manager.js +22 -0
  32. package/dist/core/tools/bash.d.ts +18 -1
  33. package/dist/core/tools/bash.js +41 -23
  34. package/dist/core/tools/index.d.ts +4 -1
  35. package/dist/core/tools/index.js +9 -1
  36. package/dist/core/tools/powershell.d.ts +15 -0
  37. package/dist/core/tools/powershell.js +38 -0
  38. package/dist/core/tools/powershell.test.d.ts +1 -0
  39. package/dist/core/tools/powershell.test.js +20 -0
  40. package/dist/extensions/context-compaction-reminder.test.ts +82 -82
  41. package/dist/extensions/context-compaction-reminder.ts +28 -28
  42. package/dist/extensions/pi-intercom/LICENSE +21 -21
  43. package/dist/extensions/pi-intercom/broker/client.test.ts +83 -83
  44. package/dist/extensions/pi-intercom/broker/extension.test.ts +387 -387
  45. package/dist/extensions/pi-intercom/broker/framing.test.ts +114 -114
  46. package/dist/extensions/pi-intercom/broker/paths.test.ts +153 -153
  47. package/dist/extensions/pi-intercom/broker/paths.ts +134 -134
  48. package/dist/extensions/pi-intercom/broker/runtime-claim.test.ts +34 -34
  49. package/dist/extensions/pi-intercom/broker/runtime-claim.ts +21 -21
  50. package/dist/extensions/pi-intercom/cwd.test.ts +40 -40
  51. package/dist/extensions/pi-intercom/cwd.ts +31 -31
  52. package/dist/extensions/pi-intercom/extension-api.ts +44 -44
  53. package/dist/extensions/pi-intercom/format-context.test.ts +31 -31
  54. package/dist/extensions/pi-intercom/format-context.ts +32 -32
  55. package/dist/extensions/pi-intercom/test/overlay-width.test.ts +66 -66
  56. package/dist/extensions/pi-intercom/ui/compose.ts +143 -143
  57. package/dist/extensions/pi-intercom/ui/session-list.ts +166 -166
  58. package/dist/extensions/pi-powerline-footer/bash-mode/shell-session.ts +286 -286
  59. package/dist/extensions/pi-powerline-footer/bash-mode/transcript.ts +108 -108
  60. package/dist/extensions/pi-powerline-footer/separators.ts +57 -57
  61. package/dist/extensions/pi-powerline-footer/tests/session-usage.test.ts +47 -47
  62. package/dist/extensions/pi-powerline-footer/tests/tps.test.ts +39 -39
  63. package/dist/extensions/pi-powerline-footer/theme.example.json +24 -24
  64. package/dist/extensions/pi-powerline-footer/theme.json +12 -12
  65. package/dist/extensions/pi-powerline-footer/tps.ts +345 -345
  66. package/dist/extensions/pi-rewind-hook/README.md +245 -245
  67. package/dist/extensions/pi-rewind-hook/index.ts +1445 -1445
  68. package/dist/extensions/pi-rewind-hook/package.json +29 -29
  69. package/dist/extensions/pi-subagents/install.mjs +0 -0
  70. package/dist/extensions/pi-web-agent/package.json +31 -31
  71. package/dist/extensions/pi-web-agent/src/backends/config.ts +205 -205
  72. package/dist/extensions/pi-web-agent/src/backends/doctor.ts +136 -136
  73. package/dist/extensions/pi-web-agent/src/backends/factory.ts +152 -152
  74. package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +25 -25
  75. package/dist/extensions/pi-web-agent/src/cache/ttl-cache.ts +28 -28
  76. package/dist/extensions/pi-web-agent/src/changelog-notice.ts +136 -136
  77. package/dist/extensions/pi-web-agent/src/commands/web-agent-config.ts +946 -946
  78. package/dist/extensions/pi-web-agent/src/extension.ts +126 -126
  79. package/dist/extensions/pi-web-agent/src/extract/readability.ts +118 -118
  80. package/dist/extensions/pi-web-agent/src/fetch/browser-resolution.ts +199 -199
  81. package/dist/extensions/pi-web-agent/src/fetch/firecrawl-fetch.ts +100 -100
  82. package/dist/extensions/pi-web-agent/src/fetch/headless-fetch.ts +117 -117
  83. package/dist/extensions/pi-web-agent/src/fetch/http-fetch.ts +67 -67
  84. package/dist/extensions/pi-web-agent/src/orchestration/answer-synthesizer.ts +60 -60
  85. package/dist/extensions/pi-web-agent/src/orchestration/candidate-selector.ts +50 -50
  86. package/dist/extensions/pi-web-agent/src/orchestration/direct-url.ts +52 -52
  87. package/dist/extensions/pi-web-agent/src/orchestration/evidence-quality.ts +105 -105
  88. package/dist/extensions/pi-web-agent/src/orchestration/evidence-ranker.ts +45 -45
  89. package/dist/extensions/pi-web-agent/src/orchestration/index.ts +28 -28
  90. package/dist/extensions/pi-web-agent/src/orchestration/query-planner.ts +47 -47
  91. package/dist/extensions/pi-web-agent/src/orchestration/research-orchestrator.ts +376 -376
  92. package/dist/extensions/pi-web-agent/src/orchestration/research-types.ts +64 -64
  93. package/dist/extensions/pi-web-agent/src/orchestration/research-worker.ts +181 -181
  94. package/dist/extensions/pi-web-agent/src/orchestration/source-profile.ts +101 -101
  95. package/dist/extensions/pi-web-agent/src/orchestration/stop-decider.ts +81 -81
  96. package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +210 -210
  97. package/dist/extensions/pi-web-agent/src/presentation/config.ts +75 -75
  98. package/dist/extensions/pi-web-agent/src/presentation/explore-presentation.ts +61 -61
  99. package/dist/extensions/pi-web-agent/src/presentation/fetch-presentation.ts +54 -54
  100. package/dist/extensions/pi-web-agent/src/presentation/search-presentation.ts +41 -41
  101. package/dist/extensions/pi-web-agent/src/presentation/select-view.ts +20 -20
  102. package/dist/extensions/pi-web-agent/src/presentation/types.ts +63 -63
  103. package/dist/extensions/pi-web-agent/src/search/brave.ts +114 -114
  104. package/dist/extensions/pi-web-agent/src/search/duckduckgo.ts +72 -72
  105. package/dist/extensions/pi-web-agent/src/search/searxng.ts +96 -96
  106. package/dist/extensions/pi-web-agent/src/tools/web-explore.ts +71 -71
  107. package/dist/extensions/pi-web-agent/src/tools/web-fetch-headless.ts +31 -31
  108. package/dist/extensions/pi-web-agent/src/tools/web-fetch.ts +31 -31
  109. package/dist/extensions/pi-web-agent/src/tools/web-search.ts +157 -157
  110. package/dist/extensions/pi-web-agent/src/types.ts +88 -88
  111. package/dist/extensions/ponytail/package.json +8 -8
  112. package/dist/extensions/question/batch.ts +103 -103
  113. package/dist/extensions/question/constants.ts +30 -30
  114. package/dist/extensions/question/helpers.ts +58 -58
  115. package/dist/extensions/question/navigation.ts +14 -14
  116. package/dist/extensions/question/package.json +19 -19
  117. package/dist/extensions/question/schemas.ts +43 -43
  118. package/dist/extensions/question/selection-mode.ts +46 -46
  119. package/dist/extensions/question/shortcuts.ts +44 -44
  120. package/dist/extensions/question/types.ts +132 -132
  121. package/dist/extensions/test-resolve-hook-impl.mjs +6 -6
  122. package/dist/extensions/test-resolve-hook.mjs +6 -6
  123. package/dist/extensions/web-agent-onboarding.ts +222 -222
  124. package/dist/extensions/workflow/package.json +17 -17
  125. package/dist/package-manager-cli.js +71 -71
  126. package/dist/rpc-entry.js +0 -0
  127. package/dist/skills/agent-browser/SKILL.md +52 -52
  128. package/dist/skills/batch-grill-me/SKILL.md +19 -19
  129. package/dist/skills/grill-me/SKILL.md +10 -10
  130. package/dist/skills/handoff/SKILL.md +16 -16
  131. package/dist/skills/handoff-text/SKILL.md +14 -14
  132. package/dist/skills/implanger/SKILL.md +69 -69
  133. package/dist/skills/improve-codebase/REFERENCE.md +78 -78
  134. package/dist/skills/improve-codebase/SKILL.md +178 -178
  135. package/dist/skills/planger/SKILL.md +166 -166
  136. package/dist/skills/ponytail/SKILL.md +116 -116
  137. package/dist/skills/ponytail-audit/SKILL.md +42 -42
  138. package/dist/skills/ponytail-debt/SKILL.md +45 -45
  139. package/dist/skills/ponytail-gain/SKILL.md +51 -51
  140. package/dist/skills/ponytail-help/SKILL.md +70 -70
  141. package/dist/skills/ponytail-review/SKILL.md +58 -58
  142. package/dist/skills/selesai-handoff/SKILL.md +20 -20
  143. package/dist/skills/workflow-creation/SKILL.md +73 -73
  144. package/dist/themes/powerline-footer/theme.json +33 -33
  145. package/dist/utils/shell.d.ts +3 -0
  146. package/dist/utils/shell.js +17 -5
  147. package/docs/compaction.md +396 -396
  148. package/docs/containerization.md +111 -111
  149. package/docs/development.md +71 -71
  150. package/docs/docs.json +164 -164
  151. package/docs/environment-variables.md +86 -86
  152. package/docs/index.md +83 -83
  153. package/docs/json.md +82 -82
  154. package/docs/models.md +502 -502
  155. package/docs/packages.md +227 -227
  156. package/docs/plans/subagent-delegation/phase-0-correctness.md +264 -264
  157. package/docs/plans/subagent-delegation/phase-1-behavioral-contract.md +485 -485
  158. package/docs/plans/subagent-delegation/phase-2-context-controls.md +281 -281
  159. package/docs/plans/subagent-delegation/phase-3-advisory-routing.md +361 -361
  160. package/docs/plans/subagent-delegation/phase-4-optional-enforcement.md +380 -380
  161. package/docs/prompt-templates.md +95 -95
  162. package/docs/providers.md +293 -293
  163. package/docs/sdk.md +1144 -1143
  164. package/docs/security.md +59 -59
  165. package/docs/session-format.md +414 -414
  166. package/docs/sessions.md +145 -145
  167. package/docs/shared-host-extensions.md +109 -109
  168. package/docs/shell-aliases.md +13 -13
  169. package/docs/skills.md +231 -231
  170. package/docs/terminal-setup.md +142 -142
  171. package/docs/termux.md +127 -127
  172. package/docs/themes.md +295 -295
  173. package/docs/tmux.md +63 -63
  174. package/docs/tui.md +927 -927
  175. package/docs/windows.md +17 -17
  176. package/examples/README.md +25 -25
  177. package/examples/extensions/README.md +211 -211
  178. package/examples/extensions/auto-commit-on-exit.ts +49 -49
  179. package/examples/extensions/bash-spawn-hook.ts +30 -30
  180. package/examples/extensions/bookmark.ts +50 -50
  181. package/examples/extensions/border-status-editor.ts +150 -150
  182. package/examples/extensions/built-in-tool-renderer.ts +249 -249
  183. package/examples/extensions/claude-rules.ts +86 -86
  184. package/examples/extensions/commands.ts +72 -72
  185. package/examples/extensions/confirm-destructive.ts +59 -59
  186. package/examples/extensions/custom-compaction.ts +130 -130
  187. package/examples/extensions/custom-footer.ts +64 -64
  188. package/examples/extensions/custom-header.ts +73 -73
  189. package/examples/extensions/custom-provider-anthropic/index.ts +610 -610
  190. package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -24
  191. package/examples/extensions/custom-provider-anthropic/package.json +19 -19
  192. package/examples/extensions/custom-provider-gitlab-duo/index.ts +404 -404
  193. package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -16
  194. package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -82
  195. package/examples/extensions/dirty-repo-guard.ts +56 -56
  196. package/examples/extensions/doom-overlay/README.md +46 -46
  197. package/examples/extensions/doom-overlay/doom/build/doom.js +21 -21
  198. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  199. package/examples/extensions/doom-overlay/doom/build.sh +152 -152
  200. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -72
  201. package/examples/extensions/doom-overlay/doom-component.ts +132 -132
  202. package/examples/extensions/doom-overlay/doom-engine.ts +173 -173
  203. package/examples/extensions/doom-overlay/doom-keys.ts +104 -104
  204. package/examples/extensions/doom-overlay/index.ts +74 -74
  205. package/examples/extensions/doom-overlay/wad-finder.ts +51 -51
  206. package/examples/extensions/dynamic-resources/SKILL.md +8 -8
  207. package/examples/extensions/dynamic-resources/dynamic.json +79 -79
  208. package/examples/extensions/dynamic-resources/dynamic.md +5 -5
  209. package/examples/extensions/dynamic-resources/index.ts +15 -15
  210. package/examples/extensions/dynamic-tools.ts +74 -74
  211. package/examples/extensions/event-bus.ts +43 -43
  212. package/examples/extensions/file-trigger.ts +41 -41
  213. package/examples/extensions/git-checkpoint.ts +53 -53
  214. package/examples/extensions/git-merge-and-resolve.ts +115 -115
  215. package/examples/extensions/github-issue-autocomplete.ts +185 -185
  216. package/examples/extensions/gondolin/index.ts +531 -531
  217. package/examples/extensions/gondolin/package-lock.json +185 -185
  218. package/examples/extensions/gondolin/package.json +19 -19
  219. package/examples/extensions/handoff.ts +199 -199
  220. package/examples/extensions/hello.ts +26 -26
  221. package/examples/extensions/hidden-thinking-label.ts +53 -53
  222. package/examples/extensions/inline-bash.ts +94 -94
  223. package/examples/extensions/input-transform-streaming.ts +39 -39
  224. package/examples/extensions/input-transform.ts +43 -43
  225. package/examples/extensions/interactive-shell.ts +196 -196
  226. package/examples/extensions/mac-system-theme.ts +47 -47
  227. package/examples/extensions/message-renderer.ts +59 -59
  228. package/examples/extensions/minimal-mode.ts +426 -426
  229. package/examples/extensions/modal-editor.ts +85 -85
  230. package/examples/extensions/model-status.ts +31 -31
  231. package/examples/extensions/notify.ts +55 -55
  232. package/examples/extensions/overlay-qa-tests.ts +1450 -1450
  233. package/examples/extensions/overlay-test.ts +153 -153
  234. package/examples/extensions/permission-gate.ts +34 -34
  235. package/examples/extensions/pirate.ts +47 -47
  236. package/examples/extensions/plan-mode/README.md +66 -66
  237. package/examples/extensions/plan-mode/index.ts +390 -390
  238. package/examples/extensions/plan-mode/utils.ts +168 -168
  239. package/examples/extensions/preset.ts +436 -436
  240. package/examples/extensions/project-trust.ts +64 -64
  241. package/examples/extensions/prompt-customizer.ts +97 -97
  242. package/examples/extensions/protected-paths.ts +30 -30
  243. package/examples/extensions/provider-payload.ts +18 -18
  244. package/examples/extensions/qna.ts +122 -122
  245. package/examples/extensions/question.ts +285 -285
  246. package/examples/extensions/questionnaire.ts +448 -448
  247. package/examples/extensions/rainbow-editor.ts +88 -88
  248. package/examples/extensions/reload-runtime.ts +37 -37
  249. package/examples/extensions/rpc-demo.ts +118 -118
  250. package/examples/extensions/sandbox/index.ts +321 -321
  251. package/examples/extensions/sandbox/package-lock.json +92 -92
  252. package/examples/extensions/sandbox/package.json +19 -19
  253. package/examples/extensions/send-user-message.ts +97 -97
  254. package/examples/extensions/session-name.ts +27 -27
  255. package/examples/extensions/shutdown-command.ts +63 -63
  256. package/examples/extensions/snake.ts +343 -343
  257. package/examples/extensions/space-invaders.ts +560 -560
  258. package/examples/extensions/ssh.ts +220 -220
  259. package/examples/extensions/status-line.ts +32 -32
  260. package/examples/extensions/structured-output.ts +65 -65
  261. package/examples/extensions/subagent/README.md +175 -175
  262. package/examples/extensions/subagent/agents/planner.md +37 -37
  263. package/examples/extensions/subagent/agents/reviewer.md +35 -35
  264. package/examples/extensions/subagent/agents/scout.md +50 -50
  265. package/examples/extensions/subagent/agents/worker.md +24 -24
  266. package/examples/extensions/subagent/agents.ts +126 -126
  267. package/examples/extensions/subagent/index.ts +1015 -1015
  268. package/examples/extensions/subagent/prompts/implement-and-review.md +10 -10
  269. package/examples/extensions/subagent/prompts/implement.md +10 -10
  270. package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -9
  271. package/examples/extensions/summarize.ts +209 -209
  272. package/examples/extensions/system-prompt-header.ts +17 -17
  273. package/examples/extensions/tic-tac-toe.ts +1008 -1008
  274. package/examples/extensions/timed-confirm.ts +70 -70
  275. package/examples/extensions/titlebar-spinner.ts +58 -58
  276. package/examples/extensions/todo.ts +297 -297
  277. package/examples/extensions/tool-override.ts +144 -144
  278. package/examples/extensions/tools.ts +146 -146
  279. package/examples/extensions/trigger-compact.ts +50 -50
  280. package/examples/extensions/truncated-tool.ts +195 -195
  281. package/examples/extensions/widget-placement.ts +9 -9
  282. package/examples/extensions/with-deps/index.ts +32 -32
  283. package/examples/extensions/with-deps/package-lock.json +31 -31
  284. package/examples/extensions/with-deps/package.json +22 -22
  285. package/examples/extensions/working-indicator.ts +123 -123
  286. package/examples/extensions/working-message-test.ts +25 -25
  287. package/examples/rpc-extension-ui.ts +632 -632
  288. package/examples/sdk/01-minimal.ts +26 -26
  289. package/examples/sdk/02-custom-model.ts +53 -53
  290. package/examples/sdk/03-custom-prompt.ts +75 -75
  291. package/examples/sdk/04-skills.ts +55 -55
  292. package/examples/sdk/05-tools.ts +48 -48
  293. package/examples/sdk/06-extensions.ts +99 -99
  294. package/examples/sdk/07-context-files.ts +47 -47
  295. package/examples/sdk/08-prompt-templates.ts +51 -51
  296. package/examples/sdk/09-api-keys-and-oauth.ts +52 -52
  297. package/examples/sdk/10-settings.ts +53 -53
  298. package/examples/sdk/11-sessions.ts +52 -52
  299. package/examples/sdk/12-full-control.ts +79 -79
  300. package/examples/sdk/13-session-runtime.ts +67 -67
  301. package/examples/sdk/README.md +144 -144
  302. package/package.json +4 -4
@@ -0,0 +1,21 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { createEventBus } from "../event-bus.js";
3
+ import { createExtensionRuntime, loadExtensionFromFactory } from "./loader.js";
4
+ describe("extension factory failure", () => {
5
+ test("discards pending provider and flag state and disables the failed API", async () => {
6
+ const runtime = createExtensionRuntime();
7
+ let capturedApi;
8
+ let calls = 0;
9
+ await expect(loadExtensionFromFactory((pi) => {
10
+ capturedApi = pi;
11
+ pi.events.on("failed-load", () => calls++);
12
+ pi.registerFlag("failed-flag", { type: "boolean", default: true });
13
+ pi.registerProvider("failed-provider", { baseUrl: "https://provider.test", apiKey: "key" });
14
+ throw new Error("factory failed");
15
+ }, process.cwd(), createEventBus(), runtime, "<failing>")).rejects.toThrow("factory failed");
16
+ expect(runtime.flagValues.has("failed-flag")).toBe(false);
17
+ expect(runtime.pendingProviderRegistrations).toEqual([]);
18
+ expect(calls).toBe(0);
19
+ expect(() => capturedApi?.registerFlag("late", { type: "boolean", default: true })).toThrow('Extension "<failing>" failed to load and its API is no longer active.');
20
+ });
21
+ });
@@ -454,7 +454,7 @@ export interface SessionBeforeCompactEvent {
454
454
  willRetry: boolean;
455
455
  signal: AbortSignal;
456
456
  }
457
- /** Fired after context compaction */
457
+ /** Fired after context compaction succeeds */
458
458
  export interface SessionCompactEvent {
459
459
  type: "session_compact";
460
460
  compactionEntry: CompactionEntry;
@@ -464,6 +464,15 @@ export interface SessionCompactEvent {
464
464
  /** True when the aborted turn is retried after this compaction (overflow recovery) */
465
465
  willRetry: boolean;
466
466
  }
467
+ /** Fired after context compaction fails or is aborted. */
468
+ export interface SessionCompactFailedEvent {
469
+ type: "session_compact_failed";
470
+ reason: "manual" | "threshold" | "overflow";
471
+ errorMessage?: string;
472
+ aborted: boolean;
473
+ willRetry: boolean;
474
+ fromExtension: boolean;
475
+ }
467
476
  /** Fired when the session display name changes (e.g. via /name). */
468
477
  export interface SessionInfoChangedEvent {
469
478
  type: "session_info_changed";
@@ -505,7 +514,7 @@ export interface SessionTreeEvent {
505
514
  summaryEntry?: BranchSummaryEntry;
506
515
  fromExtension?: boolean;
507
516
  }
508
- export type SessionEvent = SessionStartEvent | SessionInfoChangedEvent | SessionBeforeSwitchEvent | SessionBeforeForkEvent | SessionBeforeCompactEvent | SessionCompactEvent | SessionShutdownEvent | SessionBeforeTreeEvent | SessionTreeEvent | SessionInfoChangedEvent;
517
+ export type SessionEvent = SessionStartEvent | SessionInfoChangedEvent | SessionBeforeSwitchEvent | SessionBeforeForkEvent | SessionBeforeCompactEvent | SessionCompactEvent | SessionCompactFailedEvent | SessionShutdownEvent | SessionBeforeTreeEvent | SessionTreeEvent | SessionInfoChangedEvent;
509
518
  /** Fired before each LLM call. Can modify messages. */
510
519
  export interface ContextEvent {
511
520
  type: "context";
@@ -874,6 +883,7 @@ export interface ExtensionAPI {
874
883
  on(event: "session_before_fork", handler: ExtensionHandler<SessionBeforeForkEvent, SessionBeforeForkResult>): void;
875
884
  on(event: "session_before_compact", handler: ExtensionHandler<SessionBeforeCompactEvent, SessionBeforeCompactResult>): void;
876
885
  on(event: "session_compact", handler: ExtensionHandler<SessionCompactEvent>): void;
886
+ on(event: "session_compact_failed", handler: ExtensionHandler<SessionCompactFailedEvent>): void;
877
887
  on(event: "session_info_changed", handler: ExtensionHandler<SessionInfoChangedEvent>): void;
878
888
  on(event: "session_shutdown", handler: ExtensionHandler<SessionShutdownEvent>): void;
879
889
  on(event: "session_before_tree", handler: ExtensionHandler<SessionBeforeTreeEvent, SessionBeforeTreeResult>): void;
@@ -206,11 +206,11 @@ export declare const KEYBINDINGS: {
206
206
  readonly description: "Scroll viewport down one line";
207
207
  };
208
208
  readonly "tui.altScreen.previousPrompt": {
209
- readonly defaultKeys: "ctrl+shift+up";
209
+ readonly defaultKeys: ["ctrl+shift+up", "ctrl+up"];
210
210
  readonly description: "Jump to previous semantic prompt";
211
211
  };
212
212
  readonly "tui.altScreen.nextPrompt": {
213
- readonly defaultKeys: "ctrl+shift+down";
213
+ readonly defaultKeys: ["ctrl+shift+down", "ctrl+down"];
214
214
  readonly description: "Jump to next semantic prompt";
215
215
  };
216
216
  readonly "tui.altScreen.search": {
@@ -4,15 +4,15 @@
4
4
  * Extends the base AgentMessage type with coding-agent specific message types,
5
5
  * and provides a transformer to convert them to LLM-compatible messages.
6
6
  */
7
- export const COMPACTION_SUMMARY_PREFIX = `The conversation history before this point was compacted into the following summary:
8
-
9
- <summary>
7
+ export const COMPACTION_SUMMARY_PREFIX = `The conversation history before this point was compacted into the following summary:
8
+
9
+ <summary>
10
10
  `;
11
- export const COMPACTION_SUMMARY_SUFFIX = `
11
+ export const COMPACTION_SUMMARY_SUFFIX = `
12
12
  </summary>`;
13
- export const BRANCH_SUMMARY_PREFIX = `The following is a summary of a branch that this conversation came back from:
14
-
15
- <summary>
13
+ export const BRANCH_SUMMARY_PREFIX = `The following is a summary of a branch that this conversation came back from:
14
+
15
+ <summary>
16
16
  `;
17
17
  export const BRANCH_SUMMARY_SUFFIX = `</summary>`;
18
18
  /**
@@ -110,6 +110,7 @@ export declare function findInitialModel(options: {
110
110
  defaultProvider?: string;
111
111
  defaultModelId?: string;
112
112
  defaultThinkingLevel?: ThinkingLevel;
113
+ modelThinkingLevels?: Record<string, ThinkingLevel>;
113
114
  modelRuntime: ModelRuntime;
114
115
  }): Promise<InitialModelResult>;
115
116
  /**
@@ -440,7 +440,7 @@ export function resolveCliModel(options) {
440
440
  * 5. First available model with valid API key
441
441
  */
442
442
  export async function findInitialModel(options) {
443
- const { cliProvider, cliModel, scopedModels, isContinuing, defaultProvider, defaultModelId, defaultThinkingLevel, modelRuntime, } = options;
443
+ const { cliProvider, cliModel, scopedModels, isContinuing, defaultProvider, defaultModelId, defaultThinkingLevel, modelThinkingLevels, modelRuntime, } = options;
444
444
  let model;
445
445
  let thinkingLevel = DEFAULT_THINKING_LEVEL;
446
446
  // 1. CLI args take priority
@@ -460,9 +460,11 @@ export async function findInitialModel(options) {
460
460
  }
461
461
  // 2. Use first model from scoped models (skip if continuing/resuming)
462
462
  if (scopedModels.length > 0 && !isContinuing) {
463
+ const scopedModel = scopedModels[0];
464
+ const perModel = modelThinkingLevels?.[`${scopedModel.model.provider}/${scopedModel.model.id}`];
463
465
  return {
464
- model: scopedModels[0].model,
465
- thinkingLevel: scopedModels[0].thinkingLevel ?? defaultThinkingLevel ?? DEFAULT_THINKING_LEVEL,
466
+ model: scopedModel.model,
467
+ thinkingLevel: scopedModel.thinkingLevel ?? perModel ?? defaultThinkingLevel ?? DEFAULT_THINKING_LEVEL,
466
468
  fallbackMessage: undefined,
467
469
  };
468
470
  }
@@ -471,7 +473,11 @@ export async function findInitialModel(options) {
471
473
  const found = modelRuntime.getModel(defaultProvider, defaultModelId);
472
474
  if (found && modelRuntime.hasConfiguredAuth(found.provider)) {
473
475
  model = found;
474
- if (defaultThinkingLevel) {
476
+ const perModel = modelThinkingLevels?.[`${defaultProvider}/${defaultModelId}`];
477
+ if (perModel) {
478
+ thinkingLevel = perModel;
479
+ }
480
+ else if (defaultThinkingLevel) {
475
481
  thinkingLevel = defaultThinkingLevel;
476
482
  }
477
483
  return { model, thinkingLevel, fallbackMessage: undefined };
@@ -1973,7 +1973,7 @@ export class DefaultPackageManager {
1973
1973
  // Project extensions from .pi/
1974
1974
  addResources("extensions", collectAutoExtensionEntries(projectDirs.extensions), projectMetadata, projectOverrides.extensions, projectBaseDir);
1975
1975
  // Project skills from .pi/
1976
- addResources("skills", collectAutoSkillEntries(projectDirs.skills, "pi"), projectMetadata, projectOverrides.skills, projectBaseDir, false);
1976
+ addResources("skills", collectAutoSkillEntries(projectDirs.skills, "pi"), projectMetadata, projectOverrides.skills, projectBaseDir);
1977
1977
  }
1978
1978
  // Project skills from .agents/ (each with its own baseDir)
1979
1979
  for (const agentsSkillsDir of projectAgentsSkillDirs) {
@@ -1982,7 +1982,7 @@ export class DefaultPackageManager {
1982
1982
  ...projectMetadata,
1983
1983
  baseDir: agentsBaseDir,
1984
1984
  };
1985
- addResources("skills", collectAutoSkillEntries(agentsSkillsDir, "agents"), agentsMetadata, projectOverrides.skills, agentsBaseDir, false);
1985
+ addResources("skills", collectAutoSkillEntries(agentsSkillsDir, "agents"), agentsMetadata, projectOverrides.skills, agentsBaseDir);
1986
1986
  }
1987
1987
  if (projectTrusted) {
1988
1988
  addResources("prompts", collectAutoPromptEntries(projectDirs.prompts), projectMetadata, projectOverrides.prompts, projectBaseDir);
@@ -2057,14 +2057,14 @@ export class DefaultPackageManager {
2057
2057
  }
2058
2058
  addResources("extensions", piHostKept, { ...userMetadata, baseDir: piHostBaseDir }, userOverrides.extensions, piHostBaseDir);
2059
2059
  // User skills from ~/.selesai/agent/
2060
- addResources("skills", collectAutoSkillEntries(userDirs.skills, "pi"), userMetadata, userOverrides.skills, globalBaseDir, false);
2060
+ addResources("skills", collectAutoSkillEntries(userDirs.skills, "pi"), userMetadata, userOverrides.skills, globalBaseDir);
2061
2061
  // User skills from ~/.agents/ (with its own baseDir)
2062
2062
  const userAgentsBaseDir = dirname(userAgentsSkillsDir);
2063
2063
  const userAgentsMetadata = {
2064
2064
  ...userMetadata,
2065
2065
  baseDir: userAgentsBaseDir,
2066
2066
  };
2067
- addResources("skills", collectAutoSkillEntries(userAgentsSkillsDir, "agents"), userAgentsMetadata, userOverrides.skills, userAgentsBaseDir, false);
2067
+ addResources("skills", collectAutoSkillEntries(userAgentsSkillsDir, "agents"), userAgentsMetadata, userOverrides.skills, userAgentsBaseDir);
2068
2068
  addResources("prompts", collectAutoPromptEntries(userDirs.prompts), userMetadata, userOverrides.prompts, globalBaseDir);
2069
2069
  addResources("themes", collectAutoThemeEntries(userDirs.themes), userMetadata, userOverrides.themes, globalBaseDir);
2070
2070
  return collisions;
@@ -6,7 +6,7 @@ import { ModelRuntime } from "./model-runtime.ts";
6
6
  import type { ResourceLoader } from "./resource-loader.ts";
7
7
  import { SessionManager } from "./session-manager.ts";
8
8
  import { SettingsManager } from "./settings-manager.ts";
9
- import { createBashTool, createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createReadOnlyTools, createReadTool, createWriteTool, withFileMutationQueue } from "./tools/index.ts";
9
+ import { createBashTool, createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createPowerShellTool, createReadOnlyTools, createReadTool, createWriteTool, withFileMutationQueue } from "./tools/index.ts";
10
10
  export interface CreateAgentSessionOptions {
11
11
  /** Working directory for project-local discovery. Default: process.cwd() */
12
12
  cwd?: string;
@@ -36,9 +36,9 @@ export interface CreateAgentSessionOptions {
36
36
  *
37
37
  * When omitted, pi uses the `defaultTools` setting for the initial built-in
38
38
  * selection when configured. Otherwise it enables the default built-in tools
39
- * (read, bash, edit, write). Extension/custom tools remain enabled unless
40
- * `noTools` changes that default. When provided, only the listed tool names are
41
- * enabled.
39
+ * (read, bash, edit, write). Extension/custom tools are inactive by default;
40
+ * `noTools: "builtin"` enables them instead. When provided, only the listed
41
+ * tool names are enabled.
42
42
  */
43
43
  tools?: string[];
44
44
  /** Optional denylist of tool names to disable. Applies after `tools` when both are provided. */
@@ -68,7 +68,7 @@ export type { ExtensionAPI, ExtensionCommandContext, ExtensionContext, Extension
68
68
  export type { PromptTemplate } from "./prompt-templates.ts";
69
69
  export type { Skill } from "./skills.ts";
70
70
  export type { Tool } from "./tools/index.ts";
71
- export { withFileMutationQueue, createCodingTools, createReadOnlyTools, createReadTool, createBashTool, createEditTool, createWriteTool, createGrepTool, createFindTool, createLsTool, };
71
+ export { withFileMutationQueue, createCodingTools, createReadOnlyTools, createReadTool, createBashTool, createPowerShellTool, createEditTool, createWriteTool, createGrepTool, createFindTool, createLsTool, };
72
72
  /**
73
73
  * Create an AgentSession with the specified options.
74
74
  *
package/dist/core/sdk.js CHANGED
@@ -14,7 +14,7 @@ import { DefaultResourceLoader } from "./resource-loader.js";
14
14
  import { getDefaultSessionDir, SessionManager } from "./session-manager.js";
15
15
  import { SettingsManager } from "./settings-manager.js";
16
16
  import { time } from "./timings.js";
17
- import { createBashTool, createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createReadOnlyTools, createReadTool, createWriteTool, withFileMutationQueue, } from "./tools/index.js";
17
+ import { createBashTool, createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createPowerShellTool, createReadOnlyTools, createReadTool, createWriteTool, withFileMutationQueue, } from "./tools/index.js";
18
18
  // Preserve the pre-0.81 fallback for extensions that construct Agent instances
19
19
  // or invoke low-level agent loops without supplying streamFn. Agent core remains
20
20
  // provider-agnostic and does not import pi-ai/compat itself.
@@ -23,7 +23,7 @@ setDefaultStreamFn(streamSimple);
23
23
  export * from "./agent-session-runtime.js";
24
24
  export { withFileMutationQueue,
25
25
  // Tool factories (for custom cwd)
26
- createCodingTools, createReadOnlyTools, createReadTool, createBashTool, createEditTool, createWriteTool, createGrepTool, createFindTool, createLsTool, };
26
+ createCodingTools, createReadOnlyTools, createReadTool, createBashTool, createPowerShellTool, createEditTool, createWriteTool, createGrepTool, createFindTool, createLsTool, };
27
27
  // Helper Functions
28
28
  function getDefaultAgentDir() {
29
29
  return getAgentDir();
@@ -101,6 +101,7 @@ export async function createAgentSession(options = {}) {
101
101
  defaultProvider: settingsManager.getDefaultProvider(),
102
102
  defaultModelId: settingsManager.getDefaultModel(),
103
103
  defaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),
104
+ modelThinkingLevels: settingsManager.getAllModelThinkingLevels(),
104
105
  modelRuntime,
105
106
  });
106
107
  model = result.model;
@@ -118,7 +119,10 @@ export async function createAgentSession(options = {}) {
118
119
  ? existingSession.thinkingLevel
119
120
  : (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
120
121
  }
121
- // Fall back to settings default
122
+ // Fall back to the selected model's override, then settings default
123
+ if (thinkingLevel === undefined && model) {
124
+ thinkingLevel = settingsManager.getModelThinkingLevel(model.provider, model.id);
125
+ }
122
126
  if (thinkingLevel === undefined) {
123
127
  thinkingLevel = settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
124
128
  }
@@ -134,7 +138,15 @@ export async function createAgentSession(options = {}) {
134
138
  const allowedToolNames = options.tools ?? (options.noTools === "all" ? [] : undefined);
135
139
  const excludedToolNames = options.excludeTools;
136
140
  const excludedToolNameSet = excludedToolNames ? new Set(excludedToolNames) : undefined;
137
- const initialActiveToolNames = (options.tools ?? (options.noTools ? [] : (configuredDefaultToolNames ?? defaultActiveToolNames))).filter((name) => !excludedToolNameSet?.has(name));
141
+ const initialActiveToolNames = (options.tools ??
142
+ (options.noTools === "builtin"
143
+ ? [
144
+ ...resourceLoader.getExtensions().extensions.flatMap((extension) => Array.from(extension.tools.keys())),
145
+ ...(options.customTools?.map((tool) => tool.name) ?? []),
146
+ ]
147
+ : options.noTools
148
+ ? []
149
+ : (configuredDefaultToolNames ?? defaultActiveToolNames))).filter((name) => !excludedToolNameSet?.has(name));
138
150
  let agent;
139
151
  // Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)
140
152
  const convertToLlmWithBlockImages = (messages) => {
@@ -72,6 +72,8 @@ export interface Settings {
72
72
  defaultProvider?: string;
73
73
  defaultModel?: string;
74
74
  defaultThinkingLevel?: ThinkingLevel;
75
+ /** Per-model default thinking overrides, keyed by "provider/modelId". */
76
+ modelThinkingLevels?: Record<string, ThinkingLevel>;
75
77
  transport?: TransportSetting;
76
78
  steeringMode?: "all" | "one-at-a-time";
77
79
  followUpMode?: "all" | "one-at-a-time";
@@ -210,6 +212,10 @@ export declare class SettingsManager {
210
212
  setTheme(theme: string): void;
211
213
  getDefaultThinkingLevel(): ThinkingLevel | undefined;
212
214
  setDefaultThinkingLevel(level: ThinkingLevel): void;
215
+ getModelThinkingLevel(provider: string, modelId: string): ThinkingLevel | undefined;
216
+ getAllModelThinkingLevels(): Record<string, ThinkingLevel>;
217
+ setModelThinkingLevel(provider: string, modelId: string, level: ThinkingLevel): void;
218
+ removeModelThinkingLevel(provider: string, modelId: string): void;
213
219
  getTransport(): TransportSetting;
214
220
  setTransport(transport: TransportSetting): void;
215
221
  getCompactionEnabled(): boolean;
@@ -515,6 +515,28 @@ export class SettingsManager {
515
515
  this.markModified("defaultThinkingLevel");
516
516
  this.save();
517
517
  }
518
+ getModelThinkingLevel(provider, modelId) {
519
+ return this.settings.modelThinkingLevels?.[`${provider}/${modelId}`];
520
+ }
521
+ getAllModelThinkingLevels() {
522
+ return { ...(this.settings.modelThinkingLevels ?? {}) };
523
+ }
524
+ setModelThinkingLevel(provider, modelId, level) {
525
+ this.globalSettings.modelThinkingLevels ??= {};
526
+ this.globalSettings.modelThinkingLevels[`${provider}/${modelId}`] = level;
527
+ this.markModified("modelThinkingLevels");
528
+ this.save();
529
+ }
530
+ removeModelThinkingLevel(provider, modelId) {
531
+ if (!this.globalSettings.modelThinkingLevels)
532
+ return;
533
+ delete this.globalSettings.modelThinkingLevels[`${provider}/${modelId}`];
534
+ if (Object.keys(this.globalSettings.modelThinkingLevels).length === 0) {
535
+ delete this.globalSettings.modelThinkingLevels;
536
+ }
537
+ this.markModified("modelThinkingLevels");
538
+ this.save();
539
+ }
518
540
  getTransport() {
519
541
  return this.settings.transport ?? "auto";
520
542
  }
@@ -1,11 +1,16 @@
1
1
  import type { AgentTool } from "@earendil-works/pi-agent-core";
2
2
  import { type Static, Type } from "typebox";
3
+ import { type ShellConfig } from "../../utils/shell.ts";
3
4
  import type { ToolDefinition } from "../extensions/types.ts";
4
5
  import { type TruncationResult } from "./truncate.ts";
5
6
  declare const bashSchema: Type.TObject<{
6
7
  command: Type.TString;
7
8
  timeout: Type.TOptional<Type.TNumber>;
8
9
  }>;
10
+ export declare const bashToolSystemPromptContribution: {
11
+ readonly snippet: "Execute bash commands (ls, grep, find, etc.)";
12
+ readonly guidelines: readonly ["You can inspect PI_* environment variables for current model and session details."];
13
+ };
9
14
  export type BashToolInput = Static<typeof bashSchema>;
10
15
  export interface BashToolDetails {
11
16
  truncation?: TruncationResult;
@@ -32,6 +37,8 @@ export interface BashOperations {
32
37
  exitCode: number | null;
33
38
  }>;
34
39
  }
40
+ /** Shared process execution used by the built-in shell tools. */
41
+ export declare function createLocalShellOperations(shellName: string, resolveShellConfig: () => ShellConfig): BashOperations;
35
42
  /**
36
43
  * Create bash operations using pi's built-in local shell execution backend.
37
44
  *
@@ -59,11 +66,21 @@ export interface BashToolOptions {
59
66
  /** Hook to adjust command, cwd, or env before execution */
60
67
  spawnHook?: BashSpawnHook;
61
68
  }
62
- type BashRenderState = {
69
+ export type BashRenderState = {
63
70
  startedAt: number | undefined;
64
71
  endedAt: number | undefined;
65
72
  interval: NodeJS.Timeout | undefined;
66
73
  };
74
+ export interface ShellToolConfig {
75
+ name: string;
76
+ label: string;
77
+ shellName: string;
78
+ prompt: string;
79
+ promptSnippet: string;
80
+ promptGuidelines?: readonly string[];
81
+ tempFilePrefix: string;
82
+ }
83
+ export declare function createShellToolDefinition(cwd: string, config: ShellToolConfig, options?: BashToolOptions): ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState>;
67
84
  export declare function createBashToolDefinition(cwd: string, options?: BashToolOptions): ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState>;
68
85
  export declare function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema>;
69
86
  export {};
@@ -7,8 +7,8 @@ import { keyHint } from "../../modes/interactive/components/keybinding-hints.js"
7
7
  import { truncateToVisualLines } from "../../modes/interactive/components/visual-truncate.js";
8
8
  import { theme } from "../../modes/interactive/theme/theme.js";
9
9
  import { waitForChildProcess } from "../../utils/child-process.js";
10
- import { getExperimentalToolSampling } from "../experimental.js";
11
10
  import { getShellConfig, getShellEnv, killProcessTree, trackDetachedChildPid, untrackDetachedChildPid, } from "../../utils/shell.js";
11
+ import { getExperimentalToolSampling } from "../experimental.js";
12
12
  import { OutputAccumulator } from "./output-accumulator.js";
13
13
  import { getTextOutput, invalidArgText, str } from "./render-utils.js";
14
14
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
@@ -28,28 +28,27 @@ function resolveTimeoutMs(timeout) {
28
28
  return timeoutMs;
29
29
  }
30
30
  const bashSchema = Type.Object({
31
- command: Type.String({ description: "Bash command to execute" }),
31
+ command: Type.String({ description: "Shell command to execute" }),
32
32
  timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (optional, no default timeout)" })),
33
33
  });
34
- /**
35
- * Create bash operations using pi's built-in local shell execution backend.
36
- *
37
- * This is useful for extensions that intercept user_bash and still want pi's
38
- * standard local shell behavior while wrapping or rewriting commands.
39
- */
40
- export function createLocalBashOperations(options) {
34
+ export const bashToolSystemPromptContribution = {
35
+ snippet: "Execute bash commands (ls, grep, find, etc.)",
36
+ guidelines: ["You can inspect PI_* environment variables for current model and session details."],
37
+ };
38
+ /** Shared process execution used by the built-in shell tools. */
39
+ export function createLocalShellOperations(shellName, resolveShellConfig) {
41
40
  return {
42
41
  exec: async (command, cwd, { onData, signal, timeout, env }) => {
43
42
  const timeoutMs = resolveTimeoutMs(timeout);
44
43
  if (signal?.aborted) {
45
44
  throw new Error("aborted");
46
45
  }
47
- const shellConfig = getShellConfig(options?.shellPath);
46
+ const shellConfig = resolveShellConfig();
48
47
  try {
49
48
  await fsAccess(cwd, constants.F_OK);
50
49
  }
51
50
  catch {
52
- throw new Error(`Working directory does not exist: ${cwd}\nCannot execute bash commands.`);
51
+ throw new Error(`Working directory does not exist: ${cwd}\nCannot execute ${shellName} commands.`);
53
52
  }
54
53
  const commandFromStdin = shellConfig.commandTransport === "stdin";
55
54
  const child = spawn(shellConfig.shell, commandFromStdin ? shellConfig.args : [...shellConfig.args, command], {
@@ -112,6 +111,15 @@ export function createLocalBashOperations(options) {
112
111
  },
113
112
  };
114
113
  }
114
+ /**
115
+ * Create bash operations using pi's built-in local shell execution backend.
116
+ *
117
+ * This is useful for extensions that intercept user_bash and still want pi's
118
+ * standard local shell behavior while wrapping or rewriting commands.
119
+ */
120
+ export function createLocalBashOperations(options) {
121
+ return createLocalShellOperations("bash", () => getShellConfig(options?.shellPath));
122
+ }
115
123
  function resolveSpawnContext(command, cwd, spawnHook, exposeSessionEnvironment, ctx) {
116
124
  const env = { ...getShellEnv() };
117
125
  delete env.PI_SESSION_ID;
@@ -147,12 +155,12 @@ class BashResultRenderComponent extends Container {
147
155
  function formatDuration(ms) {
148
156
  return `${(ms / 1000).toFixed(1)}s`;
149
157
  }
150
- function formatBashCall(args) {
158
+ function formatShellCall(args, prompt) {
151
159
  const command = str(args?.command);
152
160
  const timeout = args?.timeout;
153
161
  const timeoutSuffix = timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : "";
154
162
  const commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg("toolOutput", "...");
155
- return theme.fg("toolTitle", theme.bold(`$ ${commandDisplay}`)) + timeoutSuffix;
163
+ return theme.fg("toolTitle", theme.bold(`${prompt} ${commandDisplay}`)) + timeoutSuffix;
156
164
  }
157
165
  function rebuildBashResultRenderComponent(component, result, options, showImages, startedAt, endedAt) {
158
166
  const state = component.state;
@@ -219,25 +227,23 @@ function rebuildBashResultRenderComponent(component, result, options, showImages
219
227
  component.addChild(new Text(`\n${theme.fg("muted", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));
220
228
  }
221
229
  }
222
- export function createBashToolDefinition(cwd, options) {
230
+ export function createShellToolDefinition(cwd, config, options) {
223
231
  const ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });
224
232
  const commandPrefix = options?.commandPrefix;
225
233
  const exposeSessionEnvironment = options?.exposeSessionEnvironment ?? true;
226
234
  const spawnHook = options?.spawnHook;
227
235
  return {
228
- name: "bash",
229
- label: "bash",
230
- description: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,
231
- promptSnippet: "Execute bash commands (ls, grep, find, etc.)",
232
- promptGuidelines: exposeSessionEnvironment
233
- ? ["Inspect PI_* environment variables for current model and session details."]
234
- : undefined,
236
+ name: config.name,
237
+ label: config.label,
238
+ description: `Execute a ${config.shellName} command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,
239
+ promptSnippet: config.promptSnippet,
240
+ promptGuidelines: exposeSessionEnvironment && config.promptGuidelines ? [...config.promptGuidelines] : undefined,
235
241
  parameters: bashSchema,
236
242
  constrainedSampling: getExperimentalToolSampling(),
237
243
  async execute(_toolCallId, { command, timeout }, signal, onUpdate, ctx) {
238
244
  const resolvedCommand = commandPrefix ? `${commandPrefix}\n${command}` : command;
239
245
  const spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook, exposeSessionEnvironment, ctx);
240
- const output = new OutputAccumulator({ tempFilePrefix: "pi-bash" });
246
+ const output = new OutputAccumulator({ tempFilePrefix: config.tempFilePrefix });
241
247
  let acceptingOutput = true;
242
248
  let updateTimer;
243
249
  let updateDirty = false;
@@ -358,7 +364,7 @@ export function createBashToolDefinition(cwd, options) {
358
364
  state.endedAt = undefined;
359
365
  }
360
366
  const text = context.lastComponent ?? new Text("", 0, 0);
361
- text.setText(formatBashCall(args));
367
+ text.setText(formatShellCall(args, config.prompt));
362
368
  return text;
363
369
  },
364
370
  renderResult(result, options, _theme, context) {
@@ -380,6 +386,18 @@ export function createBashToolDefinition(cwd, options) {
380
386
  },
381
387
  };
382
388
  }
389
+ const bashToolConfig = {
390
+ name: "bash",
391
+ label: "bash",
392
+ shellName: "bash",
393
+ prompt: "$",
394
+ promptSnippet: bashToolSystemPromptContribution.snippet,
395
+ promptGuidelines: bashToolSystemPromptContribution.guidelines,
396
+ tempFilePrefix: "selesai-bash",
397
+ };
398
+ export function createBashToolDefinition(cwd, options) {
399
+ return createShellToolDefinition(cwd, bashToolConfig, options);
400
+ }
383
401
  export function createBashTool(cwd, options) {
384
402
  const definition = createBashToolDefinition(cwd, options);
385
403
  const tool = wrapToolDefinition(definition);
@@ -1,4 +1,5 @@
1
1
  export { type BashOperations, type BashSpawnContext, type BashSpawnHook, type BashToolDetails, type BashToolInput, type BashToolOptions, createBashTool, createBashToolDefinition, createLocalBashOperations, } from "./bash.ts";
2
+ export { createLocalPowerShellOperations, createPowerShellTool, createPowerShellToolDefinition, type PowerShellOperations, type PowerShellSpawnContext, type PowerShellSpawnHook, type PowerShellToolDetails, type PowerShellToolInput, type PowerShellToolOptions, } from "./powershell.ts";
2
3
  export { createEditTool, createEditToolDefinition, type EditOperations, type EditToolDetails, type EditToolInput, type EditToolOptions, } from "./edit.ts";
3
4
  export { withFileMutationQueue } from "./file-mutation-queue.ts";
4
5
  export { createFindTool, createFindToolDefinition, type FindOperations, type FindToolDetails, type FindToolInput, type FindToolOptions, } from "./find.ts";
@@ -15,15 +16,17 @@ import { type EditToolOptions } from "./edit.ts";
15
16
  import { type FindToolOptions } from "./find.ts";
16
17
  import { type GrepToolOptions } from "./grep.ts";
17
18
  import { type LsToolOptions } from "./ls.ts";
19
+ import { type PowerShellToolOptions } from "./powershell.ts";
18
20
  import { type ReadToolOptions } from "./read.ts";
19
21
  import { type WriteToolOptions } from "./write.ts";
20
22
  export type Tool = AgentTool<any>;
21
23
  export type ToolDef = ToolDefinition<any, any>;
22
- export type ToolName = "read" | "bash" | "edit" | "write" | "grep" | "find" | "ls";
24
+ export type ToolName = "read" | "bash" | "powershell" | "edit" | "write" | "grep" | "find" | "ls";
23
25
  export declare const allToolNames: Set<ToolName>;
24
26
  export interface ToolsOptions {
25
27
  read?: ReadToolOptions;
26
28
  bash?: BashToolOptions;
29
+ powershell?: PowerShellToolOptions;
27
30
  write?: WriteToolOptions;
28
31
  edit?: EditToolOptions;
29
32
  grep?: GrepToolOptions;
@@ -1,4 +1,5 @@
1
1
  export { createBashTool, createBashToolDefinition, createLocalBashOperations, } from "./bash.js";
2
+ export { createLocalPowerShellOperations, createPowerShellTool, createPowerShellToolDefinition, } from "./powershell.js";
2
3
  export { createEditTool, createEditToolDefinition, } from "./edit.js";
3
4
  export { withFileMutationQueue } from "./file-mutation-queue.js";
4
5
  export { createFindTool, createFindToolDefinition, } from "./find.js";
@@ -13,15 +14,18 @@ import { createEditTool, createEditToolDefinition } from "./edit.js";
13
14
  import { createFindTool, createFindToolDefinition } from "./find.js";
14
15
  import { createGrepTool, createGrepToolDefinition } from "./grep.js";
15
16
  import { createLsTool, createLsToolDefinition } from "./ls.js";
17
+ import { createPowerShellTool, createPowerShellToolDefinition } from "./powershell.js";
16
18
  import { createReadTool, createReadToolDefinition } from "./read.js";
17
19
  import { createWriteTool, createWriteToolDefinition } from "./write.js";
18
- export const allToolNames = new Set(["read", "bash", "edit", "write", "grep", "find", "ls"]);
20
+ export const allToolNames = new Set(["read", "bash", "powershell", "edit", "write", "grep", "find", "ls"]);
19
21
  export function createToolDefinition(toolName, cwd, options) {
20
22
  switch (toolName) {
21
23
  case "read":
22
24
  return createReadToolDefinition(cwd, options?.read);
23
25
  case "bash":
24
26
  return createBashToolDefinition(cwd, options?.bash);
27
+ case "powershell":
28
+ return createPowerShellToolDefinition(cwd, options?.powershell);
25
29
  case "edit":
26
30
  return createEditToolDefinition(cwd, options?.edit);
27
31
  case "write":
@@ -42,6 +46,8 @@ export function createTool(toolName, cwd, options) {
42
46
  return createReadTool(cwd, options?.read);
43
47
  case "bash":
44
48
  return createBashTool(cwd, options?.bash);
49
+ case "powershell":
50
+ return createPowerShellTool(cwd, options?.powershell);
45
51
  case "edit":
46
52
  return createEditTool(cwd, options?.edit);
47
53
  case "write":
@@ -76,6 +82,7 @@ export function createAllToolDefinitions(cwd, options) {
76
82
  return {
77
83
  read: createReadToolDefinition(cwd, options?.read),
78
84
  bash: createBashToolDefinition(cwd, options?.bash),
85
+ powershell: createPowerShellToolDefinition(cwd, options?.powershell),
79
86
  edit: createEditToolDefinition(cwd, options?.edit),
80
87
  write: createWriteToolDefinition(cwd, options?.write),
81
88
  grep: createGrepToolDefinition(cwd, options?.grep),
@@ -103,6 +110,7 @@ export function createAllTools(cwd, options) {
103
110
  return {
104
111
  read: createReadTool(cwd, options?.read),
105
112
  bash: createBashTool(cwd, options?.bash),
113
+ powershell: createPowerShellTool(cwd, options?.powershell),
106
114
  edit: createEditTool(cwd, options?.edit),
107
115
  write: createWriteTool(cwd, options?.write),
108
116
  grep: createGrepTool(cwd, options?.grep),
@@ -0,0 +1,15 @@
1
+ import { type BashOperations, type BashSpawnContext, type BashSpawnHook, type BashToolDetails, type BashToolInput, type BashToolOptions, type createBashTool, createShellToolDefinition } from "./bash.ts";
2
+ export declare const powershellToolSystemPromptContribution: {
3
+ readonly snippet: "Execute PowerShell commands";
4
+ readonly guidelines: readonly ["You can inspect PI_* environment variables for current model and session details."];
5
+ };
6
+ export type PowerShellOperations = BashOperations;
7
+ export type PowerShellSpawnContext = BashSpawnContext;
8
+ export type PowerShellSpawnHook = BashSpawnHook;
9
+ export type PowerShellToolDetails = BashToolDetails;
10
+ export type PowerShellToolInput = BashToolInput;
11
+ export interface PowerShellToolOptions extends Pick<BashToolOptions, "operations" | "exposeSessionEnvironment" | "spawnHook"> {
12
+ }
13
+ export declare function createLocalPowerShellOperations(): PowerShellOperations;
14
+ export declare function createPowerShellToolDefinition(cwd: string, options?: PowerShellToolOptions): ReturnType<typeof createShellToolDefinition>;
15
+ export declare function createPowerShellTool(cwd: string, options?: PowerShellToolOptions): ReturnType<typeof createBashTool>;
@@ -0,0 +1,38 @@
1
+ import { getPowerShellConfig } from "../../utils/shell.js";
2
+ import { createLocalShellOperations, createShellToolDefinition, } from "./bash.js";
3
+ import { wrapToolDefinition } from "./tool-definition-wrapper.js";
4
+ const UTF8_OUTPUT_PREFIX = "try { [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 } catch {}\n";
5
+ export const powershellToolSystemPromptContribution = {
6
+ snippet: "Execute PowerShell commands",
7
+ guidelines: ["You can inspect PI_* environment variables for current model and session details."],
8
+ };
9
+ export function createLocalPowerShellOperations() {
10
+ const operations = createLocalShellOperations("PowerShell", getPowerShellConfig);
11
+ return {
12
+ exec: (command, cwd, options) => operations.exec(`${UTF8_OUTPUT_PREFIX}${command}`, cwd, options),
13
+ };
14
+ }
15
+ const powershellToolConfig = {
16
+ name: "powershell",
17
+ label: "powershell",
18
+ shellName: "PowerShell",
19
+ prompt: "PS>",
20
+ promptSnippet: powershellToolSystemPromptContribution.snippet,
21
+ promptGuidelines: powershellToolSystemPromptContribution.guidelines,
22
+ tempFilePrefix: "selesai-powershell",
23
+ };
24
+ export function createPowerShellToolDefinition(cwd, options) {
25
+ return createShellToolDefinition(cwd, powershellToolConfig, {
26
+ ...options,
27
+ operations: options?.operations ?? createLocalPowerShellOperations(),
28
+ });
29
+ }
30
+ export function createPowerShellTool(cwd, options) {
31
+ const definition = createPowerShellToolDefinition(cwd, options);
32
+ const tool = wrapToolDefinition(definition);
33
+ Object.assign(tool, {
34
+ promptSnippet: definition.promptSnippet,
35
+ promptGuidelines: definition.promptGuidelines,
36
+ });
37
+ return tool;
38
+ }