apex-code 0.4.0 → 0.5.1

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 (573) hide show
  1. package/CHANGELOG.md +280 -6
  2. package/README.md +624 -0
  3. package/dist/bun/cli.d.ts +3 -1
  4. package/dist/bun/cli.d.ts.map +1 -1
  5. package/dist/bun/cli.js +3 -9
  6. package/dist/bun/cli.js.map +1 -1
  7. package/dist/bun/runtime-setup.d.ts +2 -0
  8. package/dist/bun/runtime-setup.d.ts.map +1 -0
  9. package/dist/bun/runtime-setup.js +9 -0
  10. package/dist/bun/runtime-setup.js.map +1 -0
  11. package/dist/bun/sandbox-env-setup.d.ts +2 -0
  12. package/dist/bun/sandbox-env-setup.d.ts.map +1 -0
  13. package/dist/bun/sandbox-env-setup.js +4 -0
  14. package/dist/bun/sandbox-env-setup.js.map +1 -0
  15. package/dist/cli/args.d.ts.map +1 -1
  16. package/dist/cli/args.js +16 -5
  17. package/dist/cli/args.js.map +1 -1
  18. package/dist/cli/config-selector.d.ts.map +1 -1
  19. package/dist/cli/config-selector.js +2 -1
  20. package/dist/cli/config-selector.js.map +1 -1
  21. package/dist/cli/file-processor.d.ts +1 -1
  22. package/dist/cli/file-processor.d.ts.map +1 -1
  23. package/dist/cli/file-processor.js.map +1 -1
  24. package/dist/cli/session-picker.d.ts +1 -1
  25. package/dist/cli/session-picker.d.ts.map +1 -1
  26. package/dist/cli/session-picker.js.map +1 -1
  27. package/dist/cli/setup.d.ts +2 -0
  28. package/dist/cli/setup.d.ts.map +1 -0
  29. package/dist/cli/setup.js +13 -0
  30. package/dist/cli/setup.js.map +1 -0
  31. package/dist/config.d.ts +1 -0
  32. package/dist/config.d.ts.map +1 -1
  33. package/dist/config.js +1 -0
  34. package/dist/config.js.map +1 -1
  35. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  36. package/dist/core/agent-session-runtime.js +16 -8
  37. package/dist/core/agent-session-runtime.js.map +1 -1
  38. package/dist/core/agent-session.d.ts +85 -15
  39. package/dist/core/agent-session.d.ts.map +1 -1
  40. package/dist/core/agent-session.js +685 -266
  41. package/dist/core/agent-session.js.map +1 -1
  42. package/dist/core/auth-storage.d.ts +4 -0
  43. package/dist/core/auth-storage.d.ts.map +1 -1
  44. package/dist/core/auth-storage.js +55 -0
  45. package/dist/core/auth-storage.js.map +1 -1
  46. package/dist/core/bug-report.d.ts +176 -0
  47. package/dist/core/bug-report.d.ts.map +1 -0
  48. package/dist/core/bug-report.js +291 -0
  49. package/dist/core/bug-report.js.map +1 -0
  50. package/dist/core/cache-stats.d.ts.map +1 -1
  51. package/dist/core/cache-stats.js +12 -1
  52. package/dist/core/cache-stats.js.map +1 -1
  53. package/dist/core/cache-warmer.d.ts +103 -0
  54. package/dist/core/cache-warmer.d.ts.map +1 -0
  55. package/dist/core/cache-warmer.js +356 -0
  56. package/dist/core/cache-warmer.js.map +1 -0
  57. package/dist/core/compaction/branch-summarization.d.ts +1 -1
  58. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  59. package/dist/core/compaction/branch-summarization.js +4 -3
  60. package/dist/core/compaction/branch-summarization.js.map +1 -1
  61. package/dist/core/compaction/compaction.d.ts +5 -3
  62. package/dist/core/compaction/compaction.d.ts.map +1 -1
  63. package/dist/core/compaction/compaction.js +163 -66
  64. package/dist/core/compaction/compaction.js.map +1 -1
  65. package/dist/core/context/pipeline.d.ts +8 -8
  66. package/dist/core/context/pipeline.d.ts.map +1 -1
  67. package/dist/core/context/pipeline.js +21 -12
  68. package/dist/core/context/pipeline.js.map +1 -1
  69. package/dist/core/crash-log.d.ts +27 -0
  70. package/dist/core/crash-log.d.ts.map +1 -0
  71. package/dist/core/crash-log.js +139 -0
  72. package/dist/core/crash-log.js.map +1 -0
  73. package/dist/core/export-html/template.js +6 -1
  74. package/dist/core/extensions/index.d.ts +1 -1
  75. package/dist/core/extensions/index.d.ts.map +1 -1
  76. package/dist/core/extensions/index.js.map +1 -1
  77. package/dist/core/extensions/jiti-loader.d.ts +2 -0
  78. package/dist/core/extensions/jiti-loader.d.ts.map +1 -0
  79. package/dist/core/extensions/jiti-loader.js +4 -0
  80. package/dist/core/extensions/jiti-loader.js.map +1 -0
  81. package/dist/core/extensions/jiti-static-loader.d.ts +2 -0
  82. package/dist/core/extensions/jiti-static-loader.d.ts.map +1 -0
  83. package/dist/core/extensions/jiti-static-loader.js +4 -0
  84. package/dist/core/extensions/jiti-static-loader.js.map +1 -0
  85. package/dist/core/extensions/loader.d.ts.map +1 -1
  86. package/dist/core/extensions/loader.js +40 -59
  87. package/dist/core/extensions/loader.js.map +1 -1
  88. package/dist/core/extensions/runner.d.ts +24 -8
  89. package/dist/core/extensions/runner.d.ts.map +1 -1
  90. package/dist/core/extensions/runner.js +195 -72
  91. package/dist/core/extensions/runner.js.map +1 -1
  92. package/dist/core/extensions/types.d.ts +142 -55
  93. package/dist/core/extensions/types.d.ts.map +1 -1
  94. package/dist/core/extensions/types.js.map +1 -1
  95. package/dist/core/extensions/virtual-modules.d.ts +3 -0
  96. package/dist/core/extensions/virtual-modules.d.ts.map +1 -0
  97. package/dist/core/extensions/virtual-modules.js +41 -0
  98. package/dist/core/extensions/virtual-modules.js.map +1 -0
  99. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  100. package/dist/core/extensions/wrapper.js +1 -20
  101. package/dist/core/extensions/wrapper.js.map +1 -1
  102. package/dist/core/http-dispatcher.d.ts.map +1 -1
  103. package/dist/core/http-dispatcher.js +2 -0
  104. package/dist/core/http-dispatcher.js.map +1 -1
  105. package/dist/core/index.d.ts +2 -1
  106. package/dist/core/index.d.ts.map +1 -1
  107. package/dist/core/index.js.map +1 -1
  108. package/dist/core/keybindings.d.ts +6 -1
  109. package/dist/core/keybindings.d.ts.map +1 -1
  110. package/dist/core/keybindings.js +5 -1
  111. package/dist/core/keybindings.js.map +1 -1
  112. package/dist/core/messages.d.ts +1 -1
  113. package/dist/core/messages.d.ts.map +1 -1
  114. package/dist/core/messages.js +1 -0
  115. package/dist/core/messages.js.map +1 -1
  116. package/dist/core/model-config.d.ts +168 -20
  117. package/dist/core/model-config.d.ts.map +1 -1
  118. package/dist/core/model-config.js +44 -18
  119. package/dist/core/model-config.js.map +1 -1
  120. package/dist/core/model-registry.d.ts +5 -1
  121. package/dist/core/model-registry.d.ts.map +1 -1
  122. package/dist/core/model-registry.js +8 -0
  123. package/dist/core/model-registry.js.map +1 -1
  124. package/dist/core/model-resolver.d.ts.map +1 -1
  125. package/dist/core/model-resolver.js +4 -3
  126. package/dist/core/model-resolver.js.map +1 -1
  127. package/dist/core/model-runtime.d.ts +1 -0
  128. package/dist/core/model-runtime.d.ts.map +1 -1
  129. package/dist/core/model-runtime.js +12 -6
  130. package/dist/core/model-runtime.js.map +1 -1
  131. package/dist/core/permissions/store.d.ts.map +1 -1
  132. package/dist/core/permissions/store.js +11 -8
  133. package/dist/core/permissions/store.js.map +1 -1
  134. package/dist/core/prompt-templates.d.ts +6 -1
  135. package/dist/core/prompt-templates.d.ts.map +1 -1
  136. package/dist/core/prompt-templates.js +61 -35
  137. package/dist/core/prompt-templates.js.map +1 -1
  138. package/dist/core/provider-composer.d.ts +4 -2
  139. package/dist/core/provider-composer.d.ts.map +1 -1
  140. package/dist/core/provider-composer.js +29 -2
  141. package/dist/core/provider-composer.js.map +1 -1
  142. package/dist/core/radius.d.ts +3 -0
  143. package/dist/core/radius.d.ts.map +1 -1
  144. package/dist/core/radius.js +6 -0
  145. package/dist/core/radius.js.map +1 -1
  146. package/dist/core/resource-loader.d.ts.map +1 -1
  147. package/dist/core/resource-loader.js +6 -2
  148. package/dist/core/resource-loader.js.map +1 -1
  149. package/dist/core/sdk.d.ts.map +1 -1
  150. package/dist/core/sdk.js +63 -42
  151. package/dist/core/sdk.js.map +1 -1
  152. package/dist/core/session-export.d.ts +6 -2
  153. package/dist/core/session-export.d.ts.map +1 -1
  154. package/dist/core/session-export.js +14 -13
  155. package/dist/core/session-export.js.map +1 -1
  156. package/dist/core/session-manager.d.ts +66 -15
  157. package/dist/core/session-manager.d.ts.map +1 -1
  158. package/dist/core/session-manager.js +295 -115
  159. package/dist/core/session-manager.js.map +1 -1
  160. package/dist/core/settings-manager.d.ts +27 -4
  161. package/dist/core/settings-manager.d.ts.map +1 -1
  162. package/dist/core/settings-manager.js +56 -8
  163. package/dist/core/settings-manager.js.map +1 -1
  164. package/dist/core/skills.d.ts +1 -1
  165. package/dist/core/skills.d.ts.map +1 -1
  166. package/dist/core/skills.js +5 -4
  167. package/dist/core/skills.js.map +1 -1
  168. package/dist/core/slash-commands.d.ts.map +1 -1
  169. package/dist/core/slash-commands.js +1 -0
  170. package/dist/core/slash-commands.js.map +1 -1
  171. package/dist/core/system-prompt.d.ts +49 -6
  172. package/dist/core/system-prompt.d.ts.map +1 -1
  173. package/dist/core/system-prompt.js +140 -79
  174. package/dist/core/system-prompt.js.map +1 -1
  175. package/dist/core/tools/bash.d.ts +9 -15
  176. package/dist/core/tools/bash.d.ts.map +1 -1
  177. package/dist/core/tools/bash.js +20 -137
  178. package/dist/core/tools/bash.js.map +1 -1
  179. package/dist/core/tools/delegate.d.ts.map +1 -1
  180. package/dist/core/tools/delegate.js +2 -2
  181. package/dist/core/tools/delegate.js.map +1 -1
  182. package/dist/core/tools/edit.d.ts +1 -12
  183. package/dist/core/tools/edit.d.ts.map +1 -1
  184. package/dist/core/tools/edit.js +6 -204
  185. package/dist/core/tools/edit.js.map +1 -1
  186. package/dist/core/tools/find.d.ts.map +1 -1
  187. package/dist/core/tools/find.js +4 -55
  188. package/dist/core/tools/find.js.map +1 -1
  189. package/dist/core/tools/grep.d.ts.map +1 -1
  190. package/dist/core/tools/grep.js +4 -60
  191. package/dist/core/tools/grep.js.map +1 -1
  192. package/dist/core/tools/ls.d.ts.map +1 -1
  193. package/dist/core/tools/ls.js +4 -49
  194. package/dist/core/tools/ls.js.map +1 -1
  195. package/dist/core/tools/read.d.ts +4 -1
  196. package/dist/core/tools/read.d.ts.map +1 -1
  197. package/dist/core/tools/read.js +10 -122
  198. package/dist/core/tools/read.js.map +1 -1
  199. package/dist/core/tools/renderers/bash.d.ts +18 -0
  200. package/dist/core/tools/renderers/bash.d.ts.map +1 -0
  201. package/dist/core/tools/renderers/bash.js +126 -0
  202. package/dist/core/tools/renderers/bash.js.map +1 -0
  203. package/dist/core/tools/renderers/edit.d.ts +23 -0
  204. package/dist/core/tools/renderers/edit.d.ts.map +1 -0
  205. package/dist/core/tools/renderers/edit.js +207 -0
  206. package/dist/core/tools/renderers/edit.js.map +1 -0
  207. package/dist/core/tools/renderers/find.d.ts +10 -0
  208. package/dist/core/tools/renderers/find.d.ts.map +1 -0
  209. package/dist/core/tools/renderers/find.js +64 -0
  210. package/dist/core/tools/renderers/find.js.map +1 -0
  211. package/dist/core/tools/renderers/grep.d.ts +10 -0
  212. package/dist/core/tools/renderers/grep.d.ts.map +1 -0
  213. package/dist/core/tools/renderers/grep.js +69 -0
  214. package/dist/core/tools/renderers/grep.js.map +1 -0
  215. package/dist/core/tools/renderers/index.d.ts +34 -0
  216. package/dist/core/tools/renderers/index.d.ts.map +1 -0
  217. package/dist/core/tools/renderers/index.js +47 -0
  218. package/dist/core/tools/renderers/index.js.map +1 -0
  219. package/dist/core/tools/renderers/ls.d.ts +10 -0
  220. package/dist/core/tools/renderers/ls.d.ts.map +1 -0
  221. package/dist/core/tools/renderers/ls.js +58 -0
  222. package/dist/core/tools/renderers/ls.js.map +1 -0
  223. package/dist/core/tools/renderers/read.d.ts +11 -0
  224. package/dist/core/tools/renderers/read.d.ts.map +1 -0
  225. package/dist/core/tools/renderers/read.js +132 -0
  226. package/dist/core/tools/renderers/read.js.map +1 -0
  227. package/dist/core/tools/renderers/write.d.ts +10 -0
  228. package/dist/core/tools/renderers/write.d.ts.map +1 -0
  229. package/dist/core/tools/renderers/write.js +152 -0
  230. package/dist/core/tools/renderers/write.js.map +1 -0
  231. package/dist/core/tools/write.d.ts.map +1 -1
  232. package/dist/core/tools/write.js +6 -147
  233. package/dist/core/tools/write.js.map +1 -1
  234. package/dist/core/usage-totals.d.ts +1 -1
  235. package/dist/core/usage-totals.d.ts.map +1 -1
  236. package/dist/core/usage-totals.js +5 -1
  237. package/dist/core/usage-totals.js.map +1 -1
  238. package/dist/extensions/llama/client.d.ts +2 -0
  239. package/dist/extensions/llama/client.d.ts.map +1 -1
  240. package/dist/extensions/llama/client.js +7 -3
  241. package/dist/extensions/llama/client.js.map +1 -1
  242. package/dist/extensions/llama/provider.d.ts.map +1 -1
  243. package/dist/extensions/llama/provider.js +20 -5
  244. package/dist/extensions/llama/provider.js.map +1 -1
  245. package/dist/extensions/llama/ui.d.ts.map +1 -1
  246. package/dist/extensions/llama/ui.js +3 -2
  247. package/dist/extensions/llama/ui.js.map +1 -1
  248. package/dist/index.d.ts +5 -4
  249. package/dist/index.d.ts.map +1 -1
  250. package/dist/index.js +1 -1
  251. package/dist/index.js.map +1 -1
  252. package/dist/main.d.ts.map +1 -1
  253. package/dist/main.js +21 -13
  254. package/dist/main.js.map +1 -1
  255. package/dist/modes/interactive/bug-report.d.ts +16 -0
  256. package/dist/modes/interactive/bug-report.d.ts.map +1 -0
  257. package/dist/modes/interactive/bug-report.js +167 -0
  258. package/dist/modes/interactive/bug-report.js.map +1 -0
  259. package/dist/modes/interactive/chat-viewport.d.ts +20 -0
  260. package/dist/modes/interactive/chat-viewport.d.ts.map +1 -0
  261. package/dist/modes/interactive/chat-viewport.js +28 -0
  262. package/dist/modes/interactive/chat-viewport.js.map +1 -0
  263. package/dist/modes/interactive/components/assistant-message.d.ts +7 -0
  264. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  265. package/dist/modes/interactive/components/assistant-message.js +63 -27
  266. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  267. package/dist/modes/interactive/components/autocomplete-source.d.ts +8 -0
  268. package/dist/modes/interactive/components/autocomplete-source.d.ts.map +1 -0
  269. package/dist/modes/interactive/components/autocomplete-source.js +23 -0
  270. package/dist/modes/interactive/components/autocomplete-source.js.map +1 -0
  271. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  272. package/dist/modes/interactive/components/bash-execution.js +6 -4
  273. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  274. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  275. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  276. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  277. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  278. package/dist/modes/interactive/components/branch-summary-message.js +12 -5
  279. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  280. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  281. package/dist/modes/interactive/components/compaction-summary-message.js +12 -5
  282. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  283. package/dist/modes/interactive/components/composer-dock.d.ts +12 -0
  284. package/dist/modes/interactive/components/composer-dock.d.ts.map +1 -0
  285. package/dist/modes/interactive/components/composer-dock.js +24 -0
  286. package/dist/modes/interactive/components/composer-dock.js.map +1 -0
  287. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  288. package/dist/modes/interactive/components/config-selector.js +11 -9
  289. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  290. package/dist/modes/interactive/components/custom-editor.d.ts +12 -0
  291. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  292. package/dist/modes/interactive/components/custom-editor.js +38 -0
  293. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  294. package/dist/modes/interactive/components/error-summary.d.ts +11 -0
  295. package/dist/modes/interactive/components/error-summary.d.ts.map +1 -0
  296. package/dist/modes/interactive/components/error-summary.js +31 -0
  297. package/dist/modes/interactive/components/error-summary.js.map +1 -0
  298. package/dist/modes/interactive/components/extension-editor.d.ts +4 -1
  299. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  300. package/dist/modes/interactive/components/extension-editor.js +15 -10
  301. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  302. package/dist/modes/interactive/components/extension-input.d.ts +2 -0
  303. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  304. package/dist/modes/interactive/components/extension-input.js +15 -5
  305. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  306. package/dist/modes/interactive/components/extension-selector.d.ts +2 -0
  307. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  308. package/dist/modes/interactive/components/extension-selector.js +15 -10
  309. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  310. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  311. package/dist/modes/interactive/components/first-time-setup.js +6 -6
  312. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  313. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  314. package/dist/modes/interactive/components/footer.js +12 -4
  315. package/dist/modes/interactive/components/footer.js.map +1 -1
  316. package/dist/modes/interactive/components/index.d.ts +1 -1
  317. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  318. package/dist/modes/interactive/components/index.js.map +1 -1
  319. package/dist/modes/interactive/components/keybinding-hints.d.ts +21 -0
  320. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  321. package/dist/modes/interactive/components/keybinding-hints.js +34 -0
  322. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  323. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  324. package/dist/modes/interactive/components/login-dialog.js +27 -23
  325. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  326. package/dist/modes/interactive/components/model-selector.d.ts +1 -1
  327. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  328. package/dist/modes/interactive/components/model-selector.js +16 -20
  329. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  330. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  331. package/dist/modes/interactive/components/oauth-selector.js +7 -6
  332. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  333. package/dist/modes/interactive/components/permission-preview.d.ts.map +1 -1
  334. package/dist/modes/interactive/components/permission-preview.js +3 -3
  335. package/dist/modes/interactive/components/permission-preview.js.map +1 -1
  336. package/dist/modes/interactive/components/prompt-input.d.ts +12 -0
  337. package/dist/modes/interactive/components/prompt-input.d.ts.map +1 -0
  338. package/dist/modes/interactive/components/prompt-input.js +18 -0
  339. package/dist/modes/interactive/components/prompt-input.js.map +1 -0
  340. package/dist/modes/interactive/components/scoped-models-selector.d.ts +1 -1
  341. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  342. package/dist/modes/interactive/components/scoped-models-selector.js +17 -18
  343. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  344. package/dist/modes/interactive/components/session-selector.d.ts +5 -5
  345. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  346. package/dist/modes/interactive/components/session-selector.js +100 -67
  347. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  348. package/dist/modes/interactive/components/settings-selector.d.ts +5 -1
  349. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  350. package/dist/modes/interactive/components/settings-selector.js +74 -16
  351. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  352. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -1
  353. package/dist/modes/interactive/components/settings-submenu.js +8 -6
  354. package/dist/modes/interactive/components/settings-submenu.js.map +1 -1
  355. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  356. package/dist/modes/interactive/components/skill-invocation-message.js +11 -4
  357. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  358. package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
  359. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  360. package/dist/modes/interactive/components/status-indicator.js +10 -3
  361. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  362. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  363. package/dist/modes/interactive/components/thinking-selector.js +14 -9
  364. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  365. package/dist/modes/interactive/components/tool-execution.d.ts +28 -16
  366. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  367. package/dist/modes/interactive/components/tool-execution.js +73 -80
  368. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  369. package/dist/modes/interactive/components/tool-panel.d.ts.map +1 -1
  370. package/dist/modes/interactive/components/tool-panel.js +7 -2
  371. package/dist/modes/interactive/components/tool-panel.js.map +1 -1
  372. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  373. package/dist/modes/interactive/components/tree-selector.js +18 -5
  374. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  375. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  376. package/dist/modes/interactive/components/trust-selector.js +12 -12
  377. package/dist/modes/interactive/components/trust-selector.js.map +1 -1
  378. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  379. package/dist/modes/interactive/components/user-message-selector.js +2 -2
  380. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  381. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  382. package/dist/modes/interactive/components/user-message.js +6 -3
  383. package/dist/modes/interactive/components/user-message.js.map +1 -1
  384. package/dist/modes/interactive/interactive-mode.d.ts +50 -31
  385. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  386. package/dist/modes/interactive/interactive-mode.js +494 -229
  387. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  388. package/dist/modes/interactive/theme/dark.json +2 -1
  389. package/dist/modes/interactive/theme/light.json +2 -1
  390. package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
  391. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  392. package/dist/modes/interactive/theme/theme-controller.js +5 -0
  393. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  394. package/dist/modes/interactive/theme/theme-json.d.ts +84 -0
  395. package/dist/modes/interactive/theme/theme-json.d.ts.map +1 -0
  396. package/dist/modes/interactive/theme/theme-json.js +130 -0
  397. package/dist/modes/interactive/theme/theme-json.js.map +1 -0
  398. package/dist/modes/interactive/theme/theme-schema.json +6 -2
  399. package/dist/modes/interactive/theme/theme.d.ts +14 -5
  400. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  401. package/dist/modes/interactive/theme/theme.js +18 -119
  402. package/dist/modes/interactive/theme/theme.js.map +1 -1
  403. package/dist/modes/interactive/tui-renderer.d.ts +21 -0
  404. package/dist/modes/interactive/tui-renderer.d.ts.map +1 -0
  405. package/dist/modes/interactive/tui-renderer.js +66 -0
  406. package/dist/modes/interactive/tui-renderer.js.map +1 -0
  407. package/dist/modes/print-mode.d.ts.map +1 -1
  408. package/dist/modes/print-mode.js +49 -23
  409. package/dist/modes/print-mode.js.map +1 -1
  410. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  411. package/dist/modes/rpc/rpc-mode.js +2 -2
  412. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  413. package/dist/testing/replay/recorded-provider.d.ts +2 -2
  414. package/dist/testing/replay/recorded-provider.d.ts.map +1 -1
  415. package/dist/testing/replay/recorded-provider.js +2 -5
  416. package/dist/testing/replay/recorded-provider.js.map +1 -1
  417. package/dist/testing/replay/runner.d.ts.map +1 -1
  418. package/dist/testing/replay/runner.js +0 -1
  419. package/dist/testing/replay/runner.js.map +1 -1
  420. package/dist/utils/clipboard-command.d.ts +7 -0
  421. package/dist/utils/clipboard-command.d.ts.map +1 -0
  422. package/dist/utils/clipboard-command.js +45 -0
  423. package/dist/utils/clipboard-command.js.map +1 -0
  424. package/dist/utils/clipboard-image.d.ts.map +1 -1
  425. package/dist/utils/clipboard-image.js +53 -81
  426. package/dist/utils/clipboard-image.js.map +1 -1
  427. package/dist/utils/clipboard.d.ts.map +1 -1
  428. package/dist/utils/clipboard.js +111 -121
  429. package/dist/utils/clipboard.js.map +1 -1
  430. package/dist/utils/exif-orientation.d.ts.map +1 -1
  431. package/dist/utils/exif-orientation.js +2 -3
  432. package/dist/utils/exif-orientation.js.map +1 -1
  433. package/dist/utils/mime.d.ts.map +1 -1
  434. package/dist/utils/mime.js +1 -1
  435. package/dist/utils/mime.js.map +1 -1
  436. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  437. package/dist/utils/syntax-highlight.js +21 -21
  438. package/dist/utils/syntax-highlight.js.map +1 -1
  439. package/dist/utils/tool-result-images.d.ts +3 -1
  440. package/dist/utils/tool-result-images.d.ts.map +1 -1
  441. package/dist/utils/tool-result-images.js +4 -1
  442. package/dist/utils/tool-result-images.js.map +1 -1
  443. package/dist/utils/tools-manager.d.ts.map +1 -1
  444. package/dist/utils/tools-manager.js +11 -2
  445. package/dist/utils/tools-manager.js.map +1 -1
  446. package/dist/utils/wsl.d.ts +3 -0
  447. package/dist/utils/wsl.d.ts.map +1 -0
  448. package/dist/utils/wsl.js +15 -0
  449. package/dist/utils/wsl.js.map +1 -0
  450. package/dist/utils/zip.d.ts +7 -0
  451. package/dist/utils/zip.d.ts.map +1 -0
  452. package/dist/utils/zip.js +60 -0
  453. package/dist/utils/zip.js.map +1 -0
  454. package/docs/cli-integration.md +107 -0
  455. package/docs/cli.md +269 -0
  456. package/docs/compaction.md +73 -24
  457. package/docs/configuration.md +45 -0
  458. package/docs/containerization.md +22 -21
  459. package/docs/custom-provider.md +21 -12
  460. package/docs/development.md +19 -0
  461. package/docs/docs.json +139 -95
  462. package/docs/extensions.md +243 -371
  463. package/docs/how-pi-works.md +49 -0
  464. package/docs/images/interactive-mode.png +0 -0
  465. package/docs/index.md +3 -3
  466. package/docs/json.md +43 -3
  467. package/docs/keybindings.md +57 -54
  468. package/docs/llama-cpp.md +2 -2
  469. package/docs/message-types.md +261 -0
  470. package/docs/models.md +103 -49
  471. package/docs/packages.md +49 -47
  472. package/docs/prompt-templates.md +29 -56
  473. package/docs/providers.md +91 -139
  474. package/docs/quickstart.md +42 -14
  475. package/docs/rpc-commands.md +854 -0
  476. package/docs/rpc-extension-ui.md +200 -0
  477. package/docs/rpc.md +121 -223
  478. package/docs/sdk.md +99 -124
  479. package/docs/security.md +7 -7
  480. package/docs/session-format.md +85 -82
  481. package/docs/sessions.md +41 -57
  482. package/docs/settings.md +95 -88
  483. package/docs/shell-aliases.md +68 -3
  484. package/docs/skills.md +62 -49
  485. package/docs/slash-commands.md +60 -0
  486. package/docs/terminal-setup.md +86 -55
  487. package/docs/termux.md +65 -75
  488. package/docs/themes.md +64 -86
  489. package/docs/tmux.md +29 -7
  490. package/docs/tui.md +67 -122
  491. package/docs/usage.md +31 -42
  492. package/docs/windows.md +41 -13
  493. package/examples/README.md +16 -2
  494. package/examples/extensions/README.md +0 -1
  495. package/examples/extensions/custom-provider-anthropic/index.ts +18 -12
  496. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  497. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  498. package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -2
  499. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  500. package/examples/extensions/dynamic-resources/dynamic.json +2 -0
  501. package/examples/extensions/gondolin/package-lock.json +2 -2
  502. package/examples/extensions/gondolin/package.json +1 -1
  503. package/examples/extensions/prompt-customizer.ts +19 -67
  504. package/examples/extensions/sandbox/package-lock.json +2 -2
  505. package/examples/extensions/sandbox/package.json +1 -1
  506. package/examples/extensions/with-deps/package-lock.json +2 -2
  507. package/examples/extensions/with-deps/package.json +1 -1
  508. package/examples/plugins/pi-example-plugin/README.md +38 -0
  509. package/examples/plugins/pi-example-plugin/package.json +10 -0
  510. package/examples/plugins/pi-example-plugin/src/contract.ts +13 -0
  511. package/examples/plugins/pi-example-plugin/src/session.ts +24 -0
  512. package/examples/plugins/pi-example-plugin/src/tui.ts +31 -0
  513. package/examples/rpc-client.ts +35 -0
  514. package/examples/rpc-extension-ui.ts +25 -5
  515. package/examples/sdk/README.md +1 -1
  516. package/npm-shrinkwrap.json +861 -513
  517. package/package.json +27 -18
  518. package/dist/bun/register-bedrock.d.ts +0 -2
  519. package/dist/bun/register-bedrock.d.ts.map +0 -1
  520. package/dist/bun/register-bedrock.js +0 -4
  521. package/dist/bun/register-bedrock.js.map +0 -1
  522. package/dist/cli/experimental/auth.d.ts +0 -16
  523. package/dist/cli/experimental/auth.d.ts.map +0 -1
  524. package/dist/cli/experimental/auth.js +0 -13
  525. package/dist/cli/experimental/auth.js.map +0 -1
  526. package/dist/cli/experimental/cli.d.ts +0 -6
  527. package/dist/cli/experimental/cli.d.ts.map +0 -1
  528. package/dist/cli/experimental/cli.js +0 -5
  529. package/dist/cli/experimental/cli.js.map +0 -1
  530. package/dist/cli/experimental/command-options.d.ts +0 -17
  531. package/dist/cli/experimental/command-options.d.ts.map +0 -1
  532. package/dist/cli/experimental/command-options.js +0 -35
  533. package/dist/cli/experimental/command-options.js.map +0 -1
  534. package/dist/cli/experimental/command.d.ts +0 -63
  535. package/dist/cli/experimental/command.d.ts.map +0 -1
  536. package/dist/cli/experimental/command.js +0 -130
  537. package/dist/cli/experimental/command.js.map +0 -1
  538. package/dist/cli/experimental/commands/client.d.ts +0 -13
  539. package/dist/cli/experimental/commands/client.d.ts.map +0 -1
  540. package/dist/cli/experimental/commands/client.js +0 -25
  541. package/dist/cli/experimental/commands/client.js.map +0 -1
  542. package/dist/cli/experimental/commands/pi.d.ts +0 -15
  543. package/dist/cli/experimental/commands/pi.d.ts.map +0 -1
  544. package/dist/cli/experimental/commands/pi.js +0 -28
  545. package/dist/cli/experimental/commands/pi.js.map +0 -1
  546. package/dist/cli/experimental/commands/server.d.ts +0 -13
  547. package/dist/cli/experimental/commands/server.d.ts.map +0 -1
  548. package/dist/cli/experimental/commands/server.js +0 -25
  549. package/dist/cli/experimental/commands/server.js.map +0 -1
  550. package/dist/cli/experimental/transport-address.d.ts +0 -10
  551. package/dist/cli/experimental/transport-address.d.ts.map +0 -1
  552. package/dist/cli/experimental/transport-address.js +0 -38
  553. package/dist/cli/experimental/transport-address.js.map +0 -1
  554. package/dist/client/index.d.ts +0 -3
  555. package/dist/client/index.d.ts.map +0 -1
  556. package/dist/client/index.js +0 -3
  557. package/dist/client/index.js.map +0 -1
  558. package/dist/client/remote-session.d.ts +0 -53
  559. package/dist/client/remote-session.d.ts.map +0 -1
  560. package/dist/client/remote-session.js +0 -340
  561. package/dist/client/remote-session.js.map +0 -1
  562. package/dist/client/transcript.d.ts +0 -12
  563. package/dist/client/transcript.d.ts.map +0 -1
  564. package/dist/client/transcript.js +0 -98
  565. package/dist/client/transcript.js.map +0 -1
  566. package/dist/modes/interactive/components/message-spine.d.ts +0 -14
  567. package/dist/modes/interactive/components/message-spine.d.ts.map +0 -1
  568. package/dist/modes/interactive/components/message-spine.js +0 -19
  569. package/dist/modes/interactive/components/message-spine.js.map +0 -1
  570. package/dist/utils/clipboard-native.d.ts +0 -11
  571. package/dist/utils/clipboard-native.d.ts.map +0 -1
  572. package/dist/utils/clipboard-native.js +0 -20
  573. package/dist/utils/clipboard-native.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/core/permissions/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Persistence for the permission rule model (ADR 0004). Four of the eight sources\n * are file-backed (policy, local, project, user); the other four are runtime-only\n * (flag, cliArg — parsed once from argv, not stored here; command, session — held\n * in memory for the life of the process). `session`-source updates are never\n * written to disk: \"allow for this session\" dies with the session by design.\n *\n * Reuses the locked-JSON-file backend already proven for Phase 1's operational\n * state (FileAuthStorageBackend) rather than widening `SettingsScope`, which only\n * covers two of these four file-backed sources and represents a different, larger\n * settings domain.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../../config.ts\";\nimport { resolvePath } from \"../../utils/paths.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend } from \"../auth-storage.ts\";\nimport {\n\tPROJECT_LOCAL_PERMISSIONS_FILE,\n\tPROJECT_PERMISSIONS_FILE,\n\tprojectResourcePathByName,\n} from \"../project-resources.ts\";\nimport type { PermissionBehavior } from \"../tools/contract.ts\";\nimport type { PermissionRule, PermissionSource } from \"./rules.ts\";\n\nexport const PERMISSION_MODES = [\"default\", \"plan\", \"acceptEdits\", \"bypassPermissions\", \"dontAsk\"] as const;\nexport type PermissionMode = (typeof PERMISSION_MODES)[number];\n\n/** Sources a PermissionUpdate may target. Excludes `policy` (administrator-managed,\n * never user-writable) and `flag`/`cliArg` (parsed once from argv at startup, not\n * something this store updates after the fact). */\nexport const WRITABLE_PERMISSION_SOURCES = [\"local\", \"project\", \"user\", \"command\", \"session\"] as const;\nexport type WritablePermissionSource = (typeof WRITABLE_PERMISSION_SOURCES)[number];\n\nconst FILE_BACKED_SOURCES = [\"local\", \"project\", \"user\"] as const;\ntype FileBackedSource = (typeof FILE_BACKED_SOURCES)[number];\n\nconst RUNTIME_SOURCES = [\"command\", \"session\"] as const;\ntype RuntimeSource = (typeof RUNTIME_SOURCES)[number];\n\n/** A rule as persisted: `source` is never stored — it is implied by which file it came from. */\nexport type StoredPermissionRule = Omit<PermissionRule, \"source\">;\n\nexport interface RuleMatcher {\n\ttoolName: string;\n\truleContent?: string;\n}\n\nexport type PermissionUpdate =\n\t| { type: \"addRules\"; destination: WritablePermissionSource; rules: readonly StoredPermissionRule[] }\n\t| { type: \"replaceRules\"; destination: WritablePermissionSource; rules: readonly StoredPermissionRule[] }\n\t| { type: \"removeRules\"; destination: WritablePermissionSource; matching: readonly RuleMatcher[] }\n\t| { type: \"setMode\"; destination: WritablePermissionSource; mode: PermissionMode };\n\nexport interface PermissionStoreError {\n\tsource: PermissionSource;\n\terror: Error;\n}\n\nexport interface PermissionStoreSnapshot {\n\trules: readonly PermissionRule[];\n\tmodesBySource: ReadonlyMap<WritablePermissionSource, PermissionMode>;\n\terrors: readonly PermissionStoreError[];\n}\n\nexport interface PermissionRuleStore {\n\tsnapshot(): Promise<PermissionStoreSnapshot>;\n\tapply(update: PermissionUpdate): Promise<void>;\n}\n\nconst STORE_VERSION = 1;\n\ninterface StoredPermissionScope {\n\tversion: number;\n\trules: StoredPermissionRule[];\n\tmode?: PermissionMode;\n}\n\nfunction emptyScope(): StoredPermissionScope {\n\treturn { version: STORE_VERSION, rules: [] };\n}\n\n/**\n * Whether a permission file's contents grant anything. The trust classifier asks this\n * so a `{}` file does not raise a prompt that carries no decision. Unparseable content\n * counts as granting: a malformed or hostile file must prompt rather than pass.\n */\nexport function permissionContentConfersAuthority(content: string | undefined): boolean {\n\tlet scope: StoredPermissionScope;\n\ttry {\n\t\tscope = parseScope(content);\n\t} catch {\n\t\treturn true;\n\t}\n\treturn scope.rules.length > 0 || scope.mode !== undefined;\n}\n\nfunction isPermissionBehavior(value: unknown): value is PermissionBehavior {\n\treturn value === \"allow\" || value === \"deny\" || value === \"ask\";\n}\n\nfunction parseScope(content: string | undefined): StoredPermissionScope {\n\tif (!content) return emptyScope();\n\tconst parsed = JSON.parse(content);\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(\"Invalid permission rules file: unexpected shape\");\n\t}\n\t// FileAuthStorageBackend (shared with auth.json) seeds a freshly created file\n\t// with the literal text \"{}\". Treat a content-free object as \"nothing written\n\t// yet\" rather than malformed; a real malformed file has some recognizable but\n\t// wrong-shaped field, caught below.\n\tif (!(\"version\" in parsed) && !(\"rules\" in parsed) && !(\"mode\" in parsed)) {\n\t\treturn emptyScope();\n\t}\n\tif (\n\t\tparsed.version !== STORE_VERSION ||\n\t\t(parsed.mode !== undefined && !(PERMISSION_MODES as readonly string[]).includes(parsed.mode)) ||\n\t\t!Array.isArray(parsed.rules) ||\n\t\t!parsed.rules.every(\n\t\t\t(rule: unknown) =>\n\t\t\t\ttypeof rule === \"object\" &&\n\t\t\t\trule !== null &&\n\t\t\t\ttypeof (rule as StoredPermissionRule).toolName === \"string\" &&\n\t\t\t\tisPermissionBehavior((rule as StoredPermissionRule).behavior) &&\n\t\t\t\t((rule as StoredPermissionRule).ruleContent === undefined ||\n\t\t\t\t\ttypeof (rule as StoredPermissionRule).ruleContent === \"string\"),\n\t\t)\n\t) {\n\t\tthrow new Error(\"Invalid permission rules file: unexpected shape\");\n\t}\n\treturn parsed as StoredPermissionScope;\n}\n\nfunction applyToScope(scope: StoredPermissionScope, update: PermissionUpdate): StoredPermissionScope {\n\tswitch (update.type) {\n\t\tcase \"addRules\":\n\t\t\treturn { ...scope, rules: [...scope.rules, ...update.rules] };\n\t\tcase \"replaceRules\":\n\t\t\treturn { ...scope, rules: [...update.rules] };\n\t\tcase \"removeRules\":\n\t\t\treturn {\n\t\t\t\t...scope,\n\t\t\t\trules: scope.rules.filter(\n\t\t\t\t\t(rule) =>\n\t\t\t\t\t\t!update.matching.some((m) => m.toolName === rule.toolName && m.ruleContent === rule.ruleContent),\n\t\t\t\t),\n\t\t\t};\n\t\tcase \"setMode\":\n\t\t\treturn { ...scope, mode: update.mode };\n\t}\n}\n\nfunction defaultPolicyPath(): string {\n\tif (process.env.APEX_CODE_POLICY_PATH) return process.env.APEX_CODE_POLICY_PATH;\n\treturn process.platform === \"win32\"\n\t\t? join(process.env.ProgramData ?? \"C:\\\\ProgramData\", \"apex-code\", \"policy.json\")\n\t\t: \"/etc/apex-code/policy.json\";\n}\n\nexport interface CreateFilePermissionRuleStoreOptions {\n\tcwd: string;\n\tagentDir?: string;\n\t/** Overrides the managed policy file path. Defaults to $APEX_CODE_POLICY_PATH, else an OS-specific system location. */\n\tpolicyPath?: string;\n\t/** Test seam: inject backends for the three writable file-backed sources instead of touching real files. */\n\tbackends?: Partial<Record<FileBackedSource, AuthStorageBackend>>;\n\t/** Immutable argv layers (`flag` / `cliArg`), validated by the CLI before construction. */\n\tinitialRules?: readonly PermissionRule[];\n\t/**\n\t * Resolved before construction. False excludes both project-controlled scopes.\n\t *\n\t * Required, per ADR 0034. There is no correct default: a caller that has not resolved\n\t * trust cannot be given either answer about an untrusted checkout.\n\t */\n\tprojectTrusted: boolean;\n}\n\n/** File-backed store for policy/local/project/user, in-memory for command/session. */\nexport class FilePermissionRuleStore implements PermissionRuleStore {\n\tprivate readonly backends: Record<FileBackedSource, AuthStorageBackend>;\n\tprivate readonly policyPath: string;\n\tprivate readonly initialRules: readonly PermissionRule[];\n\tprivate readonly runtimeRules: Record<RuntimeSource, StoredPermissionRule[]> = { command: [], session: [] };\n\tprivate readonly runtimeModes: Partial<Record<RuntimeSource, PermissionMode>> = {};\n\tprivate readonly projectTrusted: boolean;\n\tprivate readonly fileScopes: Partial<\n\t\tRecord<FileBackedSource, Promise<{ scope: StoredPermissionScope; error?: Error }>>\n\t> = {};\n\tprivate readonly fileScopeUpdates: Partial<Record<FileBackedSource, Promise<void>>> = {};\n\n\tconstructor(options: CreateFilePermissionRuleStoreOptions) {\n\t\tconst agentDir = options.agentDir ?? getAgentDir();\n\t\tconst cwd = resolvePath(options.cwd);\n\t\tthis.backends = {\n\t\t\tuser: new FileAuthStorageBackend(join(agentDir, \"permissions.json\")),\n\t\t\tproject: new FileAuthStorageBackend(projectResourcePathByName(cwd, PROJECT_PERMISSIONS_FILE)),\n\t\t\tlocal: new FileAuthStorageBackend(projectResourcePathByName(cwd, PROJECT_LOCAL_PERMISSIONS_FILE)),\n\t\t\t...options.backends,\n\t\t};\n\t\tthis.policyPath = options.policyPath ?? defaultPolicyPath();\n\t\tthis.initialRules = options.initialRules ?? [];\n\t\t// A JavaScript caller reaches this with no compiler in between, so the type is not\n\t\t// the whole guard. ADR 0034.\n\t\tif (typeof options.projectTrusted !== \"boolean\") {\n\t\t\tthrow new TypeError(\"FilePermissionRuleStore requires an explicit projectTrusted decision (ADR 0034)\");\n\t\t}\n\t\tthis.projectTrusted = options.projectTrusted;\n\t\tif (!this.projectTrusted) {\n\t\t\tthis.fileScopes.local = Promise.resolve({ scope: emptyScope() });\n\t\t\tthis.fileScopes.project = Promise.resolve({ scope: emptyScope() });\n\t\t}\n\t}\n\n\tprivate capturedFileBackedScope(source: FileBackedSource): Promise<{ scope: StoredPermissionScope; error?: Error }> {\n\t\tconst captured = this.fileScopes[source];\n\t\tif (captured) return captured;\n\t\tconst initial = this.readFileBackedScope(source);\n\t\tthis.fileScopes[source] = initial;\n\t\treturn initial;\n\t}\n\n\tprivate async readFileBackedScope(\n\t\tsource: FileBackedSource,\n\t): Promise<{ scope: StoredPermissionScope; error?: Error }> {\n\t\ttry {\n\t\t\tlet content: string | undefined;\n\t\t\tawait this.backends[source].withLockAsync(async (current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\treturn { scope: parseScope(content) };\n\t\t} catch (error) {\n\t\t\treturn { scope: emptyScope(), error: error instanceof Error ? error : new Error(String(error)) };\n\t\t}\n\t}\n\n\tprivate async readPolicy(): Promise<{ scope: StoredPermissionScope; error?: Error }> {\n\t\ttry {\n\t\t\tconst content = await readFile(this.policyPath, \"utf-8\");\n\t\t\treturn { scope: parseScope(content) };\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return { scope: emptyScope() };\n\t\t\treturn { scope: emptyScope(), error: error instanceof Error ? error : new Error(String(error)) };\n\t\t}\n\t}\n\n\tprivate capturedPolicyAndUser(): Promise<{\n\t\tpolicy: { scope: StoredPermissionScope; error?: Error };\n\t\tuser: { scope: StoredPermissionScope; error?: Error };\n\t}> {\n\t\treturn Promise.all([this.readPolicy(), this.capturedFileBackedScope(\"user\")]).then(([policy, user]) => ({\n\t\t\tpolicy,\n\t\t\tuser,\n\t\t}));\n\t}\n\n\tasync snapshot(): Promise<PermissionStoreSnapshot> {\n\t\t// File-backed authorization is captured once. Direct filesystem writes,\n\t\t// including symlink replacement, cannot widen the current or next gate\n\t\t// decision. Managed policy and runtime scopes remain live.\n\t\tconst [policyAndUser, local, project] = await Promise.all([\n\t\t\tthis.capturedPolicyAndUser(),\n\t\t\tthis.capturedFileBackedScope(\"local\"),\n\t\t\tthis.capturedFileBackedScope(\"project\"),\n\t\t]);\n\t\tconst { policy, user } = policyAndUser;\n\t\tconst errors: PermissionStoreError[] = [];\n\t\tconst withSource = (\n\t\t\tsource: PermissionSource,\n\t\t\tentry: { scope: StoredPermissionScope; error?: Error },\n\t\t): PermissionRule[] => {\n\t\t\tif (entry.error) errors.push({ source, error: entry.error });\n\t\t\treturn entry.scope.rules.map((rule) => ({ ...rule, source }));\n\t\t};\n\n\t\tconst rules: PermissionRule[] = [\n\t\t\t...withSource(\"policy\", policy),\n\t\t\t...this.initialRules,\n\t\t\t...withSource(\"local\", local),\n\t\t\t...withSource(\"project\", project),\n\t\t\t...withSource(\"user\", user),\n\t\t\t...this.runtimeRules.command.map((rule) => ({ ...rule, source: \"command\" as const })),\n\t\t\t...this.runtimeRules.session.map((rule) => ({ ...rule, source: \"session\" as const })),\n\t\t];\n\n\t\tconst modesBySource = new Map<WritablePermissionSource, PermissionMode>();\n\t\tif (local.scope.mode) modesBySource.set(\"local\", local.scope.mode);\n\t\tif (project.scope.mode) modesBySource.set(\"project\", project.scope.mode);\n\t\tif (user.scope.mode) modesBySource.set(\"user\", user.scope.mode);\n\t\tif (this.runtimeModes.command) modesBySource.set(\"command\", this.runtimeModes.command);\n\t\tif (this.runtimeModes.session) modesBySource.set(\"session\", this.runtimeModes.session);\n\n\t\treturn { rules, modesBySource, errors };\n\t}\n\n\tasync apply(update: PermissionUpdate): Promise<void> {\n\t\tif (update.destination === \"command\" || update.destination === \"session\") {\n\t\t\tthis.applyRuntime(update.destination, update);\n\t\t\treturn;\n\t\t}\n\t\tif (!this.projectTrusted && (update.destination === \"local\" || update.destination === \"project\")) {\n\t\t\tthrow new Error(`Cannot update untrusted ${update.destination} permission scope`);\n\t\t}\n\t\tconst destination = update.destination;\n\t\tconst previousUpdate = this.fileScopeUpdates[destination]?.catch(() => undefined) ?? Promise.resolve();\n\t\tconst nextUpdate = previousUpdate.then(async () => {\n\t\t\tconst current = await this.capturedFileBackedScope(destination);\n\t\t\tconst next = applyToScope(current.scope, update);\n\t\t\tawait this.backends[destination].withLockAsync(async () => ({\n\t\t\t\tresult: undefined,\n\t\t\t\tnext: JSON.stringify(next, null, 2),\n\t\t\t}));\n\t\t\tthis.fileScopes[destination] = Promise.resolve({ scope: next });\n\t\t});\n\t\tthis.fileScopeUpdates[destination] = nextUpdate;\n\t\tawait nextUpdate;\n\t}\n\n\tprivate applyRuntime(destination: RuntimeSource, update: PermissionUpdate): void {\n\t\tif (update.type === \"setMode\") {\n\t\t\tthis.runtimeModes[destination] = update.mode;\n\t\t\treturn;\n\t\t}\n\t\tconst current: StoredPermissionScope = { version: STORE_VERSION, rules: this.runtimeRules[destination] };\n\t\tthis.runtimeRules[destination] = applyToScope(current, update).rules;\n\t}\n}\n\nexport interface DerivedPermissionRuleStoreOptions {\n\t/** The parent's live store. Read fresh on every `snapshot()`, never cached, so a\n\t * rule the parent picks up mid-session (e.g. a human's \"always allow\") is visible\n\t * to the child on its very next call. */\n\tparent: PermissionRuleStore;\n}\n\n/**\n * A delegated child's permission store (roadmap Phase 5, ADR 0008). A read-through\n * view over the parent's live snapshot -- including its runtime-only sources\n * (`flag`, `cliArg`, `command`, `session`), which a subprocess-spawned child could\n * never observe -- plus a child-local runtime overlay for the child's own writes.\n *\n * `apply()` never reaches the parent. It writes only into this instance's own\n * overlay, and only for the two runtime-only writable sources (`command`, `session`);\n * a file-backed destination (`local`, `project`, `user`) is rejected outright, not\n * silently redirected -- a delegated child must never be able to persist to the\n * parent's settings files, and \"runtime-only\" is enforced by construction here, not\n * by a convention a future caller could forget. The overlay is discarded with the\n * child: there is no code path from a child's approval back into the parent's store,\n * which is what keeps a child's `persist: true` from widening its parent.\n */\nexport class DerivedPermissionRuleStore implements PermissionRuleStore {\n\tprivate readonly parent: PermissionRuleStore;\n\tprivate readonly overlayRules: Record<RuntimeSource, StoredPermissionRule[]> = { command: [], session: [] };\n\tprivate readonly overlayModes: Partial<Record<RuntimeSource, PermissionMode>> = {};\n\n\tconstructor(options: DerivedPermissionRuleStoreOptions) {\n\t\tthis.parent = options.parent;\n\t}\n\n\tasync snapshot(): Promise<PermissionStoreSnapshot> {\n\t\tconst parentSnapshot = await this.parent.snapshot();\n\t\tconst rules: PermissionRule[] = [\n\t\t\t...parentSnapshot.rules,\n\t\t\t...this.overlayRules.command.map((rule) => ({ ...rule, source: \"command\" as const })),\n\t\t\t...this.overlayRules.session.map((rule) => ({ ...rule, source: \"session\" as const })),\n\t\t];\n\t\tconst modesBySource = new Map(parentSnapshot.modesBySource);\n\t\tif (this.overlayModes.command !== undefined) modesBySource.set(\"command\", this.overlayModes.command);\n\t\tif (this.overlayModes.session !== undefined) modesBySource.set(\"session\", this.overlayModes.session);\n\t\treturn { rules, modesBySource, errors: parentSnapshot.errors };\n\t}\n\n\tasync apply(update: PermissionUpdate): Promise<void> {\n\t\tif (update.destination !== \"command\" && update.destination !== \"session\") {\n\t\t\tthrow new Error(\n\t\t\t\t`A delegated child cannot persist permission rules to \"${update.destination}\" -- only \"command\" and \"session\" are writable from a derived store (ADR 0008).`,\n\t\t\t);\n\t\t}\n\t\tif (update.type === \"setMode\") {\n\t\t\tthis.overlayModes[update.destination] = update.mode;\n\t\t\treturn;\n\t\t}\n\t\tconst current: StoredPermissionScope = {\n\t\t\tversion: STORE_VERSION,\n\t\t\trules: this.overlayRules[update.destination],\n\t\t};\n\t\tthis.overlayRules[update.destination] = applyToScope(current, update).rules;\n\t}\n}\n"]}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/core/permissions/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Persistence for the permission rule model (ADR 0004). Four of the eight sources\n * are file-backed (policy, local, project, user); the other four are runtime-only\n * (flag, cliArg — parsed once from argv, not stored here; command, session — held\n * in memory for the life of the process). `session`-source updates are never\n * written to disk: \"allow for this session\" dies with the session by design.\n *\n * Reuses the locked-JSON-file backend already proven for Phase 1's operational\n * state (FileAuthStorageBackend) rather than widening `SettingsScope`, which only\n * covers two of these four file-backed sources and represents a different, larger\n * settings domain.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../../config.ts\";\nimport { resolvePath } from \"../../utils/paths.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend } from \"../auth-storage.ts\";\nimport {\n\tPROJECT_LOCAL_PERMISSIONS_FILE,\n\tPROJECT_PERMISSIONS_FILE,\n\tprojectResourcePathByName,\n} from \"../project-resources.ts\";\nimport type { PermissionBehavior } from \"../tools/contract.ts\";\nimport type { PermissionRule, PermissionSource } from \"./rules.ts\";\n\nexport const PERMISSION_MODES = [\"default\", \"plan\", \"acceptEdits\", \"bypassPermissions\", \"dontAsk\"] as const;\nexport type PermissionMode = (typeof PERMISSION_MODES)[number];\n\n/** Sources a PermissionUpdate may target. Excludes `policy` (administrator-managed,\n * never user-writable) and `flag`/`cliArg` (parsed once from argv at startup, not\n * something this store updates after the fact). */\nexport const WRITABLE_PERMISSION_SOURCES = [\"local\", \"project\", \"user\", \"command\", \"session\"] as const;\nexport type WritablePermissionSource = (typeof WRITABLE_PERMISSION_SOURCES)[number];\n\nconst FILE_BACKED_SOURCES = [\"local\", \"project\", \"user\"] as const;\ntype FileBackedSource = (typeof FILE_BACKED_SOURCES)[number];\n\nconst RUNTIME_SOURCES = [\"command\", \"session\"] as const;\ntype RuntimeSource = (typeof RUNTIME_SOURCES)[number];\n\n/** A rule as persisted: `source` is never stored — it is implied by which file it came from. */\nexport type StoredPermissionRule = Omit<PermissionRule, \"source\">;\n\nexport interface RuleMatcher {\n\ttoolName: string;\n\truleContent?: string;\n}\n\nexport type PermissionUpdate =\n\t| { type: \"addRules\"; destination: WritablePermissionSource; rules: readonly StoredPermissionRule[] }\n\t| { type: \"replaceRules\"; destination: WritablePermissionSource; rules: readonly StoredPermissionRule[] }\n\t| { type: \"removeRules\"; destination: WritablePermissionSource; matching: readonly RuleMatcher[] }\n\t| { type: \"setMode\"; destination: WritablePermissionSource; mode: PermissionMode };\n\nexport interface PermissionStoreError {\n\tsource: PermissionSource;\n\terror: Error;\n}\n\nexport interface PermissionStoreSnapshot {\n\trules: readonly PermissionRule[];\n\tmodesBySource: ReadonlyMap<WritablePermissionSource, PermissionMode>;\n\terrors: readonly PermissionStoreError[];\n}\n\nexport interface PermissionRuleStore {\n\tsnapshot(): Promise<PermissionStoreSnapshot>;\n\tapply(update: PermissionUpdate): Promise<void>;\n}\n\nconst STORE_VERSION = 1;\n\ninterface StoredPermissionScope {\n\tversion: number;\n\trules: StoredPermissionRule[];\n\tmode?: PermissionMode;\n}\n\nfunction emptyScope(): StoredPermissionScope {\n\treturn { version: STORE_VERSION, rules: [] };\n}\n\n/**\n * Whether a permission file's contents grant anything. The trust classifier asks this\n * so a `{}` file does not raise a prompt that carries no decision. Unparseable content\n * counts as granting: a malformed or hostile file must prompt rather than pass.\n */\nexport function permissionContentConfersAuthority(content: string | undefined): boolean {\n\tlet scope: StoredPermissionScope;\n\ttry {\n\t\tscope = parseScope(content);\n\t} catch {\n\t\treturn true;\n\t}\n\treturn scope.rules.length > 0 || scope.mode !== undefined;\n}\n\nfunction isPermissionBehavior(value: unknown): value is PermissionBehavior {\n\treturn value === \"allow\" || value === \"deny\" || value === \"ask\";\n}\n\nfunction parseScope(content: string | undefined): StoredPermissionScope {\n\tif (!content) return emptyScope();\n\tconst parsed = JSON.parse(content);\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(\"Invalid permission rules file: unexpected shape\");\n\t}\n\t// FileAuthStorageBackend (shared with auth.json) seeds a freshly created file\n\t// with the literal text \"{}\". Treat a content-free object as \"nothing written\n\t// yet\" rather than malformed; a real malformed file has some recognizable but\n\t// wrong-shaped field, caught below.\n\tif (!(\"version\" in parsed) && !(\"rules\" in parsed) && !(\"mode\" in parsed)) {\n\t\treturn emptyScope();\n\t}\n\tif (\n\t\tparsed.version !== STORE_VERSION ||\n\t\t(parsed.mode !== undefined && !(PERMISSION_MODES as readonly string[]).includes(parsed.mode)) ||\n\t\t!Array.isArray(parsed.rules) ||\n\t\t!parsed.rules.every(\n\t\t\t(rule: unknown) =>\n\t\t\t\ttypeof rule === \"object\" &&\n\t\t\t\trule !== null &&\n\t\t\t\ttypeof (rule as StoredPermissionRule).toolName === \"string\" &&\n\t\t\t\tisPermissionBehavior((rule as StoredPermissionRule).behavior) &&\n\t\t\t\t((rule as StoredPermissionRule).ruleContent === undefined ||\n\t\t\t\t\ttypeof (rule as StoredPermissionRule).ruleContent === \"string\"),\n\t\t)\n\t) {\n\t\tthrow new Error(\"Invalid permission rules file: unexpected shape\");\n\t}\n\treturn parsed as StoredPermissionScope;\n}\n\nfunction applyToScope(scope: StoredPermissionScope, update: PermissionUpdate): StoredPermissionScope {\n\tswitch (update.type) {\n\t\tcase \"addRules\":\n\t\t\treturn { ...scope, rules: [...scope.rules, ...update.rules] };\n\t\tcase \"replaceRules\":\n\t\t\treturn { ...scope, rules: [...update.rules] };\n\t\tcase \"removeRules\":\n\t\t\treturn {\n\t\t\t\t...scope,\n\t\t\t\trules: scope.rules.filter(\n\t\t\t\t\t(rule) =>\n\t\t\t\t\t\t!update.matching.some((m) => m.toolName === rule.toolName && m.ruleContent === rule.ruleContent),\n\t\t\t\t),\n\t\t\t};\n\t\tcase \"setMode\":\n\t\t\treturn { ...scope, mode: update.mode };\n\t}\n}\n\nfunction defaultPolicyPath(): string {\n\tif (process.env.APEX_CODE_POLICY_PATH) return process.env.APEX_CODE_POLICY_PATH;\n\treturn process.platform === \"win32\"\n\t\t? join(process.env.ProgramData ?? \"C:\\\\ProgramData\", \"apex-code\", \"policy.json\")\n\t\t: \"/etc/apex-code/policy.json\";\n}\n\nexport interface CreateFilePermissionRuleStoreOptions {\n\tcwd: string;\n\tagentDir?: string;\n\t/** Overrides the managed policy file path. Defaults to $APEX_CODE_POLICY_PATH, else an OS-specific system location. */\n\tpolicyPath?: string;\n\t/** Test seam: inject backends for the three writable file-backed sources instead of touching real files. */\n\tbackends?: Partial<Record<FileBackedSource, AuthStorageBackend>>;\n\t/** Immutable argv layers (`flag` / `cliArg`), validated by the CLI before construction. */\n\tinitialRules?: readonly PermissionRule[];\n\t/**\n\t * Resolved before construction. False excludes both project-controlled scopes.\n\t *\n\t * Required, per ADR 0034. There is no correct default: a caller that has not resolved\n\t * trust cannot be given either answer about an untrusted checkout.\n\t */\n\tprojectTrusted: boolean;\n}\n\n/** File-backed store for policy/local/project/user, in-memory for command/session. */\nexport class FilePermissionRuleStore implements PermissionRuleStore {\n\tprivate readonly backends: Record<FileBackedSource, AuthStorageBackend>;\n\tprivate readonly policyPath: string;\n\tprivate readonly initialRules: readonly PermissionRule[];\n\tprivate readonly runtimeRules: Record<RuntimeSource, StoredPermissionRule[]> = { command: [], session: [] };\n\tprivate readonly runtimeModes: Partial<Record<RuntimeSource, PermissionMode>> = {};\n\tprivate readonly projectTrusted: boolean;\n\tprivate readonly fileScopes: Partial<\n\t\tRecord<FileBackedSource, Promise<{ scope: StoredPermissionScope; error?: Error }>>\n\t> = {};\n\tprivate readonly fileScopeUpdates: Partial<Record<FileBackedSource, Promise<void>>> = {};\n\n\tconstructor(options: CreateFilePermissionRuleStoreOptions) {\n\t\tconst agentDir = options.agentDir ?? getAgentDir();\n\t\tconst cwd = resolvePath(options.cwd);\n\t\tconst paths: Record<FileBackedSource, string> = {\n\t\t\tuser: join(agentDir, \"permissions.json\"),\n\t\t\tproject: projectResourcePathByName(cwd, PROJECT_PERMISSIONS_FILE),\n\t\t\tlocal: projectResourcePathByName(cwd, PROJECT_LOCAL_PERMISSIONS_FILE),\n\t\t};\n\t\tthis.backends = {\n\t\t\tuser: new FileAuthStorageBackend(paths.user),\n\t\t\tproject: new FileAuthStorageBackend(paths.project),\n\t\t\tlocal: new FileAuthStorageBackend(paths.local),\n\t\t\t...options.backends,\n\t\t};\n\t\tthis.policyPath = options.policyPath ?? defaultPolicyPath();\n\t\tthis.initialRules = options.initialRules ?? [];\n\t\t// A JavaScript caller reaches this with no compiler in between, so the type is not\n\t\t// the whole guard. ADR 0034.\n\t\tif (typeof options.projectTrusted !== \"boolean\") {\n\t\t\tthrow new TypeError(\"FilePermissionRuleStore requires an explicit projectTrusted decision (ADR 0034)\");\n\t\t}\n\t\tthis.projectTrusted = options.projectTrusted;\n\t\tif (!this.projectTrusted) {\n\t\t\tthis.fileScopes.local = Promise.resolve({ scope: emptyScope() });\n\t\t\tthis.fileScopes.project = Promise.resolve({ scope: emptyScope() });\n\t\t}\n\t}\n\n\tprivate capturedFileBackedScope(source: FileBackedSource): Promise<{ scope: StoredPermissionScope; error?: Error }> {\n\t\tconst captured = this.fileScopes[source];\n\t\tif (captured) return captured;\n\t\tconst initial = this.readFileBackedScope(source);\n\t\tthis.fileScopes[source] = initial;\n\t\treturn initial;\n\t}\n\n\tprivate async readFileBackedScope(\n\t\tsource: FileBackedSource,\n\t): Promise<{ scope: StoredPermissionScope; error?: Error }> {\n\t\ttry {\n\t\t\tconst content = await this.backends[source].withReadLockAsync(async (current) => current);\n\t\t\treturn { scope: parseScope(content) };\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException)?.code === \"ENOENT\") return { scope: emptyScope() };\n\t\t\treturn { scope: emptyScope(), error: error instanceof Error ? error : new Error(String(error)) };\n\t\t}\n\t}\n\n\tprivate async readPolicy(): Promise<{ scope: StoredPermissionScope; error?: Error }> {\n\t\ttry {\n\t\t\tconst content = await readFile(this.policyPath, \"utf-8\");\n\t\t\treturn { scope: parseScope(content) };\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return { scope: emptyScope() };\n\t\t\treturn { scope: emptyScope(), error: error instanceof Error ? error : new Error(String(error)) };\n\t\t}\n\t}\n\n\tprivate capturedPolicyAndUser(): Promise<{\n\t\tpolicy: { scope: StoredPermissionScope; error?: Error };\n\t\tuser: { scope: StoredPermissionScope; error?: Error };\n\t}> {\n\t\treturn Promise.all([this.readPolicy(), this.capturedFileBackedScope(\"user\")]).then(([policy, user]) => ({\n\t\t\tpolicy,\n\t\t\tuser,\n\t\t}));\n\t}\n\n\tasync snapshot(): Promise<PermissionStoreSnapshot> {\n\t\t// File-backed authorization is captured once. Direct filesystem writes,\n\t\t// including symlink replacement, cannot widen the current or next gate\n\t\t// decision. Managed policy and runtime scopes remain live.\n\t\tconst [policyAndUser, local, project] = await Promise.all([\n\t\t\tthis.capturedPolicyAndUser(),\n\t\t\tthis.capturedFileBackedScope(\"local\"),\n\t\t\tthis.capturedFileBackedScope(\"project\"),\n\t\t]);\n\t\tconst { policy, user } = policyAndUser;\n\t\tconst errors: PermissionStoreError[] = [];\n\t\tconst withSource = (\n\t\t\tsource: PermissionSource,\n\t\t\tentry: { scope: StoredPermissionScope; error?: Error },\n\t\t): PermissionRule[] => {\n\t\t\tif (entry.error) errors.push({ source, error: entry.error });\n\t\t\treturn entry.scope.rules.map((rule) => ({ ...rule, source }));\n\t\t};\n\n\t\tconst rules: PermissionRule[] = [\n\t\t\t...withSource(\"policy\", policy),\n\t\t\t...this.initialRules,\n\t\t\t...withSource(\"local\", local),\n\t\t\t...withSource(\"project\", project),\n\t\t\t...withSource(\"user\", user),\n\t\t\t...this.runtimeRules.command.map((rule) => ({ ...rule, source: \"command\" as const })),\n\t\t\t...this.runtimeRules.session.map((rule) => ({ ...rule, source: \"session\" as const })),\n\t\t];\n\n\t\tconst modesBySource = new Map<WritablePermissionSource, PermissionMode>();\n\t\tif (local.scope.mode) modesBySource.set(\"local\", local.scope.mode);\n\t\tif (project.scope.mode) modesBySource.set(\"project\", project.scope.mode);\n\t\tif (user.scope.mode) modesBySource.set(\"user\", user.scope.mode);\n\t\tif (this.runtimeModes.command) modesBySource.set(\"command\", this.runtimeModes.command);\n\t\tif (this.runtimeModes.session) modesBySource.set(\"session\", this.runtimeModes.session);\n\n\t\treturn { rules, modesBySource, errors };\n\t}\n\n\tasync apply(update: PermissionUpdate): Promise<void> {\n\t\tif (update.destination === \"command\" || update.destination === \"session\") {\n\t\t\tthis.applyRuntime(update.destination, update);\n\t\t\treturn;\n\t\t}\n\t\tif (!this.projectTrusted && (update.destination === \"local\" || update.destination === \"project\")) {\n\t\t\tthrow new Error(`Cannot update untrusted ${update.destination} permission scope`);\n\t\t}\n\t\tconst destination = update.destination;\n\t\tconst previousUpdate = this.fileScopeUpdates[destination]?.catch(() => undefined) ?? Promise.resolve();\n\t\tconst nextUpdate = previousUpdate.then(async () => {\n\t\t\tconst current = await this.capturedFileBackedScope(destination);\n\t\t\tconst next = applyToScope(current.scope, update);\n\t\t\tawait this.backends[destination].withLockAsync(async () => ({\n\t\t\t\tresult: undefined,\n\t\t\t\tnext: JSON.stringify(next, null, 2),\n\t\t\t}));\n\t\t\tthis.fileScopes[destination] = Promise.resolve({ scope: next });\n\t\t});\n\t\tthis.fileScopeUpdates[destination] = nextUpdate;\n\t\tawait nextUpdate;\n\t}\n\n\tprivate applyRuntime(destination: RuntimeSource, update: PermissionUpdate): void {\n\t\tif (update.type === \"setMode\") {\n\t\t\tthis.runtimeModes[destination] = update.mode;\n\t\t\treturn;\n\t\t}\n\t\tconst current: StoredPermissionScope = { version: STORE_VERSION, rules: this.runtimeRules[destination] };\n\t\tthis.runtimeRules[destination] = applyToScope(current, update).rules;\n\t}\n}\n\nexport interface DerivedPermissionRuleStoreOptions {\n\t/** The parent's live store. Read fresh on every `snapshot()`, never cached, so a\n\t * rule the parent picks up mid-session (e.g. a human's \"always allow\") is visible\n\t * to the child on its very next call. */\n\tparent: PermissionRuleStore;\n}\n\n/**\n * A delegated child's permission store (roadmap Phase 5, ADR 0008). A read-through\n * view over the parent's live snapshot -- including its runtime-only sources\n * (`flag`, `cliArg`, `command`, `session`), which a subprocess-spawned child could\n * never observe -- plus a child-local runtime overlay for the child's own writes.\n *\n * `apply()` never reaches the parent. It writes only into this instance's own\n * overlay, and only for the two runtime-only writable sources (`command`, `session`);\n * a file-backed destination (`local`, `project`, `user`) is rejected outright, not\n * silently redirected -- a delegated child must never be able to persist to the\n * parent's settings files, and \"runtime-only\" is enforced by construction here, not\n * by a convention a future caller could forget. The overlay is discarded with the\n * child: there is no code path from a child's approval back into the parent's store,\n * which is what keeps a child's `persist: true` from widening its parent.\n */\nexport class DerivedPermissionRuleStore implements PermissionRuleStore {\n\tprivate readonly parent: PermissionRuleStore;\n\tprivate readonly overlayRules: Record<RuntimeSource, StoredPermissionRule[]> = { command: [], session: [] };\n\tprivate readonly overlayModes: Partial<Record<RuntimeSource, PermissionMode>> = {};\n\n\tconstructor(options: DerivedPermissionRuleStoreOptions) {\n\t\tthis.parent = options.parent;\n\t}\n\n\tasync snapshot(): Promise<PermissionStoreSnapshot> {\n\t\tconst parentSnapshot = await this.parent.snapshot();\n\t\tconst rules: PermissionRule[] = [\n\t\t\t...parentSnapshot.rules,\n\t\t\t...this.overlayRules.command.map((rule) => ({ ...rule, source: \"command\" as const })),\n\t\t\t...this.overlayRules.session.map((rule) => ({ ...rule, source: \"session\" as const })),\n\t\t];\n\t\tconst modesBySource = new Map(parentSnapshot.modesBySource);\n\t\tif (this.overlayModes.command !== undefined) modesBySource.set(\"command\", this.overlayModes.command);\n\t\tif (this.overlayModes.session !== undefined) modesBySource.set(\"session\", this.overlayModes.session);\n\t\treturn { rules, modesBySource, errors: parentSnapshot.errors };\n\t}\n\n\tasync apply(update: PermissionUpdate): Promise<void> {\n\t\tif (update.destination !== \"command\" && update.destination !== \"session\") {\n\t\t\tthrow new Error(\n\t\t\t\t`A delegated child cannot persist permission rules to \"${update.destination}\" -- only \"command\" and \"session\" are writable from a derived store (ADR 0008).`,\n\t\t\t);\n\t\t}\n\t\tif (update.type === \"setMode\") {\n\t\t\tthis.overlayModes[update.destination] = update.mode;\n\t\t\treturn;\n\t\t}\n\t\tconst current: StoredPermissionScope = {\n\t\t\tversion: STORE_VERSION,\n\t\t\trules: this.overlayRules[update.destination],\n\t\t};\n\t\tthis.overlayRules[update.destination] = applyToScope(current, update).rules;\n\t}\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import type { ResourceDiagnostic } from "./diagnostics.ts";
