@astralyn/pi 0.82.6 → 0.83.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/CHANGELOG.md +61 -5099
  2. package/README.md +12 -111
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +9 -2
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli/credential-print.d.ts +23 -0
  7. package/dist/cli/credential-print.d.ts.map +1 -0
  8. package/dist/cli/credential-print.js +116 -0
  9. package/dist/cli/credential-print.js.map +1 -0
  10. package/dist/cli/session-picker.d.ts.map +1 -1
  11. package/dist/cli/session-picker.js +11 -13
  12. package/dist/cli/session-picker.js.map +1 -1
  13. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  14. package/dist/core/agent-session-runtime.js +3 -0
  15. package/dist/core/agent-session-runtime.js.map +1 -1
  16. package/dist/core/agent-session.d.ts +1 -1
  17. package/dist/core/agent-session.d.ts.map +1 -1
  18. package/dist/core/agent-session.js +13 -6
  19. package/dist/core/agent-session.js.map +1 -1
  20. package/dist/core/extensions/runner.d.ts +1 -0
  21. package/dist/core/extensions/runner.d.ts.map +1 -1
  22. package/dist/core/extensions/runner.js +7 -0
  23. package/dist/core/extensions/runner.js.map +1 -1
  24. package/dist/core/extensions/types.d.ts +11 -0
  25. package/dist/core/extensions/types.d.ts.map +1 -1
  26. package/dist/core/extensions/types.js.map +1 -1
  27. package/dist/core/footer-data-provider.d.ts +10 -0
  28. package/dist/core/footer-data-provider.d.ts.map +1 -1
  29. package/dist/core/footer-data-provider.js +1 -1
  30. package/dist/core/footer-data-provider.js.map +1 -1
  31. package/dist/core/model-runtime.d.ts +2 -0
  32. package/dist/core/model-runtime.d.ts.map +1 -1
  33. package/dist/core/model-runtime.js.map +1 -1
  34. package/dist/core/package-manager.d.ts.map +1 -1
  35. package/dist/core/package-manager.js +13 -6
  36. package/dist/core/package-manager.js.map +1 -1
  37. package/dist/core/resource-loader.d.ts +15 -0
  38. package/dist/core/resource-loader.d.ts.map +1 -1
  39. package/dist/core/resource-loader.js +70 -9
  40. package/dist/core/resource-loader.js.map +1 -1
  41. package/dist/core/session-manager.d.ts.map +1 -1
  42. package/dist/core/session-manager.js +2 -2
  43. package/dist/core/session-manager.js.map +1 -1
  44. package/dist/core/tools/bash.d.ts +1 -5
  45. package/dist/core/tools/bash.d.ts.map +1 -1
  46. package/dist/core/tools/bash.js +16 -41
  47. package/dist/core/tools/bash.js.map +1 -1
  48. package/dist/core/tools/edit.d.ts.map +1 -1
  49. package/dist/core/tools/edit.js +42 -11
  50. package/dist/core/tools/edit.js.map +1 -1
  51. package/dist/core/tools/find.d.ts.map +1 -1
  52. package/dist/core/tools/find.js +40 -11
  53. package/dist/core/tools/find.js.map +1 -1
  54. package/dist/core/tools/grep.d.ts.map +1 -1
  55. package/dist/core/tools/grep.js +17 -8
  56. package/dist/core/tools/grep.js.map +1 -1
  57. package/dist/core/tools/ls.d.ts.map +1 -1
  58. package/dist/core/tools/ls.js +3 -3
  59. package/dist/core/tools/ls.js.map +1 -1
  60. package/dist/core/tools/read.d.ts.map +1 -1
  61. package/dist/core/tools/read.js +4 -4
  62. package/dist/core/tools/read.js.map +1 -1
  63. package/dist/core/tools/render-utils.d.ts +7 -0
  64. package/dist/core/tools/render-utils.d.ts.map +1 -1
  65. package/dist/core/tools/render-utils.js +11 -0
  66. package/dist/core/tools/render-utils.js.map +1 -1
  67. package/dist/core/tools/write.d.ts.map +1 -1
  68. package/dist/core/tools/write.js +2 -3
  69. package/dist/core/tools/write.js.map +1 -1
  70. package/dist/core/trust-manager.d.ts.map +1 -1
  71. package/dist/core/trust-manager.js +48 -9
  72. package/dist/core/trust-manager.js.map +1 -1
  73. package/dist/extensions/deepwiki/render.d.ts.map +1 -1
  74. package/dist/extensions/deepwiki/render.js +34 -22
  75. package/dist/extensions/deepwiki/render.js.map +1 -1
  76. package/dist/extensions/index.d.ts.map +1 -1
  77. package/dist/extensions/index.js +0 -2
  78. package/dist/extensions/index.js.map +1 -1
  79. package/dist/extensions/llama/provider.d.ts.map +1 -1
  80. package/dist/extensions/llama/provider.js +1 -1
  81. package/dist/extensions/llama/provider.js.map +1 -1
  82. package/dist/extensions/question/dialog.d.ts.map +1 -1
  83. package/dist/extensions/question/dialog.js +29 -10
  84. package/dist/extensions/question/dialog.js.map +1 -1
  85. package/dist/extensions/question/index.d.ts.map +1 -1
  86. package/dist/extensions/question/index.js +7 -2
  87. package/dist/extensions/question/index.js.map +1 -1
  88. package/dist/extensions/question/render.d.ts +4 -8
  89. package/dist/extensions/question/render.d.ts.map +1 -1
  90. package/dist/extensions/question/render.js +202 -29
  91. package/dist/extensions/question/render.js.map +1 -1
  92. package/dist/extensions/question/results.d.ts.map +1 -1
  93. package/dist/extensions/question/results.js +1 -0
  94. package/dist/extensions/question/results.js.map +1 -1
  95. package/dist/extensions/question/schema.d.ts.map +1 -1
  96. package/dist/extensions/question/schema.js +2 -2
  97. package/dist/extensions/question/schema.js.map +1 -1
  98. package/dist/extensions/question/types.d.ts +1 -0
  99. package/dist/extensions/question/types.d.ts.map +1 -1
  100. package/dist/extensions/question/types.js +1 -1
  101. package/dist/extensions/question/types.js.map +1 -1
  102. package/dist/extensions/router/stream.d.ts +1 -0
  103. package/dist/extensions/router/stream.d.ts.map +1 -1
  104. package/dist/extensions/router/stream.js +44 -13
  105. package/dist/extensions/router/stream.js.map +1 -1
  106. package/dist/extensions/subagent/constants.d.ts +1 -0
  107. package/dist/extensions/subagent/constants.d.ts.map +1 -1
  108. package/dist/extensions/subagent/constants.js +1 -0
  109. package/dist/extensions/subagent/constants.js.map +1 -1
  110. package/dist/extensions/subagent/index.d.ts.map +1 -1
  111. package/dist/extensions/subagent/index.js +2 -0
  112. package/dist/extensions/subagent/index.js.map +1 -1
  113. package/dist/extensions/subagent/render.d.ts.map +1 -1
  114. package/dist/extensions/subagent/render.js +121 -48
  115. package/dist/extensions/subagent/render.js.map +1 -1
  116. package/dist/extensions/subagent/retry.d.ts +17 -0
  117. package/dist/extensions/subagent/retry.d.ts.map +1 -0
  118. package/dist/extensions/subagent/retry.js +41 -0
  119. package/dist/extensions/subagent/retry.js.map +1 -0
  120. package/dist/extensions/subagent/runner.d.ts.map +1 -1
  121. package/dist/extensions/subagent/runner.js +33 -5
  122. package/dist/extensions/subagent/runner.js.map +1 -1
  123. package/dist/extensions/subagent/sdk-runner.d.ts +6 -0
  124. package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
  125. package/dist/extensions/subagent/sdk-runner.js +46 -24
  126. package/dist/extensions/subagent/sdk-runner.js.map +1 -1
  127. package/dist/extensions/subagent/types.d.ts +7 -0
  128. package/dist/extensions/subagent/types.d.ts.map +1 -1
  129. package/dist/extensions/subagent/types.js.map +1 -1
  130. package/dist/extensions/todo/index.d.ts.map +1 -1
  131. package/dist/extensions/todo/index.js +1 -1
  132. package/dist/extensions/todo/index.js.map +1 -1
  133. package/dist/extensions/todo/state.d.ts.map +1 -1
  134. package/dist/extensions/todo/state.js +10 -2
  135. package/dist/extensions/todo/state.js.map +1 -1
  136. package/dist/extensions/todo/view.d.ts +1 -1
  137. package/dist/extensions/todo/view.d.ts.map +1 -1
  138. package/dist/extensions/todo/view.js +179 -34
  139. package/dist/extensions/todo/view.js.map +1 -1
  140. package/dist/main.d.ts.map +1 -1
  141. package/dist/main.js +43 -0
  142. package/dist/main.js.map +1 -1
  143. package/dist/migrations.d.ts.map +1 -1
  144. package/dist/migrations.js +2 -1
  145. package/dist/migrations.js.map +1 -1
  146. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  147. package/dist/modes/interactive/components/armin.js +5 -0
  148. package/dist/modes/interactive/components/armin.js.map +1 -1
  149. package/dist/modes/interactive/components/bash-execution.d.ts +1 -0
  150. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  151. package/dist/modes/interactive/components/bash-execution.js +4 -2
  152. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  153. package/dist/modes/interactive/components/countdown-timer.d.ts +13 -6
  154. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  155. package/dist/modes/interactive/components/countdown-timer.js +54 -17
  156. package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
  157. package/dist/modes/interactive/components/extension-selector.d.ts +1 -1
  158. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  159. package/dist/modes/interactive/components/extension-selector.js +10 -6
  160. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  161. package/dist/modes/interactive/components/keybinding-hints.d.ts +11 -1
  162. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  163. package/dist/modes/interactive/components/keybinding-hints.js +56 -0
  164. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  165. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  166. package/dist/modes/interactive/components/model-selector.js +4 -1
  167. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  168. package/dist/modes/interactive/components/session-selector.d.ts +3 -0
  169. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  170. package/dist/modes/interactive/components/session-selector.js +52 -2
  171. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  172. package/dist/modes/interactive/components/tool-execution.d.ts +10 -0
  173. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  174. package/dist/modes/interactive/components/tool-execution.js +133 -18
  175. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  176. package/dist/modes/interactive/components/tool-group.d.ts +1 -0
  177. package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
  178. package/dist/modes/interactive/components/tool-group.js +5 -0
  179. package/dist/modes/interactive/components/tool-group.js.map +1 -1
  180. package/dist/modes/interactive/components/tree-selector.d.ts +10 -1
  181. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  182. package/dist/modes/interactive/components/tree-selector.js +55 -2
  183. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  184. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  185. package/dist/modes/interactive/external-editor.js +40 -3
  186. package/dist/modes/interactive/external-editor.js.map +1 -1
  187. package/dist/modes/interactive/interactive-mode.d.ts +5 -0
  188. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  189. package/dist/modes/interactive/interactive-mode.js +92 -24
  190. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  191. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  192. package/dist/modes/interactive/theme/theme.js +3 -2
  193. package/dist/modes/interactive/theme/theme.js.map +1 -1
  194. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  195. package/dist/modes/rpc/rpc-mode.js +13 -0
  196. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  197. package/dist/utils/paths.d.ts +5 -0
  198. package/dist/utils/paths.d.ts.map +1 -1
  199. package/dist/utils/paths.js +7 -0
  200. package/dist/utils/paths.js.map +1 -1
  201. package/docs/bundled/README.md +13 -15
  202. package/docs/bundled/extensions/deepwiki.md +1 -1
  203. package/docs/bundled/extensions/question.md +7 -5
  204. package/docs/bundled/extensions/router.md +3 -4
  205. package/docs/bundled/extensions/statusline.md +0 -1
  206. package/docs/bundled/extensions/subagent.md +4 -3
  207. package/docs/bundled/extensions/todo.md +1 -1
  208. package/docs/bundled/themes.md +1 -1
  209. package/docs/bundled/tool-presentation.md +17 -16
  210. package/docs/compaction.md +25 -30
  211. package/docs/custom-provider.md +298 -583
  212. package/docs/docs.json +0 -8
  213. package/docs/extensions.md +132 -121
  214. package/docs/index.md +18 -74
  215. package/docs/json.md +20 -14
  216. package/docs/models.md +193 -151
  217. package/docs/providers.md +2 -1
  218. package/docs/quickstart.md +3 -3
  219. package/docs/rpc.md +166 -124
  220. package/docs/sdk.md +119 -178
  221. package/docs/security.md +1 -1
  222. package/docs/session-format.md +139 -119
  223. package/docs/skills.md +0 -1
  224. package/docs/themes.md +2 -2
  225. package/docs/tui.md +411 -549
  226. package/docs/usage.md +3 -11
  227. package/examples/README.md +6 -1
  228. package/examples/extensions/README.md +13 -33
  229. package/examples/extensions/custom-provider-anthropic/index.ts +9 -3
  230. package/examples/extensions/dynamic-resources/dynamic.json +1 -1
  231. package/examples/extensions/truncated-tool.ts +2 -2
  232. package/examples/sdk/12-full-control.ts +2 -0
  233. package/npm-shrinkwrap.json +25 -25
  234. package/package.json +5 -5
  235. package/dist/extensions/plan/constants.d.ts +0 -40
  236. package/dist/extensions/plan/constants.d.ts.map +0 -1
  237. package/dist/extensions/plan/constants.js +0 -69
  238. package/dist/extensions/plan/constants.js.map +0 -1
  239. package/dist/extensions/plan/index.d.ts +0 -3
  240. package/dist/extensions/plan/index.d.ts.map +0 -1
  241. package/dist/extensions/plan/index.js +0 -323
  242. package/dist/extensions/plan/index.js.map +0 -1
  243. package/dist/extensions/plan/schema.d.ts +0 -15
  244. package/dist/extensions/plan/schema.d.ts.map +0 -1
  245. package/dist/extensions/plan/schema.js +0 -13
  246. package/dist/extensions/plan/schema.js.map +0 -1
  247. package/dist/extensions/plan/state.d.ts +0 -29
  248. package/dist/extensions/plan/state.d.ts.map +0 -1
  249. package/dist/extensions/plan/state.js +0 -74
  250. package/dist/extensions/plan/state.js.map +0 -1
  251. package/dist/extensions/plan/storage.d.ts +0 -18
  252. package/dist/extensions/plan/storage.d.ts.map +0 -1
  253. package/dist/extensions/plan/storage.js +0 -58
  254. package/dist/extensions/plan/storage.js.map +0 -1
  255. package/dist/extensions/plan/view.d.ts +0 -36
  256. package/dist/extensions/plan/view.d.ts.map +0 -1
  257. package/dist/extensions/plan/view.js +0 -74
  258. package/dist/extensions/plan/view.js.map +0 -1
  259. package/docs/bundled/extensions/README.md +0 -19
  260. package/docs/bundled/extensions/plan.md +0 -33
  261. package/examples/extensions/plan-mode/README.md +0 -66
  262. package/examples/extensions/plan-mode/index.ts +0 -390
  263. package/examples/extensions/plan-mode/utils.ts +0 -168
