@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
@@ -1,72 +1,72 @@
1
- /**
2
- * Commands Extension
3
- *
4
- * Demonstrates the pi.getCommands() API by providing a /commands command
5
- * that lists all available slash commands in the current session.
6
- *
7
- * Usage:
8
- * 1. Copy this file to ~/.pi/agent/extensions/ or your project's .pi/extensions/
9
- * 2. Use /commands to see available commands
10
- * 3. Use /commands extensions to filter by source
11
- */
12
-
13
- import type { ExtensionAPI, SlashCommandInfo } from "@earendil-works/pi-coding-agent";
14
-
15
- export default function commandsExtension(pi: ExtensionAPI) {
16
- pi.registerCommand("commands", {
17
- description: "List available slash commands",
18
- getArgumentCompletions: (prefix) => {
19
- const sources = ["extension", "prompt", "skill"];
20
- const filtered = sources.filter((s) => s.startsWith(prefix));
21
- return filtered.length > 0 ? filtered.map((s) => ({ value: s, label: s })) : null;
22
- },
23
- handler: async (args, ctx) => {
24
- const commands = pi.getCommands();
25
- const sourceFilter = args.trim() as "extension" | "prompt" | "skill" | "";
26
-
27
- // Filter by source if specified
28
- const filtered = sourceFilter ? commands.filter((c) => c.source === sourceFilter) : commands;
29
-
30
- if (filtered.length === 0) {
31
- ctx.ui.notify(sourceFilter ? `No ${sourceFilter} commands found` : "No commands found", "info");
32
- return;
33
- }
34
-
35
- // Build selection items grouped by source
36
- const formatCommand = (cmd: SlashCommandInfo): string => {
37
- const desc = cmd.description ? ` - ${cmd.description}` : "";
38
- return `/${cmd.name}${desc}`;
39
- };
40
-
41
- const items: string[] = [];
42
- const sources: Array<{ key: "extension" | "prompt" | "skill"; label: string }> = [
43
- { key: "extension", label: "Extensions" },
44
- { key: "prompt", label: "Prompts" },
45
- { key: "skill", label: "Skills" },
46
- ];
47
-
48
- for (const { key, label } of sources) {
49
- const cmds = filtered.filter((c) => c.source === key);
50
- if (cmds.length > 0) {
51
- items.push(`--- ${label} ---`);
52
- items.push(...cmds.map(formatCommand));
53
- }
54
- }
55
-
56
- // Show in a selector (user can scroll and see all commands)
57
- const selected = await ctx.ui.select("Available Commands", items);
58
-
59
- // If user selected a command (not a header), offer to show its path
60
- if (selected && !selected.startsWith("---")) {
61
- const cmdName = selected.split(" - ")[0].slice(1); // Remove leading /
62
- const cmd = commands.find((c) => c.name === cmdName);
63
- if (cmd?.sourceInfo.path) {
64
- const showPath = await ctx.ui.confirm(cmd.name, `View source path?\n${cmd.sourceInfo.path}`);
65
- if (showPath) {
66
- ctx.ui.notify(cmd.sourceInfo.path, "info");
67
- }
68
- }
69
- }
70
- },
71
- });
72
- }
1
+ /**
2
+ * Commands Extension
3
+ *
4
+ * Demonstrates the pi.getCommands() API by providing a /commands command
5
+ * that lists all available slash commands in the current session.
6
+ *
7
+ * Usage:
8
+ * 1. Copy this file to ~/.pi/agent/extensions/ or your project's .pi/extensions/
9
+ * 2. Use /commands to see available commands
10
+ * 3. Use /commands extensions to filter by source
11
+ */
12
+
13
+ import type { ExtensionAPI, SlashCommandInfo } from "@earendil-works/pi-coding-agent";
14
+
15
+ export default function commandsExtension(pi: ExtensionAPI) {
16
+ pi.registerCommand("commands", {
17
+ description: "List available slash commands",
18
+ getArgumentCompletions: (prefix) => {
19
+ const sources = ["extension", "prompt", "skill"];
20
+ const filtered = sources.filter((s) => s.startsWith(prefix));
21
+ return filtered.length > 0 ? filtered.map((s) => ({ value: s, label: s })) : null;
22
+ },
23
+ handler: async (args, ctx) => {
24
+ const commands = pi.getCommands();
25
+ const sourceFilter = args.trim() as "extension" | "prompt" | "skill" | "";
26
+
27
+ // Filter by source if specified
28
+ const filtered = sourceFilter ? commands.filter((c) => c.source === sourceFilter) : commands;
29
+
30
+ if (filtered.length === 0) {
31
+ ctx.ui.notify(sourceFilter ? `No ${sourceFilter} commands found` : "No commands found", "info");
32
+ return;
33
+ }
34
+
35
+ // Build selection items grouped by source
36
+ const formatCommand = (cmd: SlashCommandInfo): string => {
37
+ const desc = cmd.description ? ` - ${cmd.description}` : "";
38
+ return `/${cmd.name}${desc}`;
39
+ };
40
+
41
+ const items: string[] = [];
42
+ const sources: Array<{ key: "extension" | "prompt" | "skill"; label: string }> = [
43
+ { key: "extension", label: "Extensions" },
44
+ { key: "prompt", label: "Prompts" },
45
+ { key: "skill", label: "Skills" },
46
+ ];
47
+
48
+ for (const { key, label } of sources) {
49
+ const cmds = filtered.filter((c) => c.source === key);
50
+ if (cmds.length > 0) {
51
+ items.push(`--- ${label} ---`);
52
+ items.push(...cmds.map(formatCommand));
53
+ }
54
+ }
55
+
56
+ // Show in a selector (user can scroll and see all commands)
57
+ const selected = await ctx.ui.select("Available Commands", items);
58
+
59
+ // If user selected a command (not a header), offer to show its path
60
+ if (selected && !selected.startsWith("---")) {
61
+ const cmdName = selected.split(" - ")[0].slice(1); // Remove leading /
62
+ const cmd = commands.find((c) => c.name === cmdName);
63
+ if (cmd?.sourceInfo.path) {
64
+ const showPath = await ctx.ui.confirm(cmd.name, `View source path?\n${cmd.sourceInfo.path}`);
65
+ if (showPath) {
66
+ ctx.ui.notify(cmd.sourceInfo.path, "info");
67
+ }
68
+ }
69
+ }
70
+ },
71
+ });
72
+ }
@@ -1,59 +1,59 @@
1
- /**
2
- * Confirm Destructive Actions Extension
3
- *
4
- * Prompts for confirmation before destructive session actions (clear, switch, branch).
5
- * Demonstrates how to cancel session events using the before_* events.
6
- */
7
-
8
- import type { ExtensionAPI, SessionBeforeSwitchEvent, SessionMessageEntry } from "@earendil-works/pi-coding-agent";
9
-
10
- export default function (pi: ExtensionAPI) {
11
- pi.on("session_before_switch", async (event: SessionBeforeSwitchEvent, ctx) => {
12
- if (!ctx.hasUI) return;
13
-
14
- if (event.reason === "new") {
15
- const confirmed = await ctx.ui.confirm(
16
- "Clear session?",
17
- "This will delete all messages in the current session.",
18
- );
19
-
20
- if (!confirmed) {
21
- ctx.ui.notify("Clear cancelled", "info");
22
- return { cancel: true };
23
- }
24
- return;
25
- }
26
-
27
- // reason === "resume" - check if there are unsaved changes (messages since last assistant response)
28
- const entries = ctx.sessionManager.getEntries();
29
- const hasUnsavedWork = entries.some(
30
- (e): e is SessionMessageEntry => e.type === "message" && e.message.role === "user",
31
- );
32
-
33
- if (hasUnsavedWork) {
34
- const confirmed = await ctx.ui.confirm(
35
- "Switch session?",
36
- "You have messages in the current session. Switch anyway?",
37
- );
38
-
39
- if (!confirmed) {
40
- ctx.ui.notify("Switch cancelled", "info");
41
- return { cancel: true };
42
- }
43
- }
44
- });
45
-
46
- pi.on("session_before_fork", async (event, ctx) => {
47
- if (!ctx.hasUI) return;
48
-
49
- const choice = await ctx.ui.select(`Fork from entry ${event.entryId.slice(0, 8)}?`, [
50
- "Yes, create fork",
51
- "No, stay in current session",
52
- ]);
53
-
54
- if (choice !== "Yes, create fork") {
55
- ctx.ui.notify("Fork cancelled", "info");
56
- return { cancel: true };
57
- }
58
- });
59
- }
1
+ /**
2
+ * Confirm Destructive Actions Extension
3
+ *
4
+ * Prompts for confirmation before destructive session actions (clear, switch, branch).
5
+ * Demonstrates how to cancel session events using the before_* events.
6
+ */
7
+
8
+ import type { ExtensionAPI, SessionBeforeSwitchEvent, SessionMessageEntry } from "@earendil-works/pi-coding-agent";
9
+
10
+ export default function (pi: ExtensionAPI) {
11
+ pi.on("session_before_switch", async (event: SessionBeforeSwitchEvent, ctx) => {
12
+ if (!ctx.hasUI) return;
13
+
14
+ if (event.reason === "new") {
15
+ const confirmed = await ctx.ui.confirm(
16
+ "Clear session?",
17
+ "This will delete all messages in the current session.",
18
+ );
19
+
20
+ if (!confirmed) {
21
+ ctx.ui.notify("Clear cancelled", "info");
22
+ return { cancel: true };
23
+ }
24
+ return;
25
+ }
26
+
27
+ // reason === "resume" - check if there are unsaved changes (messages since last assistant response)
28
+ const entries = ctx.sessionManager.getEntries();
29
+ const hasUnsavedWork = entries.some(
30
+ (e): e is SessionMessageEntry => e.type === "message" && e.message.role === "user",
31
+ );
32
+
33
+ if (hasUnsavedWork) {
34
+ const confirmed = await ctx.ui.confirm(
35
+ "Switch session?",
36
+ "You have messages in the current session. Switch anyway?",
37
+ );
38
+
39
+ if (!confirmed) {
40
+ ctx.ui.notify("Switch cancelled", "info");
41
+ return { cancel: true };
42
+ }
43
+ }
44
+ });
45
+
46
+ pi.on("session_before_fork", async (event, ctx) => {
47
+ if (!ctx.hasUI) return;
48
+
49
+ const choice = await ctx.ui.select(`Fork from entry ${event.entryId.slice(0, 8)}?`, [
50
+ "Yes, create fork",
51
+ "No, stay in current session",
52
+ ]);
53
+
54
+ if (choice !== "Yes, create fork") {
55
+ ctx.ui.notify("Fork cancelled", "info");
56
+ return { cancel: true };
57
+ }
58
+ });
59
+ }
@@ -1,130 +1,130 @@
1
- /**
2
- * Custom Compaction Extension
3
- *
4
- * Replaces the default compaction behavior with a full summary of the entire context.
5
- * Instead of keeping the last 20k tokens of conversation turns, this extension:
6
- * 1. Summarizes ALL messages (messagesToSummarize + turnPrefixMessages)
7
- * 2. Discards all old turns completely, keeping only the summary
8
- *
9
- * This example also demonstrates using a different model (Gemini Flash) for summarization,
10
- * which can be cheaper/faster than the main conversation model.
11
- *
12
- * Usage:
13
- * pi --extension examples/extensions/custom-compaction.ts
14
- */
15
-
16
- import { uuidv7 } from "@earendil-works/pi-ai";
17
- import { complete } from "@earendil-works/pi-ai/compat";
18
- import type { ExtensionAPI } from "@selesai/code";
19
- import { convertToLlm, serializeConversation } from "@selesai/code";
20
-
21
- export default function (pi: ExtensionAPI) {
22
- pi.on("session_before_compact", async (event, ctx) => {
23
- ctx.ui.notify("Custom compaction extension triggered", "info");
24
-
25
- const { preparation, branchEntries: _, signal } = event;
26
- const { messagesToSummarize, turnPrefixMessages, tokensBefore, firstKeptEntryId, previousSummary } = preparation;
27
-
28
- // Use Gemini Flash for summarization (cheaper/faster than most conversation models)
29
- const model = ctx.modelRegistry.find("google", "gemini-2.5-flash");
30
- if (!model) {
31
- ctx.ui.notify(`Could not find Gemini Flash model, using default compaction`, "warning");
32
- return;
33
- }
34
-
35
- // Resolve request auth for the summarization model
36
- const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
37
- if (!auth.ok) {
38
- ctx.ui.notify(`Compaction auth failed: ${auth.error}`, "warning");
39
- return;
40
- }
41
- if (!auth.apiKey) {
42
- ctx.ui.notify(`No API key for ${model.provider}, using default compaction`, "warning");
43
- return;
44
- }
45
-
46
- // Combine all messages for full summary
47
- const allMessages = [...messagesToSummarize, ...turnPrefixMessages];
48
-
49
- ctx.ui.notify(
50
- `Custom compaction: summarizing ${allMessages.length} messages (${tokensBefore.toLocaleString()} tokens) with ${model.id}...`,
51
- "info",
52
- );
53
-
54
- // Convert messages to readable text format
55
- const conversationText = serializeConversation(convertToLlm(allMessages));
56
-
57
- // Include previous summary context if available
58
- const previousContext = previousSummary ? `\n\nPrevious session summary for context:\n${previousSummary}` : "";
59
-
60
- // Build messages that ask for a comprehensive summary
61
- const summaryMessages = [
62
- {
63
- role: "user" as const,
64
- content: [
65
- {
66
- type: "text" as const,
67
- text: `You are a conversation summarizer. Create a comprehensive summary of this conversation that captures:${previousContext}
68
-
69
- 1. The main goals and objectives discussed
70
- 2. Key decisions made and their rationale
71
- 3. Important code changes, file modifications, or technical details
72
- 4. Current state of any ongoing work
73
- 5. Any blockers, issues, or open questions
74
- 6. Next steps that were planned or suggested
75
-
76
- Be thorough but concise. The summary will replace the ENTIRE conversation history, so include all information needed to continue the work effectively.
77
-
78
- Format the summary as structured markdown with clear sections.
79
-
80
- <conversation>
81
- ${conversationText}
82
- </conversation>`,
83
- },
84
- ],
85
- timestamp: Date.now(),
86
- },
87
- ];
88
-
89
- try {
90
- // Pass signal to honor abort requests (e.g., user cancels compaction)
91
- const response = await complete(
92
- model,
93
- { messages: summaryMessages },
94
- {
95
- apiKey: auth.apiKey,
96
- headers: auth.headers,
97
- maxTokens: 8192,
98
- signal,
99
- cacheRetention: "none",
100
- sessionId: uuidv7(),
101
- },
102
- );
103
-
104
- const summary = response.content
105
- .filter((c): c is { type: "text"; text: string } => c.type === "text")
106
- .map((c) => c.text)
107
- .join("\n");
108
-
109
- if (!summary.trim()) {
110
- if (!signal.aborted) ctx.ui.notify("Compaction summary was empty, using default compaction", "warning");
111
- return;
112
- }
113
-
114
- // Return compaction content - SessionManager adds id/parentId
115
- // Use firstKeptEntryId from preparation to keep recent messages
116
- return {
117
- compaction: {
118
- summary,
119
- firstKeptEntryId,
120
- tokensBefore,
121
- },
122
- };
123
- } catch (error) {
124
- const message = error instanceof Error ? error.message : String(error);
125
- ctx.ui.notify(`Compaction failed: ${message}`, "error");
126
- // Fall back to default compaction on error
127
- return;
128
- }
129
- });
130
- }
1
+ /**
2
+ * Custom Compaction Extension
3
+ *
4
+ * Replaces the default compaction behavior with a full summary of the entire context.
5
+ * Instead of keeping the last 20k tokens of conversation turns, this extension:
6
+ * 1. Summarizes ALL messages (messagesToSummarize + turnPrefixMessages)
7
+ * 2. Discards all old turns completely, keeping only the summary
8
+ *
9
+ * This example also demonstrates using a different model (Gemini Flash) for summarization,
10
+ * which can be cheaper/faster than the main conversation model.
11
+ *
12
+ * Usage:
13
+ * pi --extension examples/extensions/custom-compaction.ts
14
+ */
15
+
16
+ import { uuidv7 } from "@earendil-works/pi-ai";
17
+ import { complete } from "@earendil-works/pi-ai/compat";
18
+ import type { ExtensionAPI } from "@selesai/code";
19
+ import { convertToLlm, serializeConversation } from "@selesai/code";
20
+
21
+ export default function (pi: ExtensionAPI) {
22
+ pi.on("session_before_compact", async (event, ctx) => {
23
+ ctx.ui.notify("Custom compaction extension triggered", "info");
24
+
25
+ const { preparation, branchEntries: _, signal } = event;
26
+ const { messagesToSummarize, turnPrefixMessages, tokensBefore, firstKeptEntryId, previousSummary } = preparation;
27
+
28
+ // Use Gemini Flash for summarization (cheaper/faster than most conversation models)
29
+ const model = ctx.modelRegistry.find("google", "gemini-2.5-flash");
30
+ if (!model) {
31
+ ctx.ui.notify(`Could not find Gemini Flash model, using default compaction`, "warning");
32
+ return;
33
+ }
34
+
35
+ // Resolve request auth for the summarization model
36
+ const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
37
+ if (!auth.ok) {
38
+ ctx.ui.notify(`Compaction auth failed: ${auth.error}`, "warning");
39
+ return;
40
+ }
41
+ if (!auth.apiKey) {
42
+ ctx.ui.notify(`No API key for ${model.provider}, using default compaction`, "warning");
43
+ return;
44
+ }
45
+
46
+ // Combine all messages for full summary
47
+ const allMessages = [...messagesToSummarize, ...turnPrefixMessages];
48
+
49
+ ctx.ui.notify(
50
+ `Custom compaction: summarizing ${allMessages.length} messages (${tokensBefore.toLocaleString()} tokens) with ${model.id}...`,
51
+ "info",
52
+ );
53
+
54
+ // Convert messages to readable text format
55
+ const conversationText = serializeConversation(convertToLlm(allMessages));
56
+
57
+ // Include previous summary context if available
58
+ const previousContext = previousSummary ? `\n\nPrevious session summary for context:\n${previousSummary}` : "";
59
+
60
+ // Build messages that ask for a comprehensive summary
61
+ const summaryMessages = [
62
+ {
63
+ role: "user" as const,
64
+ content: [
65
+ {
66
+ type: "text" as const,
67
+ text: `You are a conversation summarizer. Create a comprehensive summary of this conversation that captures:${previousContext}
68
+
69
+ 1. The main goals and objectives discussed
70
+ 2. Key decisions made and their rationale
71
+ 3. Important code changes, file modifications, or technical details
72
+ 4. Current state of any ongoing work
73
+ 5. Any blockers, issues, or open questions
74
+ 6. Next steps that were planned or suggested
75
+
76
+ Be thorough but concise. The summary will replace the ENTIRE conversation history, so include all information needed to continue the work effectively.
77
+
78
+ Format the summary as structured markdown with clear sections.
79
+
80
+ <conversation>
81
+ ${conversationText}
82
+ </conversation>`,
83
+ },
84
+ ],
85
+ timestamp: Date.now(),
86
+ },
87
+ ];
88
+
89
+ try {
90
+ // Pass signal to honor abort requests (e.g., user cancels compaction)
91
+ const response = await complete(
92
+ model,
93
+ { messages: summaryMessages },
94
+ {
95
+ apiKey: auth.apiKey,
96
+ headers: auth.headers,
97
+ maxTokens: 8192,
98
+ signal,
99
+ cacheRetention: "none",
100
+ sessionId: uuidv7(),
101
+ },
102
+ );
103
+
104
+ const summary = response.content
105
+ .filter((c): c is { type: "text"; text: string } => c.type === "text")
106
+ .map((c) => c.text)
107
+ .join("\n");
108
+
109
+ if (!summary.trim()) {
110
+ if (!signal.aborted) ctx.ui.notify("Compaction summary was empty, using default compaction", "warning");
111
+ return;
112
+ }
113
+
114
+ // Return compaction content - SessionManager adds id/parentId
115
+ // Use firstKeptEntryId from preparation to keep recent messages
116
+ return {
117
+ compaction: {
118
+ summary,
119
+ firstKeptEntryId,
120
+ tokensBefore,
121
+ },
122
+ };
123
+ } catch (error) {
124
+ const message = error instanceof Error ? error.message : String(error);
125
+ ctx.ui.notify(`Compaction failed: ${message}`, "error");
126
+ // Fall back to default compaction on error
127
+ return;
128
+ }
129
+ });
130
+ }
@@ -1,64 +1,64 @@
1
- /**
2
- * Custom Footer Extension - demonstrates ctx.ui.setFooter()
3
- *
4
- * footerData exposes data not otherwise accessible:
5
- * - getGitBranch(): current git branch
6
- * - getExtensionStatuses(): texts from ctx.ui.setStatus()
7
- *
8
- * Token stats come from ctx.sessionManager/ctx.model (already accessible).
9
- */
10
-
11
- import type { AssistantMessage } from "@earendil-works/pi-ai";
12
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
13
- import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
14
-
15
- export default function (pi: ExtensionAPI) {
16
- let enabled = false;
17
-
18
- pi.registerCommand("footer", {
19
- description: "Toggle custom footer",
20
- handler: async (_args, ctx) => {
21
- enabled = !enabled;
22
-
23
- if (enabled) {
24
- ctx.ui.setFooter((tui, theme, footerData) => {
25
- const unsub = footerData.onBranchChange(() => tui.requestRender());
26
-
27
- return {
28
- dispose: unsub,
29
- invalidate() {},
30
- render(width: number): string[] {
31
- // Compute tokens from ctx (already accessible to extensions)
32
- let input = 0,
33
- output = 0,
34
- cost = 0;
35
- for (const e of ctx.sessionManager.getBranch()) {
36
- if (e.type === "message" && e.message.role === "assistant") {
37
- const m = e.message as AssistantMessage;
38
- input += m.usage.input;
39
- output += m.usage.output;
40
- cost += m.usage.cost.total;
41
- }
42
- }
43
-
44
- // Get git branch (not otherwise accessible)
45
- const branch = footerData.getGitBranch();
46
- const fmt = (n: number) => (n < 1000 ? `${n}` : `${(n / 1000).toFixed(1)}k`);
47
-
48
- const left = theme.fg("dim", `↑${fmt(input)} ↓${fmt(output)} $${cost.toFixed(3)}`);
49
- const branchStr = branch ? ` (${branch})` : "";
50
- const right = theme.fg("dim", `${ctx.model?.id || "no-model"}${branchStr}`);
51
-
52
- const pad = " ".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
53
- return [truncateToWidth(left + pad + right, width)];
54
- },
55
- };
56
- });
57
- ctx.ui.notify("Custom footer enabled", "info");
58
- } else {
59
- ctx.ui.setFooter(undefined);
60
- ctx.ui.notify("Default footer restored", "info");
61
- }
62
- },
63
- });
64
- }
1
+ /**
2
+ * Custom Footer Extension - demonstrates ctx.ui.setFooter()
3
+ *
4
+ * footerData exposes data not otherwise accessible:
5
+ * - getGitBranch(): current git branch
6
+ * - getExtensionStatuses(): texts from ctx.ui.setStatus()
7
+ *
8
+ * Token stats come from ctx.sessionManager/ctx.model (already accessible).
9
+ */
10
+
11
+ import type { AssistantMessage } from "@earendil-works/pi-ai";
12
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
13
+ import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
14
+
15
+ export default function (pi: ExtensionAPI) {
16
+ let enabled = false;
17
+
18
+ pi.registerCommand("footer", {
19
+ description: "Toggle custom footer",
20
+ handler: async (_args, ctx) => {
21
+ enabled = !enabled;
22
+
23
+ if (enabled) {
24
+ ctx.ui.setFooter((tui, theme, footerData) => {
25
+ const unsub = footerData.onBranchChange(() => tui.requestRender());
26
+
27
+ return {
28
+ dispose: unsub,
29
+ invalidate() {},
30
+ render(width: number): string[] {
31
+ // Compute tokens from ctx (already accessible to extensions)
32
+ let input = 0,
33
+ output = 0,
34
+ cost = 0;
35
+ for (const e of ctx.sessionManager.getBranch()) {
36
+ if (e.type === "message" && e.message.role === "assistant") {
37
+ const m = e.message as AssistantMessage;
38
+ input += m.usage.input;
39
+ output += m.usage.output;
40
+ cost += m.usage.cost.total;
41
+ }
42
+ }
43
+
44
+ // Get git branch (not otherwise accessible)
45
+ const branch = footerData.getGitBranch();
46
+ const fmt = (n: number) => (n < 1000 ? `${n}` : `${(n / 1000).toFixed(1)}k`);
47
+
48
+ const left = theme.fg("dim", `↑${fmt(input)} ↓${fmt(output)} $${cost.toFixed(3)}`);
49
+ const branchStr = branch ? ` (${branch})` : "";
50
+ const right = theme.fg("dim", `${ctx.model?.id || "no-model"}${branchStr}`);
51
+
52
+ const pad = " ".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
53
+ return [truncateToWidth(left + pad + right, width)];
54
+ },
55
+ };
56
+ });
57
+ ctx.ui.notify("Custom footer enabled", "info");
58
+ } else {
59
+ ctx.ui.setFooter(undefined);
60
+ ctx.ui.notify("Default footer restored", "info");
61
+ }
62
+ },
63
+ });
64
+ }