1
2
  import { type SourceInfo } from "./source-info.ts";
2
3
  /**
3
4
  * Represents a prompt template loaded from a markdown file
@@ -39,13 +40,17 @@ export interface LoadPromptTemplatesOptions {
39
40
  /** Include default prompt directories. */
40
41
  includeDefaults: boolean;
41
42
  }
43
+ export interface LoadPromptTemplatesResult {
44
+ templates: PromptTemplate[];
45
+ diagnostics: ResourceDiagnostic[];
46
+ }
42
47
  /**
43
48
  * Load all prompt templates from:
44
49
  * 1. Global: agentDir/prompts/
45
50
  * 2. Project: cwd/{CONFIG_DIR_NAME}/prompts/
46
51
  * 3. Explicit prompt paths
47
52
  */
48
- export declare function loadPromptTemplates(options: LoadPromptTemplatesOptions): PromptTemplate[];
53
+ export declare function loadPromptTemplates(options: LoadPromptTemplatesOptions): LoadPromptTemplatesResult;
49
54
  /**
50
55
  * Expand a prompt template if it matches a template name.
51
56
  * Returns the expanded content or the original text if not a template.
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-templates.d.ts","sourceRoot":"","sources":["../../src/core/prompt-templates.ts"],"names":[],"mappings":"AAKA,OAAO,EAA6B,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CA+B7D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAgCtE;AA2ED,MAAM,WAAW,0BAA0B;IAC1C,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,0CAA0C;IAC1C,eAAe,EAAE,OAAO,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,cAAc,EAAE,CAqEzF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,MAAM,CAgBtF","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join, resolve, sep } from \"path\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { parseFrontmatter } from \"../utils/frontmatter.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { createSyntheticSourceInfo, type SourceInfo } from \"./source-info.ts\";\n\n/**\n * Represents a prompt template loaded from a markdown file\n */\nexport interface PromptTemplate {\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tcontent: string;\n\tsourceInfo: SourceInfo;\n\tfilePath: string; // Absolute path to the template file\n}\n\n/**\n * Parse command arguments respecting quoted strings (bash-style)\n * Returns array of arguments\n */\nexport function parseCommandArgs(argsString: string): string[] {\n\tconst args: string[] = [];\n\tlet current = \"\";\n\tlet inQuote: string | null = null;\n\n\tfor (let i = 0; i < argsString.length; i++) {\n\t\tconst char = argsString[i];\n\n\t\tif (inQuote) {\n\t\t\tif (char === inQuote) {\n\t\t\t\tinQuote = null;\n\t\t\t} else {\n\t\t\t\tcurrent += char;\n\t\t\t}\n\t\t} else if (char === '\"' || char === \"'\") {\n\t\t\tinQuote = char;\n\t\t} else if (/\\s/.test(char)) {\n\t\t\tif (current) {\n\t\t\t\targs.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t}\n\t}\n\n\tif (current) {\n\t\targs.push(current);\n\t}\n\n\treturn args;\n}\n\n/**\n * Substitute argument placeholders in template content\n * Supports:\n * - $1, $2, ... for positional args\n * - $@ and $ARGUMENTS for all args\n * - ${N:-default} for positional arg N with default when missing/empty\n * - ${@:-default} and ${ARGUMENTS:-default} for all args with a default when empty\n * - ${@:N} for args from Nth onwards (bash-style slicing)\n * - ${@:N:L} for L args starting from Nth\n *\n * Note: Replacement happens on the template string only. Argument and default values\n * containing patterns like $1, $@, or $ARGUMENTS are NOT recursively substituted.\n */\nexport function substituteArgs(content: string, args: string[]): string {\n\tconst allArgs = args.join(\" \");\n\n\treturn content.replace(\n\t\t/\\$\\{(\\d+|ARGUMENTS|@):-([^}]*)\\}|\\$\\{@:(\\d+)(?::(\\d+))?\\}|\\$(ARGUMENTS|@|\\d+)/g,\n\t\t(_match, defaultTarget, defaultValue, sliceStart, sliceLength, simple) => {\n\t\t\tif (defaultTarget) {\n\t\t\t\tconst value =\n\t\t\t\t\tdefaultTarget === \"@\" || defaultTarget === \"ARGUMENTS\" ? allArgs : args[parseInt(defaultTarget, 10) - 1];\n\t\t\t\treturn value ? value : defaultValue;\n\t\t\t}\n\n\t\t\tif (sliceStart) {\n\t\t\t\tlet start = parseInt(sliceStart, 10) - 1; // Convert to 0-indexed (user provides 1-indexed)\n\t\t\t\t// Treat 0 as 1 (bash convention: args start at 1)\n\t\t\t\tif (start < 0) start = 0;\n\n\t\t\t\tif (sliceLength) {\n\t\t\t\t\tconst length = parseInt(sliceLength, 10);\n\t\t\t\t\treturn args.slice(start, start + length).join(\" \");\n\t\t\t\t}\n\t\t\t\treturn args.slice(start).join(\" \");\n\t\t\t}\n\n\t\t\tif (simple === \"ARGUMENTS\" || simple === \"@\") {\n\t\t\t\treturn allArgs;\n\t\t\t}\n\n\t\t\tconst index = parseInt(simple, 10) - 1;\n\t\t\treturn args[index] ?? \"\";\n\t\t},\n\t);\n}\n\nfunction loadTemplateFromFile(filePath: string, sourceInfo: SourceInfo): PromptTemplate | null {\n\ttry {\n\t\tconst rawContent = readFileSync(filePath, \"utf-8\");\n\t\tconst { frontmatter, body } = parseFrontmatter<Record<string, string>>(rawContent);\n\n\t\tconst name = basename(filePath).replace(/\\.md$/, \"\");\n\n\t\t// Get description from frontmatter or first non-empty line\n\t\tlet description = frontmatter.description || \"\";\n\t\tif (!description) {\n\t\t\tconst firstLine = body.split(\"\\n\").find((line) => line.trim());\n\t\t\tif (firstLine) {\n\t\t\t\t// Truncate if too long\n\t\t\t\tdescription = firstLine.slice(0, 60);\n\t\t\t\tif (firstLine.length > 60) description += \"...\";\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname,\n\t\t\tdescription,\n\t\t\t...(frontmatter[\"argument-hint\"] && { argumentHint: frontmatter[\"argument-hint\"] }),\n\t\t\tcontent: body,\n\t\t\tsourceInfo,\n\t\t\tfilePath,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Scan a directory for .md files (non-recursive) and load them as prompt templates.\n */\nfunction loadTemplatesFromDir(dir: string, getSourceInfo: (filePath: string) => SourceInfo): PromptTemplate[] {\n\tconst templates: PromptTemplate[] = [];\n\n\tif (!existsSync(dir)) {\n\t\treturn templates;\n\t}\n\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\t// For symlinks, check if they point to a file\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(fullPath);\n\t\t\t\t\tisFile = stats.isFile();\n\t\t\t\t} catch {\n\t\t\t\t\t// Broken symlink, skip it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isFile && entry.name.endsWith(\".md\")) {\n\t\t\t\tconst template = loadTemplateFromFile(fullPath, getSourceInfo(fullPath));\n\t\t\t\tif (template) {\n\t\t\t\t\ttemplates.push(template);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn templates;\n\t}\n\n\treturn templates;\n}\n\nexport interface LoadPromptTemplatesOptions {\n\t/** Working directory for project-local templates. */\n\tcwd: string;\n\t/** Agent config directory for global templates. */\n\tagentDir: string;\n\t/** Explicit prompt template paths (files or directories). */\n\tpromptPaths: string[];\n\t/** Include default prompt directories. */\n\tincludeDefaults: boolean;\n}\n\n/**\n * Load all prompt templates from:\n * 1. Global: agentDir/prompts/\n * 2. Project: cwd/{CONFIG_DIR_NAME}/prompts/\n * 3. Explicit prompt paths\n */\nexport function loadPromptTemplates(options: LoadPromptTemplatesOptions): PromptTemplate[] {\n\tconst resolvedCwd = resolvePath(options.cwd);\n\tconst resolvedAgentDir = resolvePath(options.agentDir);\n\tconst promptPaths = options.promptPaths;\n\tconst includeDefaults = options.includeDefaults;\n\n\tconst templates: PromptTemplate[] = [];\n\n\tconst globalPromptsDir = join(resolvedAgentDir, \"prompts\");\n\tconst projectPromptsDir = resolve(resolvedCwd, CONFIG_DIR_NAME, \"prompts\");\n\n\tconst isUnderPath = (target: string, root: string): boolean => {\n\t\tconst normalizedRoot = resolve(root);\n\t\tif (target === normalizedRoot) {\n\t\t\treturn true;\n\t\t}\n\t\tconst prefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;\n\t\treturn target.startsWith(prefix);\n\t};\n\n\tconst getSourceInfo = (resolvedPath: string): SourceInfo => {\n\t\tif (isUnderPath(resolvedPath, globalPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"user\",\n\t\t\t\tbaseDir: globalPromptsDir,\n\t\t\t});\n\t\t}\n\t\tif (isUnderPath(resolvedPath, projectPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"project\",\n\t\t\t\tbaseDir: projectPromptsDir,\n\t\t\t});\n\t\t}\n\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\tsource: \"local\",\n\t\t\tbaseDir: statSync(resolvedPath).isDirectory() ? resolvedPath : dirname(resolvedPath),\n\t\t});\n\t};\n\n\tif (includeDefaults) {\n\t\ttemplates.push(...loadTemplatesFromDir(globalPromptsDir, getSourceInfo));\n\t\ttemplates.push(...loadTemplatesFromDir(projectPromptsDir, getSourceInfo));\n\t}\n\n\t// 3. Load explicit prompt paths\n\tfor (const rawPath of promptPaths) {\n\t\tconst resolvedPath = resolvePath(rawPath, resolvedCwd, { trim: true });\n\t\tif (!existsSync(resolvedPath)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst stats = statSync(resolvedPath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\ttemplates.push(...loadTemplatesFromDir(resolvedPath, getSourceInfo));\n\t\t\t} else if (stats.isFile() && resolvedPath.endsWith(\".md\")) {\n\t\t\t\tconst template = loadTemplateFromFile(resolvedPath, getSourceInfo(resolvedPath));\n\t\t\t\tif (template) {\n\t\t\t\t\ttemplates.push(template);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore read failures\n\t\t}\n\t}\n\n\treturn templates;\n}\n\n/**\n * Expand a prompt template if it matches a template name.\n * Returns the expanded content or the original text if not a template.\n */\nexport function expandPromptTemplate(text: string, templates: PromptTemplate[]): string {\n\tif (!text.startsWith(\"/\")) return text;\n\n\tconst match = text.match(/^\\/([^\\s]+)(?:\\s+([\\s\\S]*))?$/);\n\tif (!match) return text;\n\n\tconst templateName = match[1];\n\tconst argsString = match[2] ?? \"\";\n\n\tconst template = templates.find((t) => t.name === templateName);\n\tif (template) {\n\t\tconst args = parseCommandArgs(argsString);\n\t\treturn substituteArgs(template.content, args);\n\t}\n\n\treturn text;\n}\n"]}
1
+ {"version":3,"file":"prompt-templates.d.ts","sourceRoot":"","sources":["../../src/core/prompt-templates.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAA6B,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CA+B7D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAgCtE;AAiGD,MAAM,WAAW,0BAA0B;IAC1C,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,0CAA0C;IAC1C,eAAe,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACzC,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,WAAW,EAAE,kBAAkB,EAAE,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,yBAAyB,CA4ElG;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,MAAM,CAgBtF","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join, resolve, sep } from \"path\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { parseFrontmatter } from \"../utils/frontmatter.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport type { ResourceDiagnostic } from \"./diagnostics.ts\";\nimport { createSyntheticSourceInfo, type SourceInfo } from \"./source-info.ts\";\n\n/**\n * Represents a prompt template loaded from a markdown file\n */\nexport interface PromptTemplate {\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tcontent: string;\n\tsourceInfo: SourceInfo;\n\tfilePath: string; // Absolute path to the template file\n}\n\n/**\n * Parse command arguments respecting quoted strings (bash-style)\n * Returns array of arguments\n */\nexport function parseCommandArgs(argsString: string): string[] {\n\tconst args: string[] = [];\n\tlet current = \"\";\n\tlet inQuote: string | null = null;\n\n\tfor (let i = 0; i < argsString.length; i++) {\n\t\tconst char = argsString[i];\n\n\t\tif (inQuote) {\n\t\t\tif (char === inQuote) {\n\t\t\t\tinQuote = null;\n\t\t\t} else {\n\t\t\t\tcurrent += char;\n\t\t\t}\n\t\t} else if (char === '\"' || char === \"'\") {\n\t\t\tinQuote = char;\n\t\t} else if (/\\s/.test(char)) {\n\t\t\tif (current) {\n\t\t\t\targs.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t}\n\t}\n\n\tif (current) {\n\t\targs.push(current);\n\t}\n\n\treturn args;\n}\n\n/**\n * Substitute argument placeholders in template content\n * Supports:\n * - $1, $2, ... for positional args\n * - $@ and $ARGUMENTS for all args\n * - ${N:-default} for positional arg N with default when missing/empty\n * - ${@:-default} and ${ARGUMENTS:-default} for all args with a default when empty\n * - ${@:N} for args from Nth onwards (bash-style slicing)\n * - ${@:N:L} for L args starting from Nth\n *\n * Note: Replacement happens on the template string only. Argument and default values\n * containing patterns like $1, $@, or $ARGUMENTS are NOT recursively substituted.\n */\nexport function substituteArgs(content: string, args: string[]): string {\n\tconst allArgs = args.join(\" \");\n\n\treturn content.replace(\n\t\t/\\$\\{(\\d+|ARGUMENTS|@):-([^}]*)\\}|\\$\\{@:(\\d+)(?::(\\d+))?\\}|\\$(ARGUMENTS|@|\\d+)/g,\n\t\t(_match, defaultTarget, defaultValue, sliceStart, sliceLength, simple) => {\n\t\t\tif (defaultTarget) {\n\t\t\t\tconst value =\n\t\t\t\t\tdefaultTarget === \"@\" || defaultTarget === \"ARGUMENTS\" ? allArgs : args[parseInt(defaultTarget, 10) - 1];\n\t\t\t\treturn value ? value : defaultValue;\n\t\t\t}\n\n\t\t\tif (sliceStart) {\n\t\t\t\tlet start = parseInt(sliceStart, 10) - 1; // Convert to 0-indexed (user provides 1-indexed)\n\t\t\t\t// Treat 0 as 1 (bash convention: args start at 1)\n\t\t\t\tif (start < 0) start = 0;\n\n\t\t\t\tif (sliceLength) {\n\t\t\t\t\tconst length = parseInt(sliceLength, 10);\n\t\t\t\t\treturn args.slice(start, start + length).join(\" \");\n\t\t\t\t}\n\t\t\t\treturn args.slice(start).join(\" \");\n\t\t\t}\n\n\t\t\tif (simple === \"ARGUMENTS\" || simple === \"@\") {\n\t\t\t\treturn allArgs;\n\t\t\t}\n\n\t\t\tconst index = parseInt(simple, 10) - 1;\n\t\t\treturn args[index] ?? \"\";\n\t\t},\n\t);\n}\n\nfunction loadTemplateFromFile(\n\tfilePath: string,\n\tsourceInfo: SourceInfo,\n): { template: PromptTemplate | null; diagnostics: ResourceDiagnostic[] } {\n\tconst diagnostics: ResourceDiagnostic[] = [];\n\tlet rawContent: string;\n\ttry {\n\t\trawContent = readFileSync(filePath, \"utf-8\");\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : \"failed to read prompt template file\";\n\t\tdiagnostics.push({ type: \"warning\", message, path: filePath });\n\t\treturn { template: null, diagnostics };\n\t}\n\n\tlet frontmatter: Record<string, unknown>;\n\tlet body: string;\n\ttry {\n\t\t({ frontmatter, body } = parseFrontmatter(rawContent));\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : \"failed to parse prompt template file\";\n\t\tdiagnostics.push({ type: \"warning\", message, path: filePath });\n\t\treturn { template: null, diagnostics };\n\t}\n\n\tconst name = basename(filePath).replace(/\\.md$/, \"\");\n\n\t// Get description from frontmatter or first non-empty line\n\tlet description = typeof frontmatter.description === \"string\" ? frontmatter.description : \"\";\n\tif (!description) {\n\t\tconst firstLine = body.split(\"\\n\").find((line) => line.trim());\n\t\tif (firstLine) {\n\t\t\t// Truncate if too long\n\t\t\tdescription = firstLine.slice(0, 60);\n\t\t\tif (firstLine.length > 60) description += \"...\";\n\t\t}\n\t}\n\n\tconst argumentHint = typeof frontmatter[\"argument-hint\"] === \"string\" ? frontmatter[\"argument-hint\"] : undefined;\n\treturn {\n\t\ttemplate: {\n\t\t\tname,\n\t\t\tdescription,\n\t\t\t...(argumentHint && { argumentHint }),\n\t\t\tcontent: body,\n\t\t\tsourceInfo,\n\t\t\tfilePath,\n\t\t},\n\t\tdiagnostics,\n\t};\n}\n\n/**\n * Scan a directory for .md files (non-recursive) and load them as prompt templates.\n */\nfunction loadTemplatesFromDir(dir: string, getSourceInfo: (filePath: string) => SourceInfo): LoadPromptTemplatesResult {\n\tconst templates: PromptTemplate[] = [];\n\tconst diagnostics: ResourceDiagnostic[] = [];\n\n\tif (!existsSync(dir)) {\n\t\treturn { templates, diagnostics };\n\t}\n\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\t// For symlinks, check if they point to a file\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(fullPath);\n\t\t\t\t\tisFile = stats.isFile();\n\t\t\t\t} catch {\n\t\t\t\t\t// Broken symlink, skip it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isFile && entry.name.endsWith(\".md\")) {\n\t\t\t\tconst result = loadTemplateFromFile(fullPath, getSourceInfo(fullPath));\n\t\t\t\tif (result.template) {\n\t\t\t\t\ttemplates.push(result.template);\n\t\t\t\t}\n\t\t\t\tdiagnostics.push(...result.diagnostics);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn { templates, diagnostics };\n\t}\n\n\treturn { templates, diagnostics };\n}\n\nexport interface LoadPromptTemplatesOptions {\n\t/** Working directory for project-local templates. */\n\tcwd: string;\n\t/** Agent config directory for global templates. */\n\tagentDir: string;\n\t/** Explicit prompt template paths (files or directories). */\n\tpromptPaths: string[];\n\t/** Include default prompt directories. */\n\tincludeDefaults: boolean;\n}\n\nexport interface LoadPromptTemplatesResult {\n\ttemplates: PromptTemplate[];\n\tdiagnostics: ResourceDiagnostic[];\n}\n\n/**\n * Load all prompt templates from:\n * 1. Global: agentDir/prompts/\n * 2. Project: cwd/{CONFIG_DIR_NAME}/prompts/\n * 3. Explicit prompt paths\n */\nexport function loadPromptTemplates(options: LoadPromptTemplatesOptions): LoadPromptTemplatesResult {\n\tconst resolvedCwd = resolvePath(options.cwd);\n\tconst resolvedAgentDir = resolvePath(options.agentDir);\n\tconst promptPaths = options.promptPaths;\n\tconst includeDefaults = options.includeDefaults;\n\n\tconst templates: PromptTemplate[] = [];\n\tconst diagnostics: ResourceDiagnostic[] = [];\n\tconst addResult = (result: LoadPromptTemplatesResult): void => {\n\t\ttemplates.push(...result.templates);\n\t\tdiagnostics.push(...result.diagnostics);\n\t};\n\n\tconst globalPromptsDir = join(resolvedAgentDir, \"prompts\");\n\tconst projectPromptsDir = resolve(resolvedCwd, CONFIG_DIR_NAME, \"prompts\");\n\n\tconst isUnderPath = (target: string, root: string): boolean => {\n\t\tconst normalizedRoot = resolve(root);\n\t\tif (target === normalizedRoot) {\n\t\t\treturn true;\n\t\t}\n\t\tconst prefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;\n\t\treturn target.startsWith(prefix);\n\t};\n\n\tconst getSourceInfo = (resolvedPath: string): SourceInfo => {\n\t\tif (isUnderPath(resolvedPath, globalPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"user\",\n\t\t\t\tbaseDir: globalPromptsDir,\n\t\t\t});\n\t\t}\n\t\tif (isUnderPath(resolvedPath, projectPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"project\",\n\t\t\t\tbaseDir: projectPromptsDir,\n\t\t\t});\n\t\t}\n\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\tsource: \"local\",\n\t\t\tbaseDir: statSync(resolvedPath).isDirectory() ? resolvedPath : dirname(resolvedPath),\n\t\t});\n\t};\n\n\tif (includeDefaults) {\n\t\taddResult(loadTemplatesFromDir(globalPromptsDir, getSourceInfo));\n\t\taddResult(loadTemplatesFromDir(projectPromptsDir, getSourceInfo));\n\t}\n\n\t// 3. Load explicit prompt paths\n\tfor (const rawPath of promptPaths) {\n\t\tconst resolvedPath = resolvePath(rawPath, resolvedCwd, { trim: true });\n\t\tif (!existsSync(resolvedPath)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst stats = statSync(resolvedPath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\taddResult(loadTemplatesFromDir(resolvedPath, getSourceInfo));\n\t\t\t} else if (stats.isFile() && resolvedPath.endsWith(\".md\")) {\n\t\t\t\tconst result = loadTemplateFromFile(resolvedPath, getSourceInfo(resolvedPath));\n\t\t\t\tif (result.template) {\n\t\t\t\t\ttemplates.push(result.template);\n\t\t\t\t}\n\t\t\t\tdiagnostics.push(...result.diagnostics);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"failed to read prompt template path\";\n\t\t\tdiagnostics.push({ type: \"warning\", message, path: resolvedPath });\n\t\t}\n\t}\n\n\treturn { templates, diagnostics };\n}\n\n/**\n * Expand a prompt template if it matches a template name.\n * Returns the expanded content or the original text if not a template.\n */\nexport function expandPromptTemplate(text: string, templates: PromptTemplate[]): string {\n\tif (!text.startsWith(\"/\")) return text;\n\n\tconst match = text.match(/^\\/([^\\s]+)(?:\\s+([\\s\\S]*))?$/);\n\tif (!match) return text;\n\n\tconst templateName = match[1];\n\tconst argsString = match[2] ?? \"\";\n\n\tconst template = templates.find((t) => t.name === templateName);\n\tif (template) {\n\t\tconst args = parseCommandArgs(argsString);\n\t\treturn substituteArgs(template.content, args);\n\t}\n\n\treturn text;\n}\n"]}
@@ -79,41 +79,59 @@ export function substituteArgs(content, args) {
79
79
  });