@@ -1 +0,0 @@
1
- {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/extensions/plan/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAU9C,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAU;IACtD,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;AAAA,CAC/C;AAED,uEAAuE;AACvE,MAAM,UAAU,YAAY,CAAC,KAAa,EAAU;IACnD,MAAM,IAAI,GAAG,KAAK;SAChB,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACrB,OAAO,IAAI,IAAI,MAAM,CAAC;AAAA,CACtB;AAED,2EAA2E;AAC3E,MAAM,UAAU,cAAc,CAAC,aAAuB,EAAU;IAC/D,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,KAAK;YAAE,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,GAAG,GAAG,CAAC,CAAC;AAAA,CACf;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAiD,EAAU;IAC5F,MAAM,KAAK,GAAG;QACb,KAAK;QACL,UAAU,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;QACzC,YAAY,OAAO,CAAC,UAAU,EAAE;QAChC,QAAQ,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QACrC,YAAY,OAAO,CAAC,SAAS,EAAE;KAC/B,CAAC;IACF,IAAI,OAAO,CAAC,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC/E,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IAClD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CACxB;AAED,2DAA2D;AAC3D,MAAM,UAAU,YAAY,CAAC,OAAwB,EAAU;IAC9D,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC3C,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,cAAc,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IACxF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjC,aAAa,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;IACtG,OAAO,IAAI,CAAC;AAAA,CACZ","sourcesContent":["/**\n * plan/storage.ts — plan file layout and writing.\n *\n * Plans live under `<agentDir>/plans/<sessionId>/NN-<slug>.md`, append-only\n * with a monotonically increasing NN. The directory is only the initial\n * storage location — the authoritative index is the `planFiles` list in the\n * session's plan-mode entries, which follows fork/clone.\n */\nimport { mkdirSync, readdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../../config.ts\";\n\nexport interface SavePlanOptions {\n\tsessionId: string;\n\ttitle: string;\n\tplan: string;\n\tcwd: string;\n\trevises?: string;\n}\n\nexport function getPlansDir(sessionId: string): string {\n\treturn join(getAgentDir(), \"plans\", sessionId);\n}\n\n/** File-name slug from a plan title: lowercase, ascii-ish, bounded. */\nexport function slugifyTitle(title: string): string {\n\tconst slug = title\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9]+/g, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\")\n\t\t.slice(0, 40)\n\t\t.replace(/-+$/, \"\");\n\treturn slug || \"plan\";\n}\n\n/** Next NN based on existing `NN-*.md` names; gaps never reuse numbers. */\nexport function nextPlanNumber(existingNames: string[]): number {\n\tlet max = 0;\n\tfor (const name of existingNames) {\n\t\tconst match = /^(\\d+)-/.exec(name);\n\t\tif (match) max = Math.max(max, Number(match[1]));\n\t}\n\treturn max + 1;\n}\n\nexport function buildPlanDocument(options: SavePlanOptions & { createdIso: string }): string {\n\tconst lines = [\n\t\t\"---\",\n\t\t`title: ${JSON.stringify(options.title)}`,\n\t\t`created: ${options.createdIso}`,\n\t\t`cwd: ${JSON.stringify(options.cwd)}`,\n\t\t`session: ${options.sessionId}`,\n\t];\n\tif (options.revises) lines.push(`revises: ${JSON.stringify(options.revises)}`);\n\tlines.push(\"---\", \"\", options.plan.trimEnd(), \"\");\n\treturn lines.join(\"\\n\");\n}\n\n/** Write the plan to disk and return its absolute path. */\nexport function savePlanFile(options: SavePlanOptions): string {\n\tconst dir = getPlansDir(options.sessionId);\n\tmkdirSync(dir, { recursive: true });\n\tconst number = nextPlanNumber(readdirSync(dir));\n\tconst fileName = `${String(number).padStart(2, \"0\")}-${slugifyTitle(options.title)}.md`;\n\tconst path = join(dir, fileName);\n\twriteFileSync(path, buildPlanDocument({ ...options, createdIso: new Date().toISOString() }), \"utf-8\");\n\treturn path;\n}\n"]}
@@ -1,36 +0,0 @@
1
- /**
2
- * plan/view.ts — approval-dialog context and exit_plan tool rendering.
3
- *
4
- * `exit_plan` carries the whole plan as a parameter, and the default renderer
5
- * echoes parameters verbatim: the row would repeat, escaped onto one line, the
6
- * markdown the model just printed as response text. Collapsed rows therefore
7
- * carry the title only, and the plan body is available on expand.
8
- *
9
- * The result text is written for the model (it restates tool-state precedence
10
- * rules), so the result row renders from `details` instead.
11
- */
12
- import type { AgentToolResult, ExtensionContext } from "../../core/extensions/types.ts";
13
- import type { Theme } from "../../modes/interactive/theme/theme.ts";
14
- import type { ExitPlanDetails } from "./schema.ts";
15
- /** Collapse the home prefix so a plan path stays on one line. */
16
- export declare function shortenPlanPath(path: string): string;
17
- /**
18
- * Context pressure is the fact the compact-or-not decision turns on, so the
19
- * dialog states it instead of making the user leave and check the footer.
20
- */
21
- export declare function formatApprovalSubtitle(ctx: ExtensionContext): string | undefined;
22
- type ExitPlanCallArgs = {
23
- title?: unknown;
24
- plan?: unknown;
25
- revises?: unknown;
26
- };
27
- /** Title-only headline when collapsed; plan body and revision target on expand. */
28
- export declare function formatExitPlanCall(args: ExitPlanCallArgs | undefined, theme: Theme, expanded: boolean): string;
29
- /**
30
- * What the user needs to know: where the plan landed and what happens next.
31
- * Falls back to the model-facing text when details are missing — a thrown
32
- * approval (interrupt) has no details to render from.
33
- */
34
- export declare function formatExitPlanResult(result: AgentToolResult<ExitPlanDetails>, theme: Theme): string;
35
- export {};
36
- //# sourceMappingURL=view.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../../src/extensions/plan/view.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACxF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,KAAK,EAAoB,eAAe,EAAE,MAAM,aAAa,CAAC;AAErE,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOpD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAIhF;AAKD,KAAK,gBAAgB,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAM/E,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAiB9G;AAkBD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,eAAe,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAQnG","sourcesContent":["/**\n * plan/view.ts — approval-dialog context and exit_plan tool rendering.\n *\n * `exit_plan` carries the whole plan as a parameter, and the default renderer\n * echoes parameters verbatim: the row would repeat, escaped onto one line, the\n * markdown the model just printed as response text. Collapsed rows therefore\n * carry the title only, and the plan body is available on expand.\n *\n * The result text is written for the model (it restates tool-state precedence\n * rules), so the result row renders from `details` instead.\n */\nimport type { AgentToolResult, ExtensionContext } from \"../../core/extensions/types.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport type { ExitPlanDecision, ExitPlanDetails } from \"./schema.ts\";\n\n/** Collapse the home prefix so a plan path stays on one line. */\nexport function shortenPlanPath(path: string): string {\n\tconst rawHome = process.env.USERPROFILE || process.env.HOME || \"\";\n\tconst home = rawHome.replace(/[\\\\/]+$/, \"\");\n\tif (!home) return path;\n\tconst normalize = (value: string) => value.replace(/\\\\/g, \"/\").toLowerCase();\n\tif (!normalize(path).startsWith(`${normalize(home)}/`)) return path;\n\treturn `~${path.slice(home.length).replace(/\\\\/g, \"/\")}`;\n}\n\n/**\n * Context pressure is the fact the compact-or-not decision turns on, so the\n * dialog states it instead of making the user leave and check the footer.\n */\nexport function formatApprovalSubtitle(ctx: ExtensionContext): string | undefined {\n\tconst percent = ctx.getContextUsage()?.percent;\n\tif (percent == null) return undefined;\n\treturn `Context now ${percent.toFixed(0)}% full`;\n}\n\n// ---- tool call rendering ----------------------------------------------------\n// Args arrive off the wire and stream in, so every field is read defensively.\n\ntype ExitPlanCallArgs = { title?: unknown; plan?: unknown; revises?: unknown };\n\nfunction callText(value: unknown): string {\n\treturn typeof value === \"string\" ? value.trim() : \"\";\n}\n\n/** Title-only headline when collapsed; plan body and revision target on expand. */\nexport function formatExitPlanCall(args: ExitPlanCallArgs | undefined, theme: Theme, expanded: boolean): string {\n\tconst title = callText(args?.title);\n\tconst headline = [\n\t\ttheme.fg(\"toolTitle\", theme.bold(\"exit_plan\")),\n\t\ttitle ? theme.fg(\"text\", title) : theme.fg(\"dim\", \"…\"),\n\t].join(\" \");\n\tif (!expanded) return headline;\n\n\tconst lines = [headline];\n\tconst revises = callText(args?.revises);\n\tif (revises) lines.push(theme.fg(\"dim\", `revises ${revises}`));\n\tconst plan = callText(args?.plan);\n\tif (plan) {\n\t\tlines.push(\"\");\n\t\tfor (const line of plan.split(\"\\n\")) lines.push(theme.fg(\"dim\", line));\n\t}\n\treturn lines.join(\"\\n\");\n}\n\n// ---- tool result rendering --------------------------------------------------\n\nconst DECISION_SUMMARY: Record<ExitPlanDecision, string> = {\n\texecute: \"Executing with full context\",\n\tcompactAndExecute: \"Compacting context, then executing\",\n\tkeepPlanning: \"Still planning\",\n\tcancelled: \"No approval was pending\",\n};\n\nfunction resultText(result: AgentToolResult<ExitPlanDetails>): string {\n\treturn result.content\n\t\t.filter((block): block is { type: \"text\"; text: string } => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\"\\n\");\n}\n\n/**\n * What the user needs to know: where the plan landed and what happens next.\n * Falls back to the model-facing text when details are missing — a thrown\n * approval (interrupt) has no details to render from.\n */\nexport function formatExitPlanResult(result: AgentToolResult<ExitPlanDetails>, theme: Theme): string {\n\tconst details = result.details;\n\tif (!details) return theme.fg(\"dim\", resultText(result));\n\n\tconst lines: string[] = [];\n\tif (details.planPath) lines.push(theme.fg(\"success\", `Saved to ${shortenPlanPath(details.planPath)}`));\n\tlines.push(theme.fg(\"dim\", DECISION_SUMMARY[details.decision]));\n\treturn lines.join(\"\\n\");\n}\n"]}
@@ -1,74 +0,0 @@
1
- /** Collapse the home prefix so a plan path stays on one line. */
2
- export function shortenPlanPath(path) {
3
- const rawHome = process.env.USERPROFILE || process.env.HOME || "";
4
- const home = rawHome.replace(/[\\/]+$/, "");
5
- if (!home)
6
- return path;
7
- const normalize = (value) => value.replace(/\\/g, "/").toLowerCase();
8
- if (!normalize(path).startsWith(`${normalize(home)}/`))
9
- return path;
10
- return `~${path.slice(home.length).replace(/\\/g, "/")}`;
11
- }
12
- /**
13
- * Context pressure is the fact the compact-or-not decision turns on, so the
14
- * dialog states it instead of making the user leave and check the footer.
15
- */
16
- export function formatApprovalSubtitle(ctx) {
17
- const percent = ctx.getContextUsage()?.percent;
18
- if (percent == null)
19
- return undefined;
20
- return `Context now ${percent.toFixed(0)}% full`;
21
- }
22
- function callText(value) {
23
- return typeof value === "string" ? value.trim() : "";
24
- }
25
- /** Title-only headline when collapsed; plan body and revision target on expand. */
26
- export function formatExitPlanCall(args, theme, expanded) {
27
- const title = callText(args?.title);
28
- const headline = [
29
- theme.fg("toolTitle", theme.bold("exit_plan")),
30
- title ? theme.fg("text", title) : theme.fg("dim", "…"),
31
- ].join(" ");
32
- if (!expanded)
33
- return headline;
34
- const lines = [headline];
35
- const revises = callText(args?.revises);
36
- if (revises)
37
- lines.push(theme.fg("dim", `revises ${revises}`));
38
- const plan = callText(args?.plan);
39
- if (plan) {
40
- lines.push("");
41
- for (const line of plan.split("\n"))
42
- lines.push(theme.fg("dim", line));
43
- }
44
- return lines.join("\n");
45
- }
46
- // ---- tool result rendering --------------------------------------------------
47
- const DECISION_SUMMARY = {
48
- execute: "Executing with full context",
49
- compactAndExecute: "Compacting context, then executing",
50
- keepPlanning: "Still planning",
51
- cancelled: "No approval was pending",
52
- };
53
- function resultText(result) {
54
- return result.content
55
- .filter((block) => block.type === "text")
56
- .map((block) => block.text)
57
- .join("\n");
58
- }
59
- /**
60
- * What the user needs to know: where the plan landed and what happens next.
61
- * Falls back to the model-facing text when details are missing — a thrown
62
- * approval (interrupt) has no details to render from.
63
- */
64
- export function formatExitPlanResult(result, theme) {
65
- const details = result.details;
66
- if (!details)
67
- return theme.fg("dim", resultText(result));
68
- const lines = [];
69
- if (details.planPath)
70
- lines.push(theme.fg("success", `Saved to ${shortenPlanPath(details.planPath)}`));
71
- lines.push(theme.fg("dim", DECISION_SUMMARY[details.decision]));
72
- return lines.join("\n");
73
- }
74
- //# sourceMappingURL=view.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"view.js","sourceRoot":"","sources":["../../../src/extensions/plan/view.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * plan/view.ts — approval-dialog context and exit_plan tool rendering.\n *\n * `exit_plan` carries the whole plan as a parameter, and the default renderer\n * echoes parameters verbatim: the row would repeat, escaped onto one line, the\n * markdown the model just printed as response text. Collapsed rows therefore\n * carry the title only, and the plan body is available on expand.\n *\n * The result text is written for the model (it restates tool-state precedence\n * rules), so the result row renders from `details` instead.\n */\nimport type { AgentToolResult, ExtensionContext } from \"../../core/extensions/types.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport type { ExitPlanDecision, ExitPlanDetails } from \"./schema.ts\";\n\n/** Collapse the home prefix so a plan path stays on one line. */\nexport function shortenPlanPath(path: string): string {\n\tconst rawHome = process.env.USERPROFILE || process.env.HOME || \"\";\n\tconst home = rawHome.replace(/[\\\\/]+$/, \"\");\n\tif (!home) return path;\n\tconst normalize = (value: string) => value.replace(/\\\\/g, \"/\").toLowerCase();\n\tif (!normalize(path).startsWith(`${normalize(home)}/`)) return path;\n\treturn `~${path.slice(home.length).replace(/\\\\/g, \"/\")}`;\n}\n\n/**\n * Context pressure is the fact the compact-or-not decision turns on, so the\n * dialog states it instead of making the user leave and check the footer.\n */\nexport function formatApprovalSubtitle(ctx: ExtensionContext): string | undefined {\n\tconst percent = ctx.getContextUsage()?.percent;\n\tif (percent == null) return undefined;\n\treturn `Context now ${percent.toFixed(0)}% full`;\n}\n\n// ---- tool call rendering ----------------------------------------------------\n// Args arrive off the wire and stream in, so every field is read defensively.\n\ntype ExitPlanCallArgs = { title?: unknown; plan?: unknown; revises?: unknown };\n\nfunction callText(value: unknown): string {\n\treturn typeof value === \"string\" ? value.trim() : \"\";\n}\n\n/** Title-only headline when collapsed; plan body and revision target on expand. */\nexport function formatExitPlanCall(args: ExitPlanCallArgs | undefined, theme: Theme, expanded: boolean): string {\n\tconst title = callText(args?.title);\n\tconst headline = [\n\t\ttheme.fg(\"toolTitle\", theme.bold(\"exit_plan\")),\n\t\ttitle ? theme.fg(\"text\", title) : theme.fg(\"dim\", \"…\"),\n\t].join(\" \");\n\tif (!expanded) return headline;\n\n\tconst lines = [headline];\n\tconst revises = callText(args?.revises);\n\tif (revises) lines.push(theme.fg(\"dim\", `revises ${revises}`));\n\tconst plan = callText(args?.plan);\n\tif (plan) {\n\t\tlines.push(\"\");\n\t\tfor (const line of plan.split(\"\\n\")) lines.push(theme.fg(\"dim\", line));\n\t}\n\treturn lines.join(\"\\n\");\n}\n\n// ---- tool result rendering --------------------------------------------------\n\nconst DECISION_SUMMARY: Record<ExitPlanDecision, string> = {\n\texecute: \"Executing with full context\",\n\tcompactAndExecute: \"Compacting context, then executing\",\n\tkeepPlanning: \"Still planning\",\n\tcancelled: \"No approval was pending\",\n};\n\nfunction resultText(result: AgentToolResult<ExitPlanDetails>): string {\n\treturn result.content\n\t\t.filter((block): block is { type: \"text\"; text: string } => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\"\\n\");\n}\n\n/**\n * What the user needs to know: where the plan landed and what happens next.\n * Falls back to the model-facing text when details are missing — a thrown\n * approval (interrupt) has no details to render from.\n */\nexport function formatExitPlanResult(result: AgentToolResult<ExitPlanDetails>, theme: Theme): string {\n\tconst details = result.details;\n\tif (!details) return theme.fg(\"dim\", resultText(result));\n\n\tconst lines: string[] = [];\n\tif (details.planPath) lines.push(theme.fg(\"success\", `Saved to ${shortenPlanPath(details.planPath)}`));\n\tlines.push(theme.fg(\"dim\", DECISION_SUMMARY[details.decision]));\n\treturn lines.join(\"\\n\");\n}\n"]}
@@ -1,19 +0,0 @@
1
- # Bundled extensions
2
-
3
- These are extensions shipped in the `@astralyn/pi` npm package. For the public API used to write external extensions, see the [Extension API documentation](../../extensions.md).
4
-
5
- They are registered as hidden built-in extensions and use the same public Extension API available to external extensions. Their implementation details are internal to the package and are not required for installation or configuration.
6
-
7
- | Extension | Tool or command | Documentation |
8
- |---|---|---|
9
- | `llama.cpp` | `/llama` | [llama.cpp](../../llama-cpp.md) |
10
- | `deepwiki` | `deepwiki` | [deepwiki.md](deepwiki.md) |
11
- | `plan` | `exit_plan`, `/plan` | [plan.md](plan.md) |
12
- | `question` | `question` | [question.md](question.md) |
13
- | `todo` | `todo`, `/todos` | [todo.md](todo.md) |
14
- | `rewind` | `/rewind`, `/tree` lifecycle | [rewind.md](rewind.md) |
15
- | `router` | `/router` | [router.md](router.md) |
16
- | `statusline` | Footer | [statusline.md](statusline.md) |
17
- | `subagent` | `subagent`, `/agents` | [subagent.md](subagent.md) |
18
-
19
- The catalog documents installed-package behavior and configuration; it does not require access to the package's internal source.
@@ -1,33 +0,0 @@
1
- # plan — read-only plan mode
2
-
3
- Adds `/plan` for a Claude-Code-style planning phase: the model explores read-only, interviews the user, and leaves the mode through a user-approved `exit_plan` tool call that saves the plan to disk and optionally compacts the context before execution starts.
4
-
5
- ## Behavior
6
-
7
- - `/plan` (or the `--plan` CLI flag) toggles plan mode. On entry the built-in `edit`, `write`, and `bash` tools and the bundled `subagent` tool are deactivated — `subagent` because child sessions do not inherit the restricted tool set. Read-only built-ins and other extension tools (`question`, `todo`, `deepwiki`, …) stay available. A `tool_call` guard also blocks the four tools for requests already in flight when the mode toggles.
8
- - While planning, the system prompt gains a plan-mode block: explore before asking, interview one decision at a time with the `question` tool (with a recommendation and trade-offs), skip the interview for small clear tasks, then present the full plan as response text before calling `exit_plan`.
9
- - `exit_plan(title, plan, revises?)` opens the approval menu. Each option carries a muted description, and the dialog subtitle reports current context usage — the fact the compact-or-not decision turns on:
10
- - **Start executing** *(keep full context)* — saves the plan, restores tools, and execution continues in the same run.
11
- - **Compact context, then execute** *(best for long tasks)* — saves the plan, ends the run (`terminate`), compacts once the run has settled, and only then restores tools; execution restarts via a kickoff message that embeds the plan text, so continuation never depends on the compaction summary. If compaction fails, tools are restored and execution proceeds with full context.
12
- - **Keep planning** *(refine before running)* — optionally collects feedback (multi-line editor in the TUI, input dialog in RPC) and returns it to the model; plan mode stays active. `Esc` and dialog timeouts map here, never to an executing option, so the dismiss hint reads "keep planning" rather than "cancel".
13
- - Both executing options save the plan file. If the session has no name yet, the plan title becomes the session name.
14
- - `/plan` while planning exits directly without a menu or a file — the plan text is still in context. During a pending compaction the toggle is refused until the compaction settles.
15
- - Plan files live under `<agentDir>/plans/<sessionId>/NN-<slug>.md` (`~/.pi/agent/plans/…` by default, `PI_CODING_AGENT_DIR` respected) with `title`, `created`, `cwd`, `session`, and optional `revises` frontmatter. Numbering is append-only; revisions are new files that point back via `revises`.
16
- - To revise a plan mid-execution, re-enter with `/plan`: the system prompt lists the branch's saved plans so the model reads the latest one, accounts for completed work, and produces a revision.
17
-
18
- ## Limits
19
-
20
- - The kickoff message embeds at most 24 000 characters of plan markdown; longer plans are truncated with a pointer to the full file.
21
- - In `json`/`print` modes `exit_plan` cannot ask, so it saves the plan and exits with context kept (write access is restored without a menu).
22
- - Approving with **Compact context, then execute** and then interrupting before compaction completes (quit, resume, branch switch) degrades to the keep-context exit on the next session start.
23
- - The dialog subtitle is omitted when context usage is not yet known (no assistant turn has reported usage).
24
-
25
- ## Implementation notes
26
-
27
- - **Conversation-backed state.** Every transition appends a `plan-mode` custom entry (never sent to the LLM); lifecycle handlers replay the current branch tail → head, so `/reload`, `/tree`, resume, and fork restore the mode. Fork and clone copy entries, so the recorded plan-file paths follow the new session even though it gets a fresh session id — the `<sessionId>` directory is only the initial storage location, not the index.
28
- - **State is keyed per session id** (todo pattern): resume and `/tree` can switch sessions within one process, and handlers re-point the active bucket before touching state.
29
- - **Tool restore is an inverse operation, not a snapshot.** Entry records which tools were actually removed; exit re-adds exactly those, so tools other extensions activate during planning are not revoked and tools the user had disabled are not resurrected.
30
- - **No write access is ever needed for the plan itself** — the plan text travels as an `exit_plan` parameter and the extension writes the file.
31
- - **The compact-then-execute window is closed by construction**: plan mode (and its tool restrictions) persists until compaction completes; the run is terminated by the tool result and the compaction starts on `agent_settled`, which fires once per fully settled run.
32
- - The `exit_plan` result text authoritatively announces mode changes because the per-run system prompt override is not rebuilt mid-run.
33
- - **The tool row renders from the title and `details`, not from the parameters.** `exit_plan` carries the whole plan as a parameter, and its result text is written for the model, so the default renderer would echo the plan markdown escaped onto one line and then restate tool-state rules at the user. `renderCall` shows the title (plan body on expand); `renderResult` shows where the plan landed and what happens next.
@@ -1,66 +0,0 @@
1
- # Plan Mode Extension
2
-
3
- Read-only exploration mode for safe code analysis.
4
-
5
- ## Features
6
-
7
- - **Built-in write tools disabled**: Disables edit/write while preserving other active tools
8
- - **Bash allowlist**: Only read-only bash commands are allowed
9
- - **Plan extraction**: Extracts numbered steps from `Plan:` sections
10
- - **Progress tracking**: Widget shows completion status during execution
11
- - **[DONE:n] markers**: Explicit step completion tracking
12
- - **Session persistence**: State survives session resume
13
-
14
- ## Commands
15
-
16
- - `/plan` - Toggle plan mode
17
- - `/todos` - Show current plan progress
18
- - `Ctrl+Alt+P` - Toggle plan mode (shortcut)
19
-
20
- ## Usage
21
-
22
- 1. Enable plan mode with `/plan` or `--plan` flag
23
- 2. Ask the agent to analyze code and create a plan
24
- 3. The agent should output a numbered plan under a `Plan:` header:
25
-
26
- ```
27
- Plan:
28
- 1. First step description
29
- 2. Second step description
30
- 3. Third step description
31
- ```
32
-
33
- 4. Choose "Execute the plan" when prompted
34
- 5. During execution, the agent marks steps complete with `[DONE:n]` tags
35
- 6. Progress widget shows completion status
36
-
37
- ## How It Works
38
-
39
- ### Plan Mode (Read-Only)
40
- - Built-in edit/write tools disabled
41
- - Other active tools remain available
42
- - Bash commands filtered through allowlist
43
- - Agent creates a plan without making changes
44
-
45
- ### Execution Mode
46
- - Full tool access restored
47
- - Agent executes steps in order
48
- - `[DONE:n]` markers track completion
49
- - Widget shows progress
50
-
51
- ### Command Allowlist
52
-
53
- Safe commands (allowed):
54
- - File inspection: `cat`, `head`, `tail`, `less`, `more`
55
- - Search: `grep`, `find`, `rg`, `fd`
56
- - Directory: `ls`, `pwd`, `tree`
57
- - Git read: `git status`, `git log`, `git diff`, `git branch`
58
- - Package info: `npm list`, `npm outdated`, `yarn info`
59
- - System info: `uname`, `whoami`, `date`, `uptime`
60
-
61
- Blocked commands:
62
- - File modification: `rm`, `mv`, `cp`, `mkdir`, `touch`
63
- - Git write: `git add`, `git commit`, `git push`
64
- - Package install: `npm install`, `yarn add`, `pip install`
65
- - System: `sudo`, `kill`, `reboot`
66
- - Editors: `vim`, `nano`, `code`
@@ -1,390 +0,0 @@
1
- /**
2
- * Plan Mode Extension
3
- *
4
- * Read-only exploration mode for safe code analysis.
5
- * When enabled, built-in write tools are disabled.
6
- *
7
- * Features:
8
- * - /plan command or Ctrl+Alt+P to toggle
9
- * - Bash restricted to allowlisted read-only commands
10
- * - Extracts numbered plan steps from "Plan:" sections
11
- * - [DONE:n] markers to complete steps during execution
12
- * - Progress tracking widget during execution
13
- */
14
-
15
- import type { ExtensionAPI, ExtensionContext } from "@astralyn/pi";
16
- import type { AgentMessage } from "@earendil-works/pi-agent-core";
17
- import type { AssistantMessage, TextContent } from "@earendil-works/pi-ai";
18
- import { Key } from "@earendil-works/pi-tui";
19
- import { extractTodoItems, isSafeCommand, markCompletedSteps, type TodoItem } from "./utils.ts";
20
-
21
- // Tools
22
- const PLAN_MODE_TOOLS = ["read", "bash", "grep", "find", "ls", "questionnaire"];
23
- const NORMAL_MODE_TOOLS = ["read", "bash", "edit", "write"];
24
- const PLAN_MODE_DISABLED_TOOLS = new Set<string>(["edit", "write"]);
25
- const PLAN_MANAGED_TOOLS = new Set<string>([...PLAN_MODE_TOOLS, ...NORMAL_MODE_TOOLS]);
26
-
27
- interface PlanModeState {
28
- enabled: boolean;
29
- todos?: TodoItem[];
30
- executing?: boolean;
31
- toolsBeforePlanMode?: string[];
32
- }
33
-
34
- // Type guard for assistant messages
35
- function isAssistantMessage(m: AgentMessage): m is AssistantMessage {
36
- return m.role === "assistant" && Array.isArray(m.content);
37
- }
38
-
39
- // Extract text content from an assistant message
40
- function getTextContent(message: AssistantMessage): string {
41
- return message.content
42
- .filter((block): block is TextContent => block.type === "text")
43
- .map((block) => block.text)
44
- .join("\n");
45
- }
46
-
47
- export default function planModeExtension(pi: ExtensionAPI): void {
48
- let planModeEnabled = false;
49
- let executionMode = false;
50
- let todoItems: TodoItem[] = [];
51
- let toolsBeforePlanMode: string[] | undefined;
52
-
53
- pi.registerFlag("plan", {
54
- description: "Start in plan mode (read-only exploration)",
55
- type: "boolean",
56
- default: false,
57
- });
58
-
59
- function updateStatus(ctx: ExtensionContext): void {
60
- // Footer status
61
- if (executionMode && todoItems.length > 0) {
62
- const completed = todoItems.filter((t) => t.completed).length;
63
- ctx.ui.setStatus("plan-mode", ctx.ui.theme.fg("accent", `📋 ${completed}/${todoItems.length}`));
64
- } else if (planModeEnabled) {
65
- ctx.ui.setStatus("plan-mode", ctx.ui.theme.fg("warning", "⏸ plan"));
66
- } else {
67
- ctx.ui.setStatus("plan-mode", undefined);
68
- }
69
-
70
- // Widget showing todo list
71
- if (executionMode && todoItems.length > 0) {
72
- const lines = todoItems.map((item) => {
73
- if (item.completed) {
74
- return (
75
- ctx.ui.theme.fg("success", "☑ ") + ctx.ui.theme.fg("muted", ctx.ui.theme.strikethrough(item.text))
76
- );
77
- }
78
- return `${ctx.ui.theme.fg("muted", "☐ ")}${item.text}`;
79
- });
80
- ctx.ui.setWidget("plan-todos", lines);
81
- } else {
82
- ctx.ui.setWidget("plan-todos", undefined);
83
- }
84
- }
85
-
86
- function uniqueToolNames(toolNames: string[]): string[] {
87
- return [...new Set(toolNames)];
88
- }
89
-
90
- function getPlanModeTools(activeToolNames: string[]): string[] {
91
- return uniqueToolNames([
92
- ...activeToolNames.filter((name) => !PLAN_MODE_DISABLED_TOOLS.has(name)),
93
- ...PLAN_MODE_TOOLS,
94
- ]);
95
- }
96
-
97
- function getNormalModeTools(activeToolNames: string[]): string[] {
98
- return uniqueToolNames([
99
- ...NORMAL_MODE_TOOLS,
100
- ...activeToolNames.filter((name) => !PLAN_MANAGED_TOOLS.has(name)),
101
- ]);
102
- }
103
-
104
- function enablePlanModeTools(): void {
105
- if (toolsBeforePlanMode === undefined) {
106
- toolsBeforePlanMode = pi.getActiveTools();
107
- }
108
- pi.setActiveTools(getPlanModeTools(toolsBeforePlanMode));
109
- }
110
-
111
- function restoreNormalModeTools(): void {
112
- pi.setActiveTools(toolsBeforePlanMode ?? getNormalModeTools(pi.getActiveTools()));
113
- toolsBeforePlanMode = undefined;
114
- }
115
-
116
- function persistState(): void {
117
- pi.appendEntry("plan-mode", {
118
- enabled: planModeEnabled,
119
- todos: todoItems,
120
- executing: executionMode,
121
- toolsBeforePlanMode,
122
- });
123
- }
124
-
125
- function togglePlanMode(ctx: ExtensionContext): void {
126
- planModeEnabled = !planModeEnabled;
127
- executionMode = false;
128
- todoItems = [];
129
-
130
- if (planModeEnabled) {
131
- enablePlanModeTools();
132
- ctx.ui.notify("Plan mode enabled. Built-in write tools disabled.");
133
- } else {
134
- restoreNormalModeTools();
135
- ctx.ui.notify("Plan mode disabled. Full access restored.");
136
- }
137
- updateStatus(ctx);
138
- persistState();
139
- }
140
-
141
- pi.registerCommand("plan", {
142
- description: "Toggle plan mode (read-only exploration)",
143
- handler: async (_args, ctx) => togglePlanMode(ctx),
144
- });
145
-
146
- pi.registerCommand("todos", {
147
- description: "Show current plan todo list",
148
- handler: async (_args, ctx) => {
149
- if (todoItems.length === 0) {
150
- ctx.ui.notify("No todos. Create a plan first with /plan", "info");
151
- return;
152
- }
153
- const list = todoItems.map((item, i) => `${i + 1}. ${item.completed ? "✓" : "○"} ${item.text}`).join("\n");
154
- ctx.ui.notify(`Plan Progress:\n${list}`, "info");
155
- },
156
- });
157
-
158
- pi.registerShortcut(Key.ctrlAlt("p"), {
159
- description: "Toggle plan mode",
160
- handler: async (ctx) => togglePlanMode(ctx),
161
- });
162
-
163
- // Block destructive bash commands in plan mode
164
- pi.on("tool_call", async (event) => {
165
- if (!planModeEnabled || event.toolName !== "bash") return;
166
-
167
- const command = event.input.command as string;
168
- if (!isSafeCommand(command)) {
169
- return {
170
- block: true,
171
- reason: `Plan mode: command blocked (not allowlisted). Use /plan to disable plan mode first.\nCommand: ${command}`,
172
- };
173
- }
174
- });
175
-
176
- // Filter out stale plan mode context when not in plan mode
177
- pi.on("context", async (event) => {
178
- if (planModeEnabled) return;
179
-
180
- return {
181
- messages: event.messages.filter((m) => {
182
- const msg = m as AgentMessage & { customType?: string };
183
- if (msg.customType === "plan-mode-context") return false;
184
- if (msg.role !== "user") return true;
185
-
186
- const content = msg.content;
187
- if (typeof content === "string") {
188
- return !content.includes("[PLAN MODE ACTIVE]");
189
- }
190
- if (Array.isArray(content)) {
191
- return !content.some(
192
- (c) => c.type === "text" && (c as TextContent).text?.includes("[PLAN MODE ACTIVE]"),
193
- );
194
- }
195
- return true;
196
- }),
197
- };
198
- });
199
-
200
- // Inject plan/execution context before agent starts
201
- pi.on("before_agent_start", async () => {
202
- if (planModeEnabled) {
203
- return {
204
- message: {
205
- customType: "plan-mode-context",
206
- content: `[PLAN MODE ACTIVE]
207
- You are in plan mode - a read-only exploration mode for safe code analysis.
208
-
209
- Restrictions:
210
- - Built-in edit and write tools are disabled
211
- - Other currently active tools remain available
212
- - Bash is restricted to an allowlist of read-only commands
213
-
214
- Ask clarifying questions using the questionnaire tool.
215
- Use brave-search skill via bash for web research.
216
-
217
- Create a detailed numbered plan under a "Plan:" header:
218
-
219
- Plan:
220
- 1. First step description
221
- 2. Second step description
222
- ...
223
-
224
- Do NOT attempt to make changes - just describe what you would do.`,
225
- display: false,
226
- },
227
- };
228
- }
229
-
230
- if (executionMode && todoItems.length > 0) {
231
- const remaining = todoItems.filter((t) => !t.completed);
232
- const todoList = remaining.map((t) => `${t.step}. ${t.text}`).join("\n");
233
- return {
234
- message: {
235
- customType: "plan-execution-context",
236
- content: `[EXECUTING PLAN - Full tool access enabled]
237
-
238
- Remaining steps:
239
- ${todoList}
240
-
241
- Execute each step in order.
242
- After completing a step, include a [DONE:n] tag in your response.`,
243
- display: false,
244
- },
245
- };
246
- }
247
- });
248
-
249
- // Track progress after each turn
250
- pi.on("turn_end", async (event, ctx) => {
251
- if (!executionMode || todoItems.length === 0) return;
252
- if (!isAssistantMessage(event.message)) return;
253
-
254
- const text = getTextContent(event.message);
255
- if (markCompletedSteps(text, todoItems) > 0) {
256
- updateStatus(ctx);
257
- }
258
- persistState();
259
- });
260
-
261
- // Handle plan completion and plan mode UI
262
- pi.on("agent_end", async (event, ctx) => {
263
- // Check if execution is complete
264
- if (executionMode && todoItems.length > 0) {
265
- if (todoItems.every((t) => t.completed)) {
266
- const completedList = todoItems.map((t) => `~~${t.text}~~`).join("\n");
267
- pi.sendMessage(
268
- { customType: "plan-complete", content: `**Plan Complete!** ✓\n\n${completedList}`, display: true },
269
- { triggerTurn: false },
270
- );
271
- executionMode = false;
272
- todoItems = [];
273
- updateStatus(ctx);
274
- persistState(); // Save cleared state so resume doesn't restore old execution mode
275
- }
276
- return;
277
- }
278
-
279
- if (!planModeEnabled || !ctx.hasUI) return;
280
-
281
- // Extract todos from last assistant message
282
- const lastAssistant = [...event.messages].reverse().find(isAssistantMessage);
283
- if (lastAssistant) {
284
- const extracted = extractTodoItems(getTextContent(lastAssistant));
285
- if (extracted.length > 0) {
286
- todoItems = extracted;
287
- }
288
- }
289
-
290
- if (todoItems.length === 0) return;
291
- persistState();
292
-
293
- // Show plan steps and prompt for next action
294
- const todoListText = todoItems.map((t, i) => `${i + 1}. ☐ ${t.text}`).join("\n");
295
- const planTodoListMessage = {
296
- customType: "plan-todo-list",
297
- content: `**Plan Steps (${todoItems.length}):**\n\n${todoListText}`,
298
- display: true,
299
- };
300
-
301
- const choice = await ctx.ui.select("Plan mode - what next?", [
302
- "Execute the plan (track progress)",
303
- "Stay in plan mode",
304
- "Refine the plan",
305
- ]);
306
-
307
- if (choice?.startsWith("Execute")) {
308
- const firstTodoItem = todoItems[0];
309
- if (!firstTodoItem) return;
310
-
311
- planModeEnabled = false;
312
- executionMode = true;
313
- restoreNormalModeTools();
314
- updateStatus(ctx);
315
- persistState();
316
-
317
- const remainingList = todoItems.map((t) => `${t.step}. ${t.text}`).join("\n");
318
- const execMessage = `Execute the plan.
319
-
320
- Remaining steps:
321
- ${remainingList}
322
-
323
- Start with: ${firstTodoItem.text}
324
- After completing a step, include a [DONE:n] tag in your response.`;
325
- pi.sendMessage(planTodoListMessage, { deliverAs: "followUp" });
326
- pi.sendMessage(
327
- { customType: "plan-mode-execute", content: execMessage, display: true },
328
- { triggerTurn: true, deliverAs: "followUp" },
329
- );
330
- } else if (choice === "Refine the plan") {
331
- const refinement = await ctx.ui.editor("Refine the plan:", "");
332
- if (refinement?.trim()) {
333
- pi.sendMessage(planTodoListMessage, { deliverAs: "followUp" });
334
- pi.sendUserMessage(refinement.trim(), { deliverAs: "followUp" });
335
- }
336
- }
337
- });
338
-
339
- // Restore state on session start/resume
340
- pi.on("session_start", async (_event, ctx) => {
341
- if (pi.getFlag("plan") === true) {
342
- planModeEnabled = true;
343
- }
344
-
345
- const entries = ctx.sessionManager.getEntries();
346
-
347
- // Restore persisted state
348
- const planModeEntry = entries
349
- .filter((e: { type: string; customType?: string }) => e.type === "custom" && e.customType === "plan-mode")
350
- .pop() as { data?: PlanModeState } | undefined;
351
-
352
- if (planModeEntry?.data) {
353
- planModeEnabled = planModeEntry.data.enabled ?? planModeEnabled;
354
- todoItems = planModeEntry.data.todos ?? todoItems;
355
- executionMode = planModeEntry.data.executing ?? executionMode;
356
- toolsBeforePlanMode = planModeEntry.data.toolsBeforePlanMode ?? toolsBeforePlanMode;
357
- }
358
-
359
- // On resume: re-scan messages to rebuild completion state
360
- // Only scan messages AFTER the last "plan-mode-execute" to avoid picking up [DONE:n] from previous plans
361
- const isResume = planModeEntry !== undefined;
362
- if (isResume && executionMode && todoItems.length > 0) {
363
- // Find the index of the last plan-mode-execute entry (marks when current execution started)
364
- let executeIndex = -1;
365
- for (let i = entries.length - 1; i >= 0; i--) {
366
- const entry = entries[i] as { type: string; customType?: string };
367
- if (entry.customType === "plan-mode-execute") {
368
- executeIndex = i;
369
- break;
370
- }
371
- }
372
-
373
- // Only scan messages after the execute marker
374
- const messages: AssistantMessage[] = [];
375
- for (let i = executeIndex + 1; i < entries.length; i++) {
376
- const entry = entries[i];
377
- if (entry.type === "message" && "message" in entry && isAssistantMessage(entry.message as AgentMessage)) {
378
- messages.push(entry.message as AssistantMessage);
379
- }
380
- }
381
- const allText = messages.map(getTextContent).join("\n");
382
- markCompletedSteps(allText, todoItems);
383
- }
384
-
385
- if (planModeEnabled) {
386
- enablePlanModeTools();
387
- }
388
- updateStatus(ctx);
389
- });
390
- }