80
80
  }
81
81
  function loadTemplateFromFile(filePath, sourceInfo) {
82
+ const diagnostics = [];
83
+ let rawContent;
82
84
  try {
83
- const rawContent = readFileSync(filePath, "utf-8");
84
- const { frontmatter, body } = parseFrontmatter(rawContent);
85
- const name = basename(filePath).replace(/\.md$/, "");
86
- // Get description from frontmatter or first non-empty line
87
- let description = frontmatter.description || "";
88
- if (!description) {
89
- const firstLine = body.split("\n").find((line) => line.trim());
90
- if (firstLine) {
91
- // Truncate if too long
92
- description = firstLine.slice(0, 60);
93
- if (firstLine.length > 60)
94
- description += "...";
95
- }
85
+ rawContent = readFileSync(filePath, "utf-8");
86
+ }
87
+ catch (error) {
88
+ const message = error instanceof Error ? error.message : "failed to read prompt template file";
89
+ diagnostics.push({ type: "warning", message, path: filePath });
90
+ return { template: null, diagnostics };
91
+ }
92
+ let frontmatter;
93
+ let body;
94
+ try {
95
+ ({ frontmatter, body } = parseFrontmatter(rawContent));
96
+ }
97
+ catch (error) {
98
+ const message = error instanceof Error ? error.message : "failed to parse prompt template file";
99
+ diagnostics.push({ type: "warning", message, path: filePath });
100
+ return { template: null, diagnostics };
101
+ }
102
+ const name = basename(filePath).replace(/\.md$/, "");
103
+ // Get description from frontmatter or first non-empty line
104
+ let description = typeof frontmatter.description === "string" ? frontmatter.description : "";
105
+ if (!description) {
106
+ const firstLine = body.split("\n").find((line) => line.trim());
107
+ if (firstLine) {
108
+ // Truncate if too long
109
+ description = firstLine.slice(0, 60);
110
+ if (firstLine.length > 60)
111
+ description += "...";
96
112
  }
97
- return {
113
+ }
114
+ const argumentHint = typeof frontmatter["argument-hint"] === "string" ? frontmatter["argument-hint"] : undefined;
115
+ return {
116
+ template: {
98
117
  name,
99
118
  description,
100
- ...(frontmatter["argument-hint"] && { argumentHint: frontmatter["argument-hint"] }),
119
+ ...(argumentHint && { argumentHint }),
101
120
  content: body,
102
121
  sourceInfo,
103
122
  filePath,
104
- };
105
- }
106
- catch {
107
- return null;
108
- }
123
+ },
124
+ diagnostics,
125
+ };
109
126
  }
110
127
  /**
111
128
  * Scan a directory for .md files (non-recursive) and load them as prompt templates.
112
129
  */
113
130
  function loadTemplatesFromDir(dir, getSourceInfo) {
114
131
  const templates = [];
132
+ const diagnostics = [];
115
133
  if (!existsSync(dir)) {
116
- return templates;
134
+ return { templates, diagnostics };
117
135
  }
118
136
  try {
119
137
  const entries = readdirSync(dir, { withFileTypes: true });
@@ -132,17 +150,18 @@ function loadTemplatesFromDir(dir, getSourceInfo) {
132
150
  }
133
151
  }
134
152
  if (isFile && entry.name.endsWith(".md")) {
135
- const template = loadTemplateFromFile(fullPath, getSourceInfo(fullPath));
136
- if (template) {
137
- templates.push(template);
153
+ const result = loadTemplateFromFile(fullPath, getSourceInfo(fullPath));
154
+ if (result.template) {
155
+ templates.push(result.template);
138
156
  }
157
+ diagnostics.push(...result.diagnostics);
139
158
  }
140
159
  }
141
160
  }
142
161
  catch {
143
- return templates;
162
+ return { templates, diagnostics };
144
163
  }
145
- return templates;
164
+ return { templates, diagnostics };
146
165
  }
147
166
  /**
148
167
  * Load all prompt templates from:
@@ -156,6 +175,11 @@ export function loadPromptTemplates(options) {
156
175
  const promptPaths = options.promptPaths;
157
176
  const includeDefaults = options.includeDefaults;
158
177
  const templates = [];
178
+ const diagnostics = [];
179
+ const addResult = (result) => {
180
+ templates.push(...result.templates);
181
+ diagnostics.push(...result.diagnostics);
182
+ };
159
183
  const globalPromptsDir = join(resolvedAgentDir, "prompts");
160
184
  const projectPromptsDir = resolve(resolvedCwd, CONFIG_DIR_NAME, "prompts");
161
185
  const isUnderPath = (target, root) => {
@@ -187,8 +211,8 @@ export function loadPromptTemplates(options) {
187
211
  });
188
212
  };
189
213
  if (includeDefaults) {
190
- templates.push(...loadTemplatesFromDir(globalPromptsDir, getSourceInfo));
191
- templates.push(...loadTemplatesFromDir(projectPromptsDir, getSourceInfo));
214
+ addResult(loadTemplatesFromDir(globalPromptsDir, getSourceInfo));
215
+ addResult(loadTemplatesFromDir(projectPromptsDir, getSourceInfo));
192
216
  }
193
217
  // 3. Load explicit prompt paths
194
218
  for (const rawPath of promptPaths) {
@@ -199,20 +223,22 @@ export function loadPromptTemplates(options) {
199
223
  try {
200
224
  const stats = statSync(resolvedPath);
201
225
  if (stats.isDirectory()) {
202
- templates.push(...loadTemplatesFromDir(resolvedPath, getSourceInfo));
226
+ addResult(loadTemplatesFromDir(resolvedPath, getSourceInfo));
203
227
  }
204
228
  else if (stats.isFile() && resolvedPath.endsWith(".md")) {
205
- const template = loadTemplateFromFile(resolvedPath, getSourceInfo(resolvedPath));
206
- if (template) {
207
- templates.push(template);
229
+ const result = loadTemplateFromFile(resolvedPath, getSourceInfo(resolvedPath));
230
+ if (result.template) {
231
+ templates.push(result.template);
208
232
  }
233
+ diagnostics.push(...result.diagnostics);
209
234
  }
210
235
  }
211
- catch {
212
- // Ignore read failures
236
+ catch (error) {
237
+ const message = error instanceof Error ? error.message : "failed to read prompt template path";
238
+ diagnostics.push({ type: "warning", message, path: resolvedPath });
213
239
  }
214
240
  }
215
- return templates;
241
+ return { templates, diagnostics };
216
242
  }
217
243
  /**
218
244
  * Expand a prompt template if it matches a template name.
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-templates.js","sourceRoot":"","sources":["../../src/core/prompt-templates.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join, resolve, sep } from \"path\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { parseFrontmatter } from \"../utils/frontmatter.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { createSyntheticSourceInfo, type SourceInfo } from \"./source-info.ts\";\n\n/**\n * Represents a prompt template loaded from a markdown file\n */\nexport interface PromptTemplate {\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tcontent: string;\n\tsourceInfo: SourceInfo;\n\tfilePath: string; // Absolute path to the template file\n}\n\n/**\n * Parse command arguments respecting quoted strings (bash-style)\n * Returns array of arguments\n */\nexport function parseCommandArgs(argsString: string): string[] {\n\tconst args: string[] = [];\n\tlet current = \"\";\n\tlet inQuote: string | null = null;\n\n\tfor (let i = 0; i < argsString.length; i++) {\n\t\tconst char = argsString[i];\n\n\t\tif (inQuote) {\n\t\t\tif (char === inQuote) {\n\t\t\t\tinQuote = null;\n\t\t\t} else {\n\t\t\t\tcurrent += char;\n\t\t\t}\n\t\t} else if (char === '\"' || char === \"'\") {\n\t\t\tinQuote = char;\n\t\t} else if (/\\s/.test(char)) {\n\t\t\tif (current) {\n\t\t\t\targs.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t}\n\t}\n\n\tif (current) {\n\t\targs.push(current);\n\t}\n\n\treturn args;\n}\n\n/**\n * Substitute argument placeholders in template content\n * Supports:\n * - $1, $2, ... for positional args\n * - $@ and $ARGUMENTS for all args\n * - ${N:-default} for positional arg N with default when missing/empty\n * - ${@:-default} and ${ARGUMENTS:-default} for all args with a default when empty\n * - ${@:N} for args from Nth onwards (bash-style slicing)\n * - ${@:N:L} for L args starting from Nth\n *\n * Note: Replacement happens on the template string only. Argument and default values\n * containing patterns like $1, $@, or $ARGUMENTS are NOT recursively substituted.\n */\nexport function substituteArgs(content: string, args: string[]): string {\n\tconst allArgs = args.join(\" \");\n\n\treturn content.replace(\n\t\t/\\$\\{(\\d+|ARGUMENTS|@):-([^}]*)\\}|\\$\\{@:(\\d+)(?::(\\d+))?\\}|\\$(ARGUMENTS|@|\\d+)/g,\n\t\t(_match, defaultTarget, defaultValue, sliceStart, sliceLength, simple) => {\n\t\t\tif (defaultTarget) {\n\t\t\t\tconst value =\n\t\t\t\t\tdefaultTarget === \"@\" || defaultTarget === \"ARGUMENTS\" ? allArgs : args[parseInt(defaultTarget, 10) - 1];\n\t\t\t\treturn value ? value : defaultValue;\n\t\t\t}\n\n\t\t\tif (sliceStart) {\n\t\t\t\tlet start = parseInt(sliceStart, 10) - 1; // Convert to 0-indexed (user provides 1-indexed)\n\t\t\t\t// Treat 0 as 1 (bash convention: args start at 1)\n\t\t\t\tif (start < 0) start = 0;\n\n\t\t\t\tif (sliceLength) {\n\t\t\t\t\tconst length = parseInt(sliceLength, 10);\n\t\t\t\t\treturn args.slice(start, start + length).join(\" \");\n\t\t\t\t}\n\t\t\t\treturn args.slice(start).join(\" \");\n\t\t\t}\n\n\t\t\tif (simple === \"ARGUMENTS\" || simple === \"@\") {\n\t\t\t\treturn allArgs;\n\t\t\t}\n\n\t\t\tconst index = parseInt(simple, 10) - 1;\n\t\t\treturn args[index] ?? \"\";\n\t\t},\n\t);\n}\n\nfunction loadTemplateFromFile(filePath: string, sourceInfo: SourceInfo): PromptTemplate | null {\n\ttry {\n\t\tconst rawContent = readFileSync(filePath, \"utf-8\");\n\t\tconst { frontmatter, body } = parseFrontmatter<Record<string, string>>(rawContent);\n\n\t\tconst name = basename(filePath).replace(/\\.md$/, \"\");\n\n\t\t// Get description from frontmatter or first non-empty line\n\t\tlet description = frontmatter.description || \"\";\n\t\tif (!description) {\n\t\t\tconst firstLine = body.split(\"\\n\").find((line) => line.trim());\n\t\t\tif (firstLine) {\n\t\t\t\t// Truncate if too long\n\t\t\t\tdescription = firstLine.slice(0, 60);\n\t\t\t\tif (firstLine.length > 60) description += \"...\";\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname,\n\t\t\tdescription,\n\t\t\t...(frontmatter[\"argument-hint\"] && { argumentHint: frontmatter[\"argument-hint\"] }),\n\t\t\tcontent: body,\n\t\t\tsourceInfo,\n\t\t\tfilePath,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Scan a directory for .md files (non-recursive) and load them as prompt templates.\n */\nfunction loadTemplatesFromDir(dir: string, getSourceInfo: (filePath: string) => SourceInfo): PromptTemplate[] {\n\tconst templates: PromptTemplate[] = [];\n\n\tif (!existsSync(dir)) {\n\t\treturn templates;\n\t}\n\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\t// For symlinks, check if they point to a file\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(fullPath);\n\t\t\t\t\tisFile = stats.isFile();\n\t\t\t\t} catch {\n\t\t\t\t\t// Broken symlink, skip it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isFile && entry.name.endsWith(\".md\")) {\n\t\t\t\tconst template = loadTemplateFromFile(fullPath, getSourceInfo(fullPath));\n\t\t\t\tif (template) {\n\t\t\t\t\ttemplates.push(template);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn templates;\n\t}\n\n\treturn templates;\n}\n\nexport interface LoadPromptTemplatesOptions {\n\t/** Working directory for project-local templates. */\n\tcwd: string;\n\t/** Agent config directory for global templates. */\n\tagentDir: string;\n\t/** Explicit prompt template paths (files or directories). */\n\tpromptPaths: string[];\n\t/** Include default prompt directories. */\n\tincludeDefaults: boolean;\n}\n\n/**\n * Load all prompt templates from:\n * 1. Global: agentDir/prompts/\n * 2. Project: cwd/{CONFIG_DIR_NAME}/prompts/\n * 3. Explicit prompt paths\n */\nexport function loadPromptTemplates(options: LoadPromptTemplatesOptions): PromptTemplate[] {\n\tconst resolvedCwd = resolvePath(options.cwd);\n\tconst resolvedAgentDir = resolvePath(options.agentDir);\n\tconst promptPaths = options.promptPaths;\n\tconst includeDefaults = options.includeDefaults;\n\n\tconst templates: PromptTemplate[] = [];\n\n\tconst globalPromptsDir = join(resolvedAgentDir, \"prompts\");\n\tconst projectPromptsDir = resolve(resolvedCwd, CONFIG_DIR_NAME, \"prompts\");\n\n\tconst isUnderPath = (target: string, root: string): boolean => {\n\t\tconst normalizedRoot = resolve(root);\n\t\tif (target === normalizedRoot) {\n\t\t\treturn true;\n\t\t}\n\t\tconst prefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;\n\t\treturn target.startsWith(prefix);\n\t};\n\n\tconst getSourceInfo = (resolvedPath: string): SourceInfo => {\n\t\tif (isUnderPath(resolvedPath, globalPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"user\",\n\t\t\t\tbaseDir: globalPromptsDir,\n\t\t\t});\n\t\t}\n\t\tif (isUnderPath(resolvedPath, projectPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"project\",\n\t\t\t\tbaseDir: projectPromptsDir,\n\t\t\t});\n\t\t}\n\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\tsource: \"local\",\n\t\t\tbaseDir: statSync(resolvedPath).isDirectory() ? resolvedPath : dirname(resolvedPath),\n\t\t});\n\t};\n\n\tif (includeDefaults) {\n\t\ttemplates.push(...loadTemplatesFromDir(globalPromptsDir, getSourceInfo));\n\t\ttemplates.push(...loadTemplatesFromDir(projectPromptsDir, getSourceInfo));\n\t}\n\n\t// 3. Load explicit prompt paths\n\tfor (const rawPath of promptPaths) {\n\t\tconst resolvedPath = resolvePath(rawPath, resolvedCwd, { trim: true });\n\t\tif (!existsSync(resolvedPath)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst stats = statSync(resolvedPath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\ttemplates.push(...loadTemplatesFromDir(resolvedPath, getSourceInfo));\n\t\t\t} else if (stats.isFile() && resolvedPath.endsWith(\".md\")) {\n\t\t\t\tconst template = loadTemplateFromFile(resolvedPath, getSourceInfo(resolvedPath));\n\t\t\t\tif (template) {\n\t\t\t\t\ttemplates.push(template);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore read failures\n\t\t}\n\t}\n\n\treturn templates;\n}\n\n/**\n * Expand a prompt template if it matches a template name.\n * Returns the expanded content or the original text if not a template.\n */\nexport function expandPromptTemplate(text: string, templates: PromptTemplate[]): string {\n\tif (!text.startsWith(\"/\")) return text;\n\n\tconst match = text.match(/^\\/([^\\s]+)(?:\\s+([\\s\\S]*))?$/);\n\tif (!match) return text;\n\n\tconst templateName = match[1];\n\tconst argsString = match[2] ?? \"\";\n\n\tconst template = templates.find((t) => t.name === templateName);\n\tif (template) {\n\t\tconst args = parseCommandArgs(argsString);\n\t\treturn substituteArgs(template.content, args);\n\t}\n\n\treturn text;\n}\n"]}
1
+ {"version":3,"file":"prompt-templates.js","sourceRoot":"","sources":["../../src/core/prompt-templates.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join, resolve, sep } from \"path\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { parseFrontmatter } from \"../utils/frontmatter.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport type { ResourceDiagnostic } from \"./diagnostics.ts\";\nimport { createSyntheticSourceInfo, type SourceInfo } from \"./source-info.ts\";\n\n/**\n * Represents a prompt template loaded from a markdown file\n */\nexport interface PromptTemplate {\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tcontent: string;\n\tsourceInfo: SourceInfo;\n\tfilePath: string; // Absolute path to the template file\n}\n\n/**\n * Parse command arguments respecting quoted strings (bash-style)\n * Returns array of arguments\n */\nexport function parseCommandArgs(argsString: string): string[] {\n\tconst args: string[] = [];\n\tlet current = \"\";\n\tlet inQuote: string | null = null;\n\n\tfor (let i = 0; i < argsString.length; i++) {\n\t\tconst char = argsString[i];\n\n\t\tif (inQuote) {\n\t\t\tif (char === inQuote) {\n\t\t\t\tinQuote = null;\n\t\t\t} else {\n\t\t\t\tcurrent += char;\n\t\t\t}\n\t\t} else if (char === '\"' || char === \"'\") {\n\t\t\tinQuote = char;\n\t\t} else if (/\\s/.test(char)) {\n\t\t\tif (current) {\n\t\t\t\targs.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t}\n\t}\n\n\tif (current) {\n\t\targs.push(current);\n\t}\n\n\treturn args;\n}\n\n/**\n * Substitute argument placeholders in template content\n * Supports:\n * - $1, $2, ... for positional args\n * - $@ and $ARGUMENTS for all args\n * - ${N:-default} for positional arg N with default when missing/empty\n * - ${@:-default} and ${ARGUMENTS:-default} for all args with a default when empty\n * - ${@:N} for args from Nth onwards (bash-style slicing)\n * - ${@:N:L} for L args starting from Nth\n *\n * Note: Replacement happens on the template string only. Argument and default values\n * containing patterns like $1, $@, or $ARGUMENTS are NOT recursively substituted.\n */\nexport function substituteArgs(content: string, args: string[]): string {\n\tconst allArgs = args.join(\" \");\n\n\treturn content.replace(\n\t\t/\\$\\{(\\d+|ARGUMENTS|@):-([^}]*)\\}|\\$\\{@:(\\d+)(?::(\\d+))?\\}|\\$(ARGUMENTS|@|\\d+)/g,\n\t\t(_match, defaultTarget, defaultValue, sliceStart, sliceLength, simple) => {\n\t\t\tif (defaultTarget) {\n\t\t\t\tconst value =\n\t\t\t\t\tdefaultTarget === \"@\" || defaultTarget === \"ARGUMENTS\" ? allArgs : args[parseInt(defaultTarget, 10) - 1];\n\t\t\t\treturn value ? value : defaultValue;\n\t\t\t}\n\n\t\t\tif (sliceStart) {\n\t\t\t\tlet start = parseInt(sliceStart, 10) - 1; // Convert to 0-indexed (user provides 1-indexed)\n\t\t\t\t// Treat 0 as 1 (bash convention: args start at 1)\n\t\t\t\tif (start < 0) start = 0;\n\n\t\t\t\tif (sliceLength) {\n\t\t\t\t\tconst length = parseInt(sliceLength, 10);\n\t\t\t\t\treturn args.slice(start, start + length).join(\" \");\n\t\t\t\t}\n\t\t\t\treturn args.slice(start).join(\" \");\n\t\t\t}\n\n\t\t\tif (simple === \"ARGUMENTS\" || simple === \"@\") {\n\t\t\t\treturn allArgs;\n\t\t\t}\n\n\t\t\tconst index = parseInt(simple, 10) - 1;\n\t\t\treturn args[index] ?? \"\";\n\t\t},\n\t);\n}\n\nfunction loadTemplateFromFile(\n\tfilePath: string,\n\tsourceInfo: SourceInfo,\n): { template: PromptTemplate | null; diagnostics: ResourceDiagnostic[] } {\n\tconst diagnostics: ResourceDiagnostic[] = [];\n\tlet rawContent: string;\n\ttry {\n\t\trawContent = readFileSync(filePath, \"utf-8\");\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : \"failed to read prompt template file\";\n\t\tdiagnostics.push({ type: \"warning\", message, path: filePath });\n\t\treturn { template: null, diagnostics };\n\t}\n\n\tlet frontmatter: Record<string, unknown>;\n\tlet body: string;\n\ttry {\n\t\t({ frontmatter, body } = parseFrontmatter(rawContent));\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : \"failed to parse prompt template file\";\n\t\tdiagnostics.push({ type: \"warning\", message, path: filePath });\n\t\treturn { template: null, diagnostics };\n\t}\n\n\tconst name = basename(filePath).replace(/\\.md$/, \"\");\n\n\t// Get description from frontmatter or first non-empty line\n\tlet description = typeof frontmatter.description === \"string\" ? frontmatter.description : \"\";\n\tif (!description) {\n\t\tconst firstLine = body.split(\"\\n\").find((line) => line.trim());\n\t\tif (firstLine) {\n\t\t\t// Truncate if too long\n\t\t\tdescription = firstLine.slice(0, 60);\n\t\t\tif (firstLine.length > 60) description += \"...\";\n\t\t}\n\t}\n\n\tconst argumentHint = typeof frontmatter[\"argument-hint\"] === \"string\" ? frontmatter[\"argument-hint\"] : undefined;\n\treturn {\n\t\ttemplate: {\n\t\t\tname,\n\t\t\tdescription,\n\t\t\t...(argumentHint && { argumentHint }),\n\t\t\tcontent: body,\n\t\t\tsourceInfo,\n\t\t\tfilePath,\n\t\t},\n\t\tdiagnostics,\n\t};\n}\n\n/**\n * Scan a directory for .md files (non-recursive) and load them as prompt templates.\n */\nfunction loadTemplatesFromDir(dir: string, getSourceInfo: (filePath: string) => SourceInfo): LoadPromptTemplatesResult {\n\tconst templates: PromptTemplate[] = [];\n\tconst diagnostics: ResourceDiagnostic[] = [];\n\n\tif (!existsSync(dir)) {\n\t\treturn { templates, diagnostics };\n\t}\n\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\t// For symlinks, check if they point to a file\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(fullPath);\n\t\t\t\t\tisFile = stats.isFile();\n\t\t\t\t} catch {\n\t\t\t\t\t// Broken symlink, skip it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isFile && entry.name.endsWith(\".md\")) {\n\t\t\t\tconst result = loadTemplateFromFile(fullPath, getSourceInfo(fullPath));\n\t\t\t\tif (result.template) {\n\t\t\t\t\ttemplates.push(result.template);\n\t\t\t\t}\n\t\t\t\tdiagnostics.push(...result.diagnostics);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn { templates, diagnostics };\n\t}\n\n\treturn { templates, diagnostics };\n}\n\nexport interface LoadPromptTemplatesOptions {\n\t/** Working directory for project-local templates. */\n\tcwd: string;\n\t/** Agent config directory for global templates. */\n\tagentDir: string;\n\t/** Explicit prompt template paths (files or directories). */\n\tpromptPaths: string[];\n\t/** Include default prompt directories. */\n\tincludeDefaults: boolean;\n}\n\nexport interface LoadPromptTemplatesResult {\n\ttemplates: PromptTemplate[];\n\tdiagnostics: ResourceDiagnostic[];\n}\n\n/**\n * Load all prompt templates from:\n * 1. Global: agentDir/prompts/\n * 2. Project: cwd/{CONFIG_DIR_NAME}/prompts/\n * 3. Explicit prompt paths\n */\nexport function loadPromptTemplates(options: LoadPromptTemplatesOptions): LoadPromptTemplatesResult {\n\tconst resolvedCwd = resolvePath(options.cwd);\n\tconst resolvedAgentDir = resolvePath(options.agentDir);\n\tconst promptPaths = options.promptPaths;\n\tconst includeDefaults = options.includeDefaults;\n\n\tconst templates: PromptTemplate[] = [];\n\tconst diagnostics: ResourceDiagnostic[] = [];\n\tconst addResult = (result: LoadPromptTemplatesResult): void => {\n\t\ttemplates.push(...result.templates);\n\t\tdiagnostics.push(...result.diagnostics);\n\t};\n\n\tconst globalPromptsDir = join(resolvedAgentDir, \"prompts\");\n\tconst projectPromptsDir = resolve(resolvedCwd, CONFIG_DIR_NAME, \"prompts\");\n\n\tconst isUnderPath = (target: string, root: string): boolean => {\n\t\tconst normalizedRoot = resolve(root);\n\t\tif (target === normalizedRoot) {\n\t\t\treturn true;\n\t\t}\n\t\tconst prefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;\n\t\treturn target.startsWith(prefix);\n\t};\n\n\tconst getSourceInfo = (resolvedPath: string): SourceInfo => {\n\t\tif (isUnderPath(resolvedPath, globalPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"user\",\n\t\t\t\tbaseDir: globalPromptsDir,\n\t\t\t});\n\t\t}\n\t\tif (isUnderPath(resolvedPath, projectPromptsDir)) {\n\t\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\t\tsource: \"local\",\n\t\t\t\tscope: \"project\",\n\t\t\t\tbaseDir: projectPromptsDir,\n\t\t\t});\n\t\t}\n\t\treturn createSyntheticSourceInfo(resolvedPath, {\n\t\t\tsource: \"local\",\n\t\t\tbaseDir: statSync(resolvedPath).isDirectory() ? resolvedPath : dirname(resolvedPath),\n\t\t});\n\t};\n\n\tif (includeDefaults) {\n\t\taddResult(loadTemplatesFromDir(globalPromptsDir, getSourceInfo));\n\t\taddResult(loadTemplatesFromDir(projectPromptsDir, getSourceInfo));\n\t}\n\n\t// 3. Load explicit prompt paths\n\tfor (const rawPath of promptPaths) {\n\t\tconst resolvedPath = resolvePath(rawPath, resolvedCwd, { trim: true });\n\t\tif (!existsSync(resolvedPath)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst stats = statSync(resolvedPath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\taddResult(loadTemplatesFromDir(resolvedPath, getSourceInfo));\n\t\t\t} else if (stats.isFile() && resolvedPath.endsWith(\".md\")) {\n\t\t\t\tconst result = loadTemplateFromFile(resolvedPath, getSourceInfo(resolvedPath));\n\t\t\t\tif (result.template) {\n\t\t\t\t\ttemplates.push(result.template);\n\t\t\t\t}\n\t\t\t\tdiagnostics.push(...result.diagnostics);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"failed to read prompt template path\";\n\t\t\tdiagnostics.push({ type: \"warning\", message, path: resolvedPath });\n\t\t}\n\t}\n\n\treturn { templates, diagnostics };\n}\n\n/**\n * Expand a prompt template if it matches a template name.\n * Returns the expanded content or the original text if not a template.\n */\nexport function expandPromptTemplate(text: string, templates: PromptTemplate[]): string {\n\tif (!text.startsWith(\"/\")) return text;\n\n\tconst match = text.match(/^\\/([^\\s]+)(?:\\s+([\\s\\S]*))?$/);\n\tif (!match) return text;\n\n\tconst templateName = match[1];\n\tconst argsString = match[2] ?? \"\";\n\n\tconst template = templates.find((t) => t.name === templateName);\n\tif (template) {\n\t\tconst args = parseCommandArgs(argsString);\n\t\treturn substituteArgs(template.content, args);\n\t}\n\n\treturn text;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { type Api, type AssistantMessageEventStream, type Context, type Model, type OAuthCredentials, type OAuthLoginCallbacks, type Provider, type ProviderHeaders, type RefreshModelsContext, type SimpleStreamOptions } from "@earendil-works/pi-ai";
1
+ import { type Api, type AssistantMessageEventStream, type Model, type OAuthCredentials, type OAuthLoginCallbacks, type Provider, type ProviderHeaders, type RefreshModelsContext, type SimpleStreamOptions, type TranscriptContext } from "@earendil-works/pi-ai";
2
2
  import type { ModelConfig, ModelsJsonProvider } from "./model-config.ts";
3
3
  import { clearConfigValueCache } from "./resolve-config-value.ts";
4
4
  export interface ExtensionOAuthConfig {
@@ -18,7 +18,7 @@ export interface ProviderConfigInput {
18
18
  baseUrl?: string;
19
19
  apiKey?: string;
20
20
  api?: Api;
21
- streamSimple?: (model: Model<Api>, context: Context, options?: SimpleStreamOptions) => AssistantMessageEventStream;
21
+ streamSimple?: (model: Model<Api>, context: TranscriptContext, options?: SimpleStreamOptions) => AssistantMessageEventStream;
22
22
  headers?: Record<string, string>;
23
23
  authHeader?: boolean;
24
24
  oauth?: ExtensionOAuthConfig;
@@ -30,7 +30,9 @@ export interface ProviderConfigInput {
30
30
  reasoning: boolean;
31
31
  thinkingLevelMap?: Model<Api>["thinkingLevelMap"];
32
32
  input: ("text" | "image")[];
33
+ inputLimits?: Model<Api>["inputLimits"];
33
34
  cost: Model<Api>["cost"];
35
+ promptCache?: Model<Api>["promptCache"];
34
36
  contextWindow: number;
35
37
  maxTokens: number;
36
38
  samplingParams?: Record<string, unknown>;