shortcutxl 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (423) hide show
  1. package/README.md +59 -0
  2. package/agent-docs/README.md +397 -0
  3. package/agent-docs/docs/compaction.md +390 -0
  4. package/agent-docs/docs/custom-provider.md +580 -0
  5. package/agent-docs/docs/development.md +69 -0
  6. package/agent-docs/docs/extensions.md +1971 -0
  7. package/agent-docs/docs/json.md +79 -0
  8. package/agent-docs/docs/keybindings.md +174 -0
  9. package/agent-docs/docs/models.md +293 -0
  10. package/agent-docs/docs/packages.md +209 -0
  11. package/agent-docs/docs/prompt-templates.md +67 -0
  12. package/agent-docs/docs/providers.md +186 -0
  13. package/agent-docs/docs/rpc.md +1317 -0
  14. package/agent-docs/docs/sdk.md +962 -0
  15. package/agent-docs/docs/session.md +412 -0
  16. package/agent-docs/docs/settings.md +223 -0
  17. package/agent-docs/docs/shell-aliases.md +13 -0
  18. package/agent-docs/docs/skills.md +231 -0
  19. package/agent-docs/docs/terminal-setup.md +70 -0
  20. package/agent-docs/docs/termux.md +127 -0
  21. package/agent-docs/docs/themes.md +295 -0
  22. package/agent-docs/docs/tree.md +219 -0
  23. package/agent-docs/docs/tui.md +887 -0
  24. package/agent-docs/docs/windows.md +17 -0
  25. package/agent-docs/examples/README.md +25 -0
  26. package/agent-docs/examples/extensions/README.md +205 -0
  27. package/agent-docs/examples/extensions/antigravity-image-gen.ts +447 -0
  28. package/agent-docs/examples/extensions/auto-commit-on-exit.ts +49 -0
  29. package/agent-docs/examples/extensions/bash-spawn-hook.ts +30 -0
  30. package/agent-docs/examples/extensions/bookmark.ts +50 -0
  31. package/agent-docs/examples/extensions/built-in-tool-renderer.ts +256 -0
  32. package/agent-docs/examples/extensions/claude-rules.ts +86 -0
  33. package/agent-docs/examples/extensions/commands.ts +75 -0
  34. package/agent-docs/examples/extensions/confirm-destructive.ts +59 -0
  35. package/agent-docs/examples/extensions/custom-compaction.ts +126 -0
  36. package/agent-docs/examples/extensions/custom-footer.ts +63 -0
  37. package/agent-docs/examples/extensions/custom-header.ts +73 -0
  38. package/agent-docs/examples/extensions/custom-provider-anthropic/index.ts +660 -0
  39. package/agent-docs/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
  40. package/agent-docs/examples/extensions/custom-provider-anthropic/package.json +19 -0
  41. package/agent-docs/examples/extensions/custom-provider-gitlab-duo/index.ts +362 -0
  42. package/agent-docs/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
  43. package/agent-docs/examples/extensions/custom-provider-gitlab-duo/test.ts +88 -0
  44. package/agent-docs/examples/extensions/custom-provider-qwen-cli/index.ts +349 -0
  45. package/agent-docs/examples/extensions/custom-provider-qwen-cli/package.json +16 -0
  46. package/agent-docs/examples/extensions/dirty-repo-guard.ts +56 -0
  47. package/agent-docs/examples/extensions/doom-overlay/README.md +46 -0
  48. package/agent-docs/examples/extensions/doom-overlay/doom/build.sh +152 -0
  49. package/agent-docs/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
  50. package/agent-docs/examples/extensions/doom-overlay/doom-component.ts +133 -0
  51. package/agent-docs/examples/extensions/doom-overlay/doom-engine.ts +186 -0
  52. package/agent-docs/examples/extensions/doom-overlay/doom-keys.ts +108 -0
  53. package/agent-docs/examples/extensions/doom-overlay/index.ts +74 -0
  54. package/agent-docs/examples/extensions/doom-overlay/wad-finder.ts +51 -0
  55. package/agent-docs/examples/extensions/dynamic-resources/SKILL.md +8 -0
  56. package/agent-docs/examples/extensions/dynamic-resources/dynamic.json +79 -0
  57. package/agent-docs/examples/extensions/dynamic-resources/dynamic.md +5 -0
  58. package/agent-docs/examples/extensions/dynamic-resources/index.ts +15 -0
  59. package/agent-docs/examples/extensions/dynamic-tools.ts +77 -0
  60. package/agent-docs/examples/extensions/event-bus.ts +43 -0
  61. package/agent-docs/examples/extensions/file-trigger.ts +41 -0
  62. package/agent-docs/examples/extensions/git-checkpoint.ts +53 -0
  63. package/agent-docs/examples/extensions/handoff.ts +155 -0
  64. package/agent-docs/examples/extensions/hello.ts +25 -0
  65. package/agent-docs/examples/extensions/inline-bash.ts +94 -0
  66. package/agent-docs/examples/extensions/input-transform.ts +43 -0
  67. package/agent-docs/examples/extensions/interactive-shell.ts +209 -0
  68. package/agent-docs/examples/extensions/mac-system-theme.ts +47 -0
  69. package/agent-docs/examples/extensions/message-renderer.ts +59 -0
  70. package/agent-docs/examples/extensions/minimal-mode.ts +430 -0
  71. package/agent-docs/examples/extensions/modal-editor.ts +90 -0
  72. package/agent-docs/examples/extensions/model-status.ts +31 -0
  73. package/agent-docs/examples/extensions/notify.ts +55 -0
  74. package/agent-docs/examples/extensions/overlay-qa-tests.ts +936 -0
  75. package/agent-docs/examples/extensions/overlay-test.ts +159 -0
  76. package/agent-docs/examples/extensions/permission-gate.ts +37 -0
  77. package/agent-docs/examples/extensions/pirate.ts +47 -0
  78. package/agent-docs/examples/extensions/plan-mode/README.md +65 -0
  79. package/agent-docs/examples/extensions/plan-mode/index.ts +363 -0
  80. package/agent-docs/examples/extensions/plan-mode/utils.ts +173 -0
  81. package/agent-docs/examples/extensions/preset.ts +418 -0
  82. package/agent-docs/examples/extensions/protected-paths.ts +30 -0
  83. package/agent-docs/examples/extensions/qna.ts +122 -0
  84. package/agent-docs/examples/extensions/question.ts +278 -0
  85. package/agent-docs/examples/extensions/questionnaire.ts +440 -0
  86. package/agent-docs/examples/extensions/rainbow-editor.ts +90 -0
  87. package/agent-docs/examples/extensions/reload-runtime.ts +37 -0
  88. package/agent-docs/examples/extensions/rpc-demo.ts +124 -0
  89. package/agent-docs/examples/extensions/sandbox/index.ts +324 -0
  90. package/agent-docs/examples/extensions/sandbox/package-lock.json +92 -0
  91. package/agent-docs/examples/extensions/sandbox/package.json +19 -0
  92. package/agent-docs/examples/extensions/send-user-message.ts +97 -0
  93. package/agent-docs/examples/extensions/session-name.ts +27 -0
  94. package/agent-docs/examples/extensions/shutdown-command.ts +69 -0
  95. package/agent-docs/examples/extensions/snake.ts +343 -0
  96. package/agent-docs/examples/extensions/space-invaders.ts +566 -0
  97. package/agent-docs/examples/extensions/ssh.ts +233 -0
  98. package/agent-docs/examples/extensions/status-line.ts +40 -0
  99. package/agent-docs/examples/extensions/subagent/README.md +172 -0
  100. package/agent-docs/examples/extensions/subagent/agents/planner.md +37 -0
  101. package/agent-docs/examples/extensions/subagent/agents/reviewer.md +35 -0
  102. package/agent-docs/examples/extensions/subagent/agents/scout.md +50 -0
  103. package/agent-docs/examples/extensions/subagent/agents/worker.md +24 -0
  104. package/agent-docs/examples/extensions/subagent/agents.ts +130 -0
  105. package/agent-docs/examples/extensions/subagent/index.ts +1068 -0
  106. package/agent-docs/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
  107. package/agent-docs/examples/extensions/subagent/prompts/implement.md +10 -0
  108. package/agent-docs/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
  109. package/agent-docs/examples/extensions/summarize.ts +206 -0
  110. package/agent-docs/examples/extensions/system-prompt-header.ts +17 -0
  111. package/agent-docs/examples/extensions/timed-confirm.ts +72 -0
  112. package/agent-docs/examples/extensions/titlebar-spinner.ts +58 -0
  113. package/agent-docs/examples/extensions/todo.ts +314 -0
  114. package/agent-docs/examples/extensions/tool-override.ts +146 -0
  115. package/agent-docs/examples/extensions/tools.ts +145 -0
  116. package/agent-docs/examples/extensions/trigger-compact.ts +40 -0
  117. package/agent-docs/examples/extensions/truncated-tool.ts +194 -0
  118. package/agent-docs/examples/extensions/widget-placement.ts +17 -0
  119. package/agent-docs/examples/extensions/with-deps/index.ts +37 -0
  120. package/agent-docs/examples/extensions/with-deps/package-lock.json +31 -0
  121. package/agent-docs/examples/extensions/with-deps/package.json +22 -0
  122. package/agent-docs/examples/rpc-extension-ui.ts +654 -0
  123. package/agent-docs/examples/sdk/01-minimal.ts +22 -0
  124. package/agent-docs/examples/sdk/02-custom-model.ts +48 -0
  125. package/agent-docs/examples/sdk/03-custom-prompt.ts +55 -0
  126. package/agent-docs/examples/sdk/04-skills.ts +53 -0
  127. package/agent-docs/examples/sdk/05-tools.ts +56 -0
  128. package/agent-docs/examples/sdk/06-extensions.ts +88 -0
  129. package/agent-docs/examples/sdk/07-context-files.ts +40 -0
  130. package/agent-docs/examples/sdk/08-prompt-templates.ts +47 -0
  131. package/agent-docs/examples/sdk/09-api-keys-and-oauth.ts +48 -0
  132. package/agent-docs/examples/sdk/10-settings.ts +54 -0
  133. package/agent-docs/examples/sdk/11-sessions.ts +48 -0
  134. package/agent-docs/examples/sdk/12-full-control.ts +82 -0
  135. package/agent-docs/examples/sdk/README.md +144 -0
  136. package/agent-docs/xll-skill.md +61 -0
  137. package/agent-docs/xll-spec.md +110 -0
  138. package/dist/cli/args.js +290 -0
  139. package/dist/cli/config-selector.js +31 -0
  140. package/dist/cli/file-processor.js +79 -0
  141. package/dist/cli/list-models.js +92 -0
  142. package/dist/cli/package-commands.js +210 -0
  143. package/dist/cli/report-settings-errors.js +11 -0
  144. package/dist/cli/session-picker.js +34 -0
  145. package/dist/cli.js +19 -0
  146. package/dist/config.js +288 -0
  147. package/dist/core/abort.js +15 -0
  148. package/dist/core/agent-loop.js +352 -0
  149. package/dist/core/agent-session.js +2019 -0
  150. package/dist/core/agent.js +410 -0
  151. package/dist/core/auth-storage.js +456 -0
  152. package/dist/core/bash-executor.js +222 -0
  153. package/dist/core/compaction/branch-summarization.js +242 -0
  154. package/dist/core/compaction/compaction.js +610 -0
  155. package/dist/core/compaction/index.js +7 -0
  156. package/dist/core/compaction/utils.js +139 -0
  157. package/dist/core/defaults.js +6 -0
  158. package/dist/core/diagnostics.js +2 -0
  159. package/dist/core/event-bus.js +25 -0
  160. package/dist/core/exec.js +71 -0
  161. package/dist/core/export-html/ansi-to-html.js +256 -0
  162. package/dist/core/export-html/index.js +238 -0
  163. package/dist/core/export-html/session-view-model.js +342 -0
  164. package/dist/core/export-html/template.css +1110 -0
  165. package/dist/core/export-html/template.html +76 -0
  166. package/dist/core/export-html/template.js +1990 -0
  167. package/dist/core/export-html/tool-renderer.js +63 -0
  168. package/dist/core/export-html/vendor/highlight.min.js +7725 -0
  169. package/dist/core/export-html/vendor/marked.min.js +1803 -0
  170. package/dist/core/extensions/index.js +9 -0
  171. package/dist/core/extensions/loader.js +422 -0
  172. package/dist/core/extensions/runner.js +651 -0
  173. package/dist/core/extensions/types.js +35 -0
  174. package/dist/core/extensions/wrapper.js +102 -0
  175. package/dist/core/footer-data-provider.js +162 -0
  176. package/dist/core/index.js +9 -0
  177. package/dist/core/keybindings.js +153 -0
  178. package/dist/core/messages.js +133 -0
  179. package/dist/core/model-registry.js +539 -0
  180. package/dist/core/model-resolver.js +370 -0
  181. package/dist/core/package-manager.js +1485 -0
  182. package/dist/core/prompt-templates.js +253 -0
  183. package/dist/core/resolve-config-value.js +59 -0
  184. package/dist/core/resource-loader.js +700 -0
  185. package/dist/core/sdk.js +197 -0
  186. package/dist/core/session-bash.js +99 -0
  187. package/dist/core/session-compaction.js +165 -0
  188. package/dist/core/session-manager.js +1153 -0
  189. package/dist/core/session-models.js +99 -0
  190. package/dist/core/session-retry.js +155 -0
  191. package/dist/core/settings-manager.js +572 -0
  192. package/dist/core/skills.js +382 -0
  193. package/dist/core/slash-commands.js +31 -0
  194. package/dist/core/system-prompt.js +161 -0
  195. package/dist/core/theme.js +770 -0
  196. package/dist/core/timings.js +26 -0
  197. package/dist/core/tools/bash.js +258 -0
  198. package/dist/core/tools/edit-diff.js +245 -0
  199. package/dist/core/tools/edit.js +148 -0
  200. package/dist/core/tools/find.js +208 -0
  201. package/dist/core/tools/grep.js +246 -0
  202. package/dist/core/tools/index.js +67 -0
  203. package/dist/core/tools/ls.js +123 -0
  204. package/dist/core/tools/path-utils.js +81 -0
  205. package/dist/core/tools/read.js +160 -0
  206. package/dist/core/tools/truncate.js +70 -0
  207. package/dist/core/tools/write.js +82 -0
  208. package/dist/custom/agents/action.js +13 -0
  209. package/dist/custom/agents/document-reader.js +70 -0
  210. package/dist/custom/agents/general.js +26 -0
  211. package/dist/custom/agents/index.js +49 -0
  212. package/dist/custom/agents/installation.js +13 -0
  213. package/dist/custom/agents/types.js +7 -0
  214. package/dist/custom/auth/refresh-timer.js +33 -0
  215. package/dist/custom/auth/shortcut-oauth.js +145 -0
  216. package/dist/custom/constants.js +21 -0
  217. package/dist/custom/context/workbook-summary.js +73 -0
  218. package/dist/custom/credits/shortcut-credits.js +29 -0
  219. package/dist/custom/cron/cron-daemon-entry.js +18 -0
  220. package/dist/custom/cron/daemon-ipc.js +131 -0
  221. package/dist/custom/cron/daemon.js +224 -0
  222. package/dist/custom/cron/jobs.js +226 -0
  223. package/dist/custom/cron/run-log.js +51 -0
  224. package/dist/custom/cron/schedule.js +72 -0
  225. package/dist/custom/cron/status-line.js +98 -0
  226. package/dist/custom/cron/store.js +87 -0
  227. package/dist/custom/cron/types.js +8 -0
  228. package/dist/custom/dev/index.js +59 -0
  229. package/dist/custom/dev/trace-export.js +58 -0
  230. package/dist/custom/ensure-excel.js +63 -0
  231. package/dist/custom/excel-config.js +36 -0
  232. package/dist/custom/preflight.js +422 -0
  233. package/dist/custom/prompts/action.js +100 -0
  234. package/dist/custom/prompts/api.js +66 -0
  235. package/dist/custom/prompts/installation.js +124 -0
  236. package/dist/custom/prompts/shared.js +138 -0
  237. package/dist/custom/providers/llm-usage.js +42 -0
  238. package/dist/custom/providers/message-converter.js +74 -0
  239. package/dist/custom/providers/provider-ids.js +9 -0
  240. package/dist/custom/providers/register-openai-codex-provider.js +27 -0
  241. package/dist/custom/providers/register-shortcut-provider.js +52 -0
  242. package/dist/custom/providers/shortcut-invoke.js +117 -0
  243. package/dist/custom/providers/shortcut-stream.js +252 -0
  244. package/dist/custom/providers/sse-protocol.js +38 -0
  245. package/dist/custom/sync-xll.js +130 -0
  246. package/dist/custom/tools/cron.js +413 -0
  247. package/dist/custom/tools/excel-exec.js +167 -0
  248. package/dist/custom/tools/excel-range.js +50 -0
  249. package/dist/custom/tools/llm-analysis.js +265 -0
  250. package/dist/custom/tools/render-helpers.js +38 -0
  251. package/dist/custom/tools/switch-mode.js +94 -0
  252. package/dist/custom/tools/task/agents.js +6 -0
  253. package/dist/custom/tools/task/index.js +8 -0
  254. package/dist/custom/tools/task/render.js +348 -0
  255. package/dist/custom/tools/task/subprocess.js +320 -0
  256. package/dist/custom/tools/task/task.js +205 -0
  257. package/dist/custom/tools/todo-list.js +195 -0
  258. package/dist/custom/tracing/session-upload.js +93 -0
  259. package/dist/index.js +45 -0
  260. package/dist/main.js +613 -0
  261. package/dist/migrations.js +265 -0
  262. package/dist/modes/index.js +8 -0
  263. package/dist/modes/interactive/components/armin.js +337 -0
  264. package/dist/modes/interactive/components/assistant-message.js +94 -0
  265. package/dist/modes/interactive/components/bash-execution.js +171 -0
  266. package/dist/modes/interactive/components/bordered-loader.js +51 -0
  267. package/dist/modes/interactive/components/branch-summary-message.js +45 -0
  268. package/dist/modes/interactive/components/compaction-summary-message.js +46 -0
  269. package/dist/modes/interactive/components/config-selector.js +488 -0
  270. package/dist/modes/interactive/components/countdown-timer.js +33 -0
  271. package/dist/modes/interactive/components/custom-editor.js +93 -0
  272. package/dist/modes/interactive/components/custom-message.js +81 -0
  273. package/dist/modes/interactive/components/daxnuts.js +140 -0
  274. package/dist/modes/interactive/components/diff.js +133 -0
  275. package/dist/modes/interactive/components/dynamic-border.js +21 -0
  276. package/dist/modes/interactive/components/extension-editor.js +105 -0
  277. package/dist/modes/interactive/components/extension-input.js +61 -0
  278. package/dist/modes/interactive/components/extension-selector.js +78 -0
  279. package/dist/modes/interactive/components/footer.js +309 -0
  280. package/dist/modes/interactive/components/index.js +33 -0
  281. package/dist/modes/interactive/components/keybinding-hints.js +61 -0
  282. package/dist/modes/interactive/components/layout.js +64 -0
  283. package/dist/modes/interactive/components/login-dialog.js +148 -0
  284. package/dist/modes/interactive/components/model-selector.js +237 -0
  285. package/dist/modes/interactive/components/oauth-selector.js +111 -0
  286. package/dist/modes/interactive/components/session-selector-search.js +157 -0
  287. package/dist/modes/interactive/components/session-selector.js +860 -0
  288. package/dist/modes/interactive/components/settings-selector.js +123 -0
  289. package/dist/modes/interactive/components/show-images-selector.js +35 -0
  290. package/dist/modes/interactive/components/skill-invocation-message.js +48 -0
  291. package/dist/modes/interactive/components/theme-selector.js +47 -0
  292. package/dist/modes/interactive/components/thinking-selector.js +47 -0
  293. package/dist/modes/interactive/components/tool-execution.js +789 -0
  294. package/dist/modes/interactive/components/tool-group.js +106 -0
  295. package/dist/modes/interactive/components/tree-selector.js +962 -0
  296. package/dist/modes/interactive/components/user-message-selector.js +115 -0
  297. package/dist/modes/interactive/components/user-message.js +48 -0
  298. package/dist/modes/interactive/components/visual-truncate.js +33 -0
  299. package/dist/modes/interactive/file-attachments.js +135 -0
  300. package/dist/modes/interactive/interactive-mode.js +3775 -0
  301. package/dist/modes/interactive/theme/dark.json +85 -0
  302. package/dist/modes/interactive/theme/light.json +85 -0
  303. package/dist/modes/interactive/theme/theme-schema.json +335 -0
  304. package/dist/modes/interactive/theme/theme.js +177 -0
  305. package/dist/modes/print-mode.js +101 -0
  306. package/dist/modes/rpc/rpc-client.js +387 -0
  307. package/dist/modes/rpc/rpc-mode.js +509 -0
  308. package/dist/modes/rpc/rpc-types.js +8 -0
  309. package/dist/subagent-entry.js +145 -0
  310. package/dist/tool-names.js +34 -0
  311. package/dist/tui/autocomplete.js +596 -0
  312. package/dist/tui/components/box.js +104 -0
  313. package/dist/tui/components/cancellable-loader.js +35 -0
  314. package/dist/tui/components/editor.js +1679 -0
  315. package/dist/tui/components/image.js +69 -0
  316. package/dist/tui/components/input.js +433 -0
  317. package/dist/tui/components/loader.js +49 -0
  318. package/dist/tui/components/markdown.js +629 -0
  319. package/dist/tui/components/select-list.js +152 -0
  320. package/dist/tui/components/settings-list.js +185 -0
  321. package/dist/tui/components/spacer.js +23 -0
  322. package/dist/tui/components/text.js +89 -0
  323. package/dist/tui/components/truncated-text.js +51 -0
  324. package/dist/tui/editor-component.js +2 -0
  325. package/dist/tui/fuzzy.js +107 -0
  326. package/dist/tui/get-east-asian-width/index.js +32 -0
  327. package/dist/tui/get-east-asian-width/lookup.js +404 -0
  328. package/dist/tui/index.js +32 -0
  329. package/dist/tui/keybindings.js +114 -0
  330. package/dist/tui/keys.js +959 -0
  331. package/dist/tui/kill-ring.js +44 -0
  332. package/dist/tui/stdin-buffer.js +317 -0
  333. package/dist/tui/terminal-image.js +288 -0
  334. package/dist/tui/terminal.js +249 -0
  335. package/dist/tui/tui/autocomplete.js +596 -0
  336. package/dist/tui/tui/components/box.js +106 -0
  337. package/dist/tui/tui/components/cancellable-loader.js +35 -0
  338. package/dist/tui/tui/components/editor.js +1679 -0
  339. package/dist/tui/tui/components/image.js +69 -0
  340. package/dist/tui/tui/components/input.js +433 -0
  341. package/dist/tui/tui/components/loader.js +49 -0
  342. package/dist/tui/tui/components/markdown.js +629 -0
  343. package/dist/tui/tui/components/select-list.js +152 -0
  344. package/dist/tui/tui/components/settings-list.js +185 -0
  345. package/dist/tui/tui/components/spacer.js +23 -0
  346. package/dist/tui/tui/components/text.js +91 -0
  347. package/dist/tui/tui/components/truncated-text.js +51 -0
  348. package/dist/tui/tui/editor-component.js +2 -0
  349. package/dist/tui/tui/fuzzy.js +107 -0
  350. package/dist/tui/tui/get-east-asian-width/index.js +32 -0
  351. package/dist/tui/tui/get-east-asian-width/lookup.js +404 -0
  352. package/dist/tui/tui/index.js +32 -0
  353. package/dist/tui/tui/keybindings.js +114 -0
  354. package/dist/tui/tui/keys.js +959 -0
  355. package/dist/tui/tui/kill-ring.js +44 -0
  356. package/dist/tui/tui/stdin-buffer.js +317 -0
  357. package/dist/tui/tui/terminal-image.js +288 -0
  358. package/dist/tui/tui/terminal.js +249 -0
  359. package/dist/tui/tui/tui.js +955 -0
  360. package/dist/tui/tui/undo-stack.js +25 -0
  361. package/dist/tui/tui/utils.js +800 -0
  362. package/dist/tui/tui.js +955 -0
  363. package/dist/tui/undo-stack.js +25 -0
  364. package/dist/tui/utils.js +800 -0
  365. package/dist/utils/changelog.js +87 -0
  366. package/dist/utils/clipboard-image.js +164 -0
  367. package/dist/utils/clipboard-native.js +14 -0
  368. package/dist/utils/clipboard.js +67 -0
  369. package/dist/utils/frontmatter.js +26 -0
  370. package/dist/utils/git.js +166 -0
  371. package/dist/utils/image-convert.js +35 -0
  372. package/dist/utils/image-resize.js +183 -0
  373. package/dist/utils/mime.js +26 -0
  374. package/dist/utils/photon.js +121 -0
  375. package/dist/utils/shell.js +217 -0
  376. package/dist/utils/sleep.js +17 -0
  377. package/dist/utils/tools-manager.js +259 -0
  378. package/package.json +78 -0
  379. package/skills/excel-com-api/SKILL.md +74 -0
  380. package/skills/excel-com-api/excel-type-library.py +27767 -0
  381. package/skills/excel-com-api/office-type-library.py +10867 -0
  382. package/skills/integrations/SKILL.md +138 -0
  383. package/skills/integrations/alphasense.md +457 -0
  384. package/skills/integrations/bloomberg.md +803 -0
  385. package/skills/integrations/calcbench.md +315 -0
  386. package/skills/integrations/capiq.md +848 -0
  387. package/skills/integrations/dynamics-365-finance.md +354 -0
  388. package/skills/integrations/earnings_transcripts.md +387 -0
  389. package/skills/integrations/factset.md +758 -0
  390. package/skills/integrations/ice-fixed-income.md +344 -0
  391. package/skills/integrations/moodys-analytics.md +313 -0
  392. package/skills/integrations/morningstar.md +433 -0
  393. package/skills/integrations/nasdaq-data-link.md +249 -0
  394. package/skills/integrations/pitchbook.md +413 -0
  395. package/skills/integrations/preqin.md +422 -0
  396. package/skills/integrations/quickbooks.md +289 -0
  397. package/skills/integrations/quickfs.md +314 -0
  398. package/skills/integrations/refinitiv.md +473 -0
  399. package/skills/integrations/sage-intacct.md +401 -0
  400. package/skills/integrations/visible-alpha.md +320 -0
  401. package/skills/integrations/xero.md +393 -0
  402. package/skills/integrations/ycharts.md +306 -0
  403. package/skills/pdf-creation/SKILL.md +93 -0
  404. package/skills/pdf-extraction/SKILL.md +32 -0
  405. package/skills/powerpoint-creation/SKILL.md +110 -0
  406. package/skills/sec-edgar/SKILL.md +127 -0
  407. package/skills/sec-edgar/sec_to_pdf.py +109 -0
  408. package/xll/ShortcutXL.xll +0 -0
  409. package/xll/modules/debug_render.py +272 -0
  410. package/xll/modules/gameboy.py +241 -0
  411. package/xll/modules/pong.py +188 -0
  412. package/xll/modules/shortcut_xl/__init__.py +18 -0
  413. package/xll/modules/shortcut_xl/_categorize.py +200 -0
  414. package/xll/modules/shortcut_xl/_com.py +108 -0
  415. package/xll/modules/shortcut_xl/_format.py +252 -0
  416. package/xll/modules/shortcut_xl/_log.py +12 -0
  417. package/xll/modules/shortcut_xl/_managed.py +116 -0
  418. package/xll/modules/shortcut_xl/_registry.py +44 -0
  419. package/xll/modules/shortcut_xl/_threading.py +161 -0
  420. package/xll/modules/shortcut_xl/_tracking.py +283 -0
  421. package/xll/modules/stocks.py +100 -0
  422. package/xll/python3.dll +0 -0
  423. package/xll/python312.dll +0 -0
@@ -0,0 +1,413 @@
1
+ /**
2
+ * Cron tool — manage scheduled jobs via the agent.
3
+ *
4
+ * Actions: status, list, add, update, remove, run, history, daemon_start, daemon_stop.
5
+ * All actions read/write jobs.json directly (no daemon needed for CRUD).
6
+ */
7
+ import { Type } from '@sinclair/typebox';
8
+ import { CRON } from '../../tool-names.js';
9
+ import { getDaemonStatus, isDaemonRunning, startDaemon, stopDaemon } from '../cron/daemon-ipc.js';
10
+ import { applyJobPatch, createJob, normalizeJobState } from '../cron/jobs.js';
11
+ import { readRunLog } from '../cron/run-log.js';
12
+ import { computeNextRunAtMs } from '../cron/schedule.js';
13
+ import { getCronStorePath, loadCronStore, saveCronStore } from '../cron/store.js';
14
+ // ---------------------------------------------------------------------------
15
+ // Schema
16
+ // ---------------------------------------------------------------------------
17
+ const ScheduleSchema = Type.Union([
18
+ Type.Object({
19
+ kind: Type.Literal('at'),
20
+ at: Type.String({ description: 'ISO-8601 timestamp, e.g. "2026-03-06T17:00:00Z"' })
21
+ }),
22
+ Type.Object({
23
+ kind: Type.Literal('every'),
24
+ everyMs: Type.Number({ description: 'Interval in milliseconds, e.g. 1800000 for 30 minutes' })
25
+ }),
26
+ Type.Object({
27
+ kind: Type.Literal('cron'),
28
+ expr: Type.String({ description: 'Cron expression, e.g. "0 8 * * *" for daily at 8am' }),
29
+ tz: Type.Optional(Type.String({
30
+ description: 'IANA timezone, e.g. "America/New_York". Defaults to system timezone.'
31
+ }))
32
+ })
33
+ ]);
34
+ const JobInputSchema = Type.Object({
35
+ name: Type.String({ description: 'Short name for the job' }),
36
+ schedule: ScheduleSchema,
37
+ payload: Type.Object({
38
+ message: Type.String({ description: 'The prompt/instruction for the agent to execute' }),
39
+ model: Type.Optional(Type.String({ description: 'Model override, e.g. "shortcut/claude-opus-4-6"' }))
40
+ }),
41
+ enabled: Type.Optional(Type.Boolean({ description: 'Whether the job is enabled (default: true)' })),
42
+ description: Type.Optional(Type.String({ description: 'Longer description of what the job does' }))
43
+ });
44
+ const PatchSchema = Type.Object({
45
+ name: Type.Optional(Type.String()),
46
+ description: Type.Optional(Type.String()),
47
+ enabled: Type.Optional(Type.Boolean()),
48
+ schedule: Type.Optional(ScheduleSchema),
49
+ payload: Type.Optional(Type.Object({
50
+ message: Type.Optional(Type.String()),
51
+ model: Type.Optional(Type.String())
52
+ }))
53
+ });
54
+ const schema = Type.Object({
55
+ action: Type.Union([
56
+ Type.Literal('list'),
57
+ Type.Literal('add'),
58
+ Type.Literal('update'),
59
+ Type.Literal('remove'),
60
+ Type.Literal('run'),
61
+ Type.Literal('status'),
62
+ Type.Literal('history'),
63
+ Type.Literal('daemon_start'),
64
+ Type.Literal('daemon_stop')
65
+ ], { description: 'The action to perform' }),
66
+ job: Type.Optional(JobInputSchema),
67
+ jobId: Type.Optional(Type.String({ description: 'Job ID for update/remove/run' })),
68
+ patch: Type.Optional(PatchSchema),
69
+ includeDisabled: Type.Optional(Type.Boolean({ description: 'Include disabled jobs in list (default: false)' })),
70
+ limit: Type.Optional(Type.Number({ description: 'Max entries to return for history (default: 5)' }))
71
+ });
72
+ // ---------------------------------------------------------------------------
73
+ // Helpers
74
+ // ---------------------------------------------------------------------------
75
+ function formatSchedule(schedule) {
76
+ if (schedule.kind === 'at')
77
+ return `at ${schedule.at}`;
78
+ if (schedule.kind === 'every')
79
+ return `every ${schedule.everyMs}ms`;
80
+ return `cron "${schedule.expr}"${schedule.tz ? ` (${schedule.tz})` : ''}`;
81
+ }
82
+ function formatJob(job) {
83
+ const status = job.enabled ? 'enabled' : 'disabled';
84
+ const schedule = formatSchedule(job.schedule);
85
+ const next = job.state.nextRunAtMs ? new Date(job.state.nextRunAtMs).toISOString() : 'none';
86
+ const lastStatus = job.state.lastRunStatus ?? 'never run';
87
+ const lastError = job.state.lastError ? ` — ${job.state.lastError}` : '';
88
+ return [
89
+ ` id: ${job.id}`,
90
+ ` name: ${job.name}`,
91
+ job.description ? ` description: ${job.description}` : null,
92
+ ` status: ${status}`,
93
+ ` schedule: ${schedule}`,
94
+ ` next run: ${next}`,
95
+ ` last: ${lastStatus}${lastError}`,
96
+ ` payload: ${job.payload.message}`
97
+ ]
98
+ .filter(Boolean)
99
+ .join('\n');
100
+ }
101
+ /** Try to recover flat params that weaker models produce (e.g. top-level name/schedule instead of nested job). */
102
+ function recoverFlatParams(params) {
103
+ const p = { ...params };
104
+ if (p.action === 'add' && !p.job && typeof p.name === 'string') {
105
+ p.job = {
106
+ name: p.name,
107
+ schedule: p.schedule,
108
+ payload: (p.payload ?? { message: p.message }),
109
+ enabled: p.enabled,
110
+ description: p.description
111
+ };
112
+ }
113
+ if (p.action === 'update' && !p.jobId && typeof p.id === 'string') {
114
+ p.jobId = p.id;
115
+ // Recover flat patch fields
116
+ if (!p.patch) {
117
+ const patch = {};
118
+ if (typeof p.name === 'string')
119
+ patch.name = p.name;
120
+ if (typeof p.description === 'string')
121
+ patch.description = p.description;
122
+ if (typeof p.enabled === 'boolean')
123
+ patch.enabled = p.enabled;
124
+ if (p.schedule)
125
+ patch.schedule = p.schedule;
126
+ if (Object.keys(patch).length > 0)
127
+ p.patch = patch;
128
+ }
129
+ }
130
+ if ((p.action === 'remove' || p.action === 'run') && !p.jobId && typeof p.id === 'string') {
131
+ p.jobId = p.id;
132
+ }
133
+ return p;
134
+ }
135
+ // ---------------------------------------------------------------------------
136
+ // Action handlers
137
+ // ---------------------------------------------------------------------------
138
+ async function handleStatus(storePath) {
139
+ const store = await loadCronStore(storePath);
140
+ const now = Date.now();
141
+ const enabled = store.jobs.filter((j) => j.enabled);
142
+ const nextRun = enabled
143
+ .map((j) => j.state.nextRunAtMs)
144
+ .filter((ms) => typeof ms === 'number')
145
+ .sort((a, b) => a - b)[0];
146
+ const running = store.jobs.filter((j) => typeof j.state.runningAtMs === 'number');
147
+ const daemon = getDaemonStatus();
148
+ const lines = [
149
+ `Daemon: ${daemon.running ? `running (pid ${daemon.pid})` : 'stopped'}`,
150
+ `Jobs: ${enabled.length} enabled, ${store.jobs.length - enabled.length} disabled`
151
+ ];
152
+ if (nextRun) {
153
+ const delta = Math.max(0, nextRun - now);
154
+ const mins = Math.round(delta / 60_000);
155
+ lines.push(`Next run: ${new Date(nextRun).toISOString()} (in ~${mins}m)`);
156
+ }
157
+ if (running.length > 0) {
158
+ lines.push(`Running: ${running.length} job(s)`);
159
+ }
160
+ if (!daemon.running && enabled.length > 0) {
161
+ lines.push('\nNote: daemon is not running. Use daemon_start to start it.');
162
+ }
163
+ return lines.join('\n');
164
+ }
165
+ async function handleHistory(storePath, jobId, limit) {
166
+ // Resolve job name for display
167
+ const store = await loadCronStore(storePath);
168
+ const job = store.jobs.find((j) => j.id === jobId);
169
+ const jobLabel = job ? `"${job.name}" (${jobId})` : jobId;
170
+ const entries = await readRunLog(jobId, limit);
171
+ if (entries.length === 0) {
172
+ return `No run history for ${jobLabel}.`;
173
+ }
174
+ const lines = [`Run history for ${jobLabel} (${entries.length} most recent):\n`];
175
+ for (const entry of entries) {
176
+ const date = new Date(entry.startedAtMs).toISOString();
177
+ const duration = `${entry.durationMs}ms`;
178
+ const status = entry.status === 'ok' ? '✓' : '✗';
179
+ lines.push(`${status} ${date} — ${entry.status} (${duration})`);
180
+ if (entry.error) {
181
+ lines.push(` error: ${entry.error}`);
182
+ }
183
+ if (entry.output) {
184
+ // Show first ~500 chars of output inline
185
+ const preview = entry.output.length > 500 ? entry.output.slice(0, 500) + '...' : entry.output;
186
+ lines.push(` output: ${preview}`);
187
+ }
188
+ }
189
+ return lines.join('\n');
190
+ }
191
+ async function handleDaemonStart() {
192
+ const { pid, alreadyRunning } = await startDaemon();
193
+ if (alreadyRunning) {
194
+ return `Cron daemon is already running (pid ${pid}).`;
195
+ }
196
+ return `Cron daemon started (pid ${pid}).`;
197
+ }
198
+ async function handleDaemonStop() {
199
+ const stopped = await stopDaemon();
200
+ return stopped ? 'Cron daemon stopped.' : 'Cron daemon was not running.';
201
+ }
202
+ async function handleList(storePath, includeDisabled) {
203
+ const store = await loadCronStore(storePath);
204
+ const now = Date.now();
205
+ let storeChanged = false;
206
+ for (const j of store.jobs) {
207
+ if (normalizeJobState(j, now))
208
+ storeChanged = true;
209
+ }
210
+ if (storeChanged)
211
+ await saveCronStore(storePath, store);
212
+ const jobs = includeDisabled ? store.jobs : store.jobs.filter((j) => j.enabled);
213
+ if (jobs.length === 0) {
214
+ return includeDisabled
215
+ ? 'No cron jobs.'
216
+ : 'No enabled cron jobs. Use includeDisabled to see all.';
217
+ }
218
+ return jobs.map((j) => formatJob(j)).join('\n\n');
219
+ }
220
+ async function handleAdd(storePath, input) {
221
+ const now = Date.now();
222
+ // Validate schedule before creating
223
+ try {
224
+ const nextRun = computeNextRunAtMs(input.schedule, now);
225
+ if (nextRun === undefined && input.schedule.kind === 'at') {
226
+ return 'Error: the "at" timestamp is in the past. Use a future timestamp.';
227
+ }
228
+ }
229
+ catch (err) {
230
+ return `Error: invalid schedule — ${err instanceof Error ? err.message : String(err)}`;
231
+ }
232
+ const jobCreate = {
233
+ name: input.name,
234
+ schedule: input.schedule,
235
+ payload: input.payload,
236
+ enabled: input.enabled ?? true,
237
+ description: input.description
238
+ };
239
+ const store = await loadCronStore(storePath);
240
+ const job = createJob(jobCreate, now);
241
+ store.jobs.push(job);
242
+ await saveCronStore(storePath, store);
243
+ let result = `Created job "${job.name}" (${job.id})\nSchedule: ${formatSchedule(job.schedule)}\nNext run: ${job.state.nextRunAtMs ? new Date(job.state.nextRunAtMs).toISOString() : 'none'}`;
244
+ // Auto-start daemon if not running
245
+ if (job.enabled && !isDaemonRunning()) {
246
+ try {
247
+ const { pid } = await startDaemon();
248
+ result += `\nDaemon started (pid ${pid}).`;
249
+ }
250
+ catch {
251
+ result += '\nNote: could not auto-start daemon. Use daemon_start manually.';
252
+ }
253
+ }
254
+ return result;
255
+ }
256
+ async function handleUpdate(storePath, jobId, patch) {
257
+ const store = await loadCronStore(storePath);
258
+ const job = store.jobs.find((j) => j.id === jobId);
259
+ if (!job)
260
+ return `Error: no job with id "${jobId}". Use the list action to see available jobs.`;
261
+ const now = Date.now();
262
+ // Validate new schedule if provided
263
+ if (patch.schedule) {
264
+ try {
265
+ const nextRun = computeNextRunAtMs(patch.schedule, now);
266
+ if (nextRun === undefined && patch.schedule.kind === 'at') {
267
+ return 'Error: the "at" timestamp is in the past. Use a future timestamp.';
268
+ }
269
+ }
270
+ catch (err) {
271
+ return `Error: invalid schedule — ${err instanceof Error ? err.message : String(err)}`;
272
+ }
273
+ }
274
+ applyJobPatch(job, patch, now);
275
+ await saveCronStore(storePath, store);
276
+ return `Updated job "${job.name}" (${job.id})\n${formatJob(job)}`;
277
+ }
278
+ async function handleRemove(storePath, jobId) {
279
+ const store = await loadCronStore(storePath);
280
+ const idx = store.jobs.findIndex((j) => j.id === jobId);
281
+ if (idx === -1)
282
+ return `Error: no job with id "${jobId}".`;
283
+ const removed = store.jobs.splice(idx, 1)[0];
284
+ await saveCronStore(storePath, store);
285
+ return `Removed job "${removed.name}" (${removed.id}).`;
286
+ }
287
+ async function handleRun(storePath, jobId) {
288
+ const store = await loadCronStore(storePath);
289
+ const job = store.jobs.find((j) => j.id === jobId);
290
+ if (!job)
291
+ return `Error: no job with id "${jobId}".`;
292
+ if (!job.enabled)
293
+ return `Error: job "${job.name}" is disabled. Enable it first.`;
294
+ if (typeof job.state.runningAtMs === 'number')
295
+ return `Job "${job.name}" is already running.`;
296
+ const now = Date.now();
297
+ // Mark as due so the daemon picks it up on next tick
298
+ job.state.runningAtMs = undefined;
299
+ job.state.nextRunAtMs = now - 1; // Force due
300
+ await saveCronStore(storePath, store);
301
+ if (!isDaemonRunning()) {
302
+ return `Triggered job "${job.name}" (${job.id}), but the daemon is not running. Use daemon_start first.`;
303
+ }
304
+ return `Triggered job "${job.name}" (${job.id}). It will execute on the next daemon tick.`;
305
+ }
306
+ // ---------------------------------------------------------------------------
307
+ // Tool description
308
+ // ---------------------------------------------------------------------------
309
+ const TOOL_DESCRIPTION = `\
310
+ Manage cron jobs — scheduled tasks that run automatically.
311
+
312
+ **Actions:**
313
+ - \`status\` — Show job count and next fire time
314
+ - \`list\` — List jobs (set includeDisabled=true to see disabled ones)
315
+ - \`add\` — Create a new scheduled job
316
+ - \`update\` — Modify an existing job (requires jobId + patch)
317
+ - \`remove\` — Delete a job (requires jobId)
318
+ - \`run\` — Force a job to run on the next daemon tick (requires jobId)
319
+ - \`history\` — View recent run results with output (requires jobId, optional limit)
320
+ - \`daemon_start\` — Start the background scheduler
321
+ - \`daemon_stop\` — Stop the scheduler
322
+
323
+ **Schedule types:**
324
+ - \`at\`: one-shot at a specific time — \`{ kind: "at", at: "2026-03-06T17:00:00Z" }\`
325
+ - \`every\`: recurring interval — \`{ kind: "every", everyMs: 1800000 }\` (30 minutes)
326
+ - \`cron\`: cron expression — \`{ kind: "cron", expr: "0 8 * * *" }\` (daily at 8am)
327
+
328
+ **Examples:**
329
+ - "every morning at 8am" → schedule: \`{ kind: "cron", expr: "0 8 * * *" }\`
330
+ - "every 30 minutes" → schedule: \`{ kind: "every", everyMs: 1800000 }\`
331
+ - "at 5pm today" → schedule: \`{ kind: "at", at: "<today>T17:00:00Z" }\` (use actual date)
332
+ - "every weekday at 9am ET" → schedule: \`{ kind: "cron", expr: "0 9 * * 1-5", tz: "America/New_York" }\`
333
+ `;
334
+ // ---------------------------------------------------------------------------
335
+ // Factory
336
+ // ---------------------------------------------------------------------------
337
+ export function createCronTool(storePath = getCronStorePath()) {
338
+ return {
339
+ name: CRON,
340
+ label: 'Cron',
341
+ description: TOOL_DESCRIPTION,
342
+ parameters: schema,
343
+ async execute(_toolCallId, rawParams, _signal, _onUpdate, _ctx) {
344
+ const params = recoverFlatParams(rawParams);
345
+ let text;
346
+ switch (params.action) {
347
+ case 'status':
348
+ text = await handleStatus(storePath);
349
+ break;
350
+ case 'list':
351
+ text = await handleList(storePath, params.includeDisabled ?? false);
352
+ break;
353
+ case 'add': {
354
+ if (!params.job) {
355
+ text = 'Error: "add" action requires a "job" object with name, schedule, and payload.';
356
+ break;
357
+ }
358
+ text = await handleAdd(storePath, params.job);
359
+ break;
360
+ }
361
+ case 'update': {
362
+ if (!params.jobId) {
363
+ text = 'Error: "update" action requires "jobId".';
364
+ break;
365
+ }
366
+ if (!params.patch) {
367
+ text = 'Error: "update" action requires a "patch" object.';
368
+ break;
369
+ }
370
+ text = await handleUpdate(storePath, params.jobId, params.patch);
371
+ break;
372
+ }
373
+ case 'remove': {
374
+ if (!params.jobId) {
375
+ text = 'Error: "remove" action requires "jobId".';
376
+ break;
377
+ }
378
+ text = await handleRemove(storePath, params.jobId);
379
+ break;
380
+ }
381
+ case 'run': {
382
+ if (!params.jobId) {
383
+ text = 'Error: "run" action requires "jobId".';
384
+ break;
385
+ }
386
+ text = await handleRun(storePath, params.jobId);
387
+ break;
388
+ }
389
+ case 'history': {
390
+ if (!params.jobId) {
391
+ text = 'Error: "history" action requires "jobId".';
392
+ break;
393
+ }
394
+ text = await handleHistory(storePath, params.jobId, params.limit ?? 5);
395
+ break;
396
+ }
397
+ case 'daemon_start':
398
+ text = await handleDaemonStart();
399
+ break;
400
+ case 'daemon_stop':
401
+ text = await handleDaemonStop();
402
+ break;
403
+ default:
404
+ text = `Unknown action "${params.action}". Valid actions: status, list, add, update, remove, run, history, daemon_start, daemon_stop.`;
405
+ }
406
+ return {
407
+ content: [{ type: 'text', text }],
408
+ details: undefined
409
+ };
410
+ }
411
+ };
412
+ }
413
+ //# sourceMappingURL=cron.js.map
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Excel exec tool — POST /exec with Python code to ShortcutXL's HTTP server.
3
+ *
4
+ * Request: POST /exec { "code": "..." }
5
+ * Response: { "ok": true, "output": "..." } or { "ok": false, "error": "..." }
6
+ */
7
+ import { Type } from '@sinclair/typebox';
8
+ import { truncateOutput } from '../../core/tools/truncate.js';
9
+ import { EXCEL_EXEC } from '../../tool-names.js';
10
+ import { Text } from '../../tui/index.js';
11
+ import { formatNotation, parseExcelLocation } from './excel-range.js';
12
+ import { highlightCode } from './render-helpers.js';
13
+ const TOOL_NAME = EXCEL_EXEC;
14
+ /**
15
+ * Wraps user code so it runs inside managed_app() without any
16
+ * indentation changes (which would corrupt multiline strings).
17
+ *
18
+ * We compile the user code separately and exec() it, passing `app`
19
+ * from the context manager into the exec namespace.
20
+ */
21
+ /** Sentinels used to separate structured data from agent stdout. */
22
+ const DIRTY_CELLS_MARKER = '\n__DIRTY_CELLS_JSON__';
23
+ const DIFF_SUMMARY_MARKER = '\n__DIFF_SUMMARY__';
24
+ const NO_OUTPUT_FALLBACK = '(no output)';
25
+ const NO_CHANGES_MSG = 'No changes made.';
26
+ function wrapCode(userCode) {
27
+ // JSON.stringify produces a valid Python string literal (both use the
28
+ // same escape sequences for \n, \t, \\, \", and Unicode \uXXXX).
29
+ const codeLiteral = JSON.stringify(userCode);
30
+ return [
31
+ 'from shortcut_xl import run_managed as _run_managed, format_cell_diff as _format_diff',
32
+ 'import json as _json',
33
+ `_code = compile(${codeLiteral}, "<excel_exec>", "exec")`,
34
+ 'def _fn(app):',
35
+ ' _ns = {**globals(), "app": app}',
36
+ ' exec(_code, _ns)',
37
+ '_dirty = _run_managed(_fn)',
38
+ 'print("\\n__DIRTY_CELLS_JSON__" + _json.dumps(_dirty, default=str))',
39
+ 'print("\\n__DIFF_SUMMARY__" + _format_diff(_dirty))'
40
+ ].join('\n');
41
+ }
42
+ /** What the LLM sees — hides the context-manager wrapper. */
43
+ const VISIBLE_PREAMBLE = `from shortcut_xl import xl_app\napp = xl_app()\n`;
44
+ const TOOL_DESCRIPTION = `\
45
+ Execute Python code in Excel via ShortcutXL.
46
+ The code runs in-process with access to the Excel COM API via app (an Excel.Application COM object, same API as VBA).
47
+ Stdout/stderr are captured. Use print() to return values.
48
+
49
+ \`\`\`python
50
+ ${VISIBLE_PREAMBLE}
51
+ \`\`\`
52
+
53
+ is auto-prepended — do NOT include it in your code.
54
+
55
+ **Code discipline:**
56
+ - Keep each code block under 100 lines — errors accumulate quickly in long blocks.
57
+ - Minimize comments. Use print() statements for inspection, and keep them concise.
58
+ - For read operations, prefer reading large ranges to grab richer context instead of iteratively reading small ranges.
59
+ - Your code should be simple and efficient. Minimize code duplication.
60
+ - Stop retrying after 2 attempts for unexplained failures.
61
+ `;
62
+ const schema = Type.Object({
63
+ description: Type.String({
64
+ description: 'Short description of what the code does for non-technical users. You MUST reference ranges for any read / write operation. Use this notation for reads and writes, including the bracekts: <Workbook:Sheet!Range>. Omit range if operating on the whole sheet or workbook.'
65
+ }),
66
+ code: Type.String({
67
+ description: 'Python code to execute in Excel. `app` (Excel.Application) is already available — do NOT import xl_app. Use print() to return values.'
68
+ })
69
+ });
70
+ export function createExcelExecTool(httpUrl) {
71
+ return {
72
+ name: TOOL_NAME,
73
+ label: 'Excel exec',
74
+ description: TOOL_DESCRIPTION,
75
+ parameters: schema,
76
+ renderCall(args, options, theme) {
77
+ const desc = typeof args?.description === 'string' ? args.description : '';
78
+ const code = typeof args?.code === 'string' ? args.code : '';
79
+ // Highlight the Excel reference in accent color
80
+ let label = desc || 'excel_exec';
81
+ const loc = desc ? parseExcelLocation(desc) : null;
82
+ if (loc) {
83
+ const notation = formatNotation(loc);
84
+ if (notation) {
85
+ const styled = theme.fg('accent', notation);
86
+ const bracketedIdx = label.indexOf(`<${notation}>`);
87
+ if (bracketedIdx >= 0) {
88
+ label = label.replace(`<${notation}>`, styled);
89
+ }
90
+ else {
91
+ label = label.replace(notation, styled);
92
+ }
93
+ }
94
+ }
95
+ const title = theme.fg('toolTitle', theme.bold(`𝕏 ${label}`));
96
+ if (!options.expanded) {
97
+ return new Text(title, 0, 0);
98
+ }
99
+ if (!code) {
100
+ return new Text(`${title}\n\n${theme.fg('toolOutput', '...')}`, 0, 0);
101
+ }
102
+ const highlighted = highlightCode(code, 'python');
103
+ return new Text(`${title}\n\n${highlighted.join('\n')}`, 0, 0);
104
+ },
105
+ renderResult(result, options, theme) {
106
+ if (!options.expanded)
107
+ return undefined;
108
+ const textBlocks = result.content?.filter((c) => c.type === 'text') || [];
109
+ const output = textBlocks.map((c) => ('text' in c ? c.text : '') || '').join('\n');
110
+ if (!output)
111
+ return undefined;
112
+ return new Text(theme.fg('toolOutput', output), 0, 0);
113
+ },
114
+ async execute(_toolCallId, { code, description }, signal, _onUpdate, _ctx) {
115
+ const fullCode = wrapCode(code);
116
+ let response;
117
+ try {
118
+ response = await fetch(`${httpUrl}/exec`, {
119
+ method: 'POST',
120
+ headers: { 'Content-Type': 'application/json' },
121
+ body: JSON.stringify({ code: fullCode }),
122
+ signal
123
+ });
124
+ }
125
+ catch (e) {
126
+ const msg = e instanceof Error ? e.message : String(e);
127
+ throw new Error(`Failed to connect to Excel at ${httpUrl}: ${msg}`);
128
+ }
129
+ const result = (await response.json());
130
+ if (!result.ok) {
131
+ throw new Error(result.error ?? 'Unknown error from Excel');
132
+ }
133
+ // Extract diff summary and dirty cells from stdout (appended by wrapCode)
134
+ let output = result.output ?? NO_OUTPUT_FALLBACK;
135
+ let dirtyCells = [];
136
+ let diffSummary = '';
137
+ // Extract diff summary first (it comes after dirty cells JSON)
138
+ const summaryIdx = output.indexOf(DIFF_SUMMARY_MARKER);
139
+ if (summaryIdx !== -1) {
140
+ diffSummary = output.slice(summaryIdx + DIFF_SUMMARY_MARKER.length).trim();
141
+ output = output.slice(0, summaryIdx);
142
+ }
143
+ // Extract dirty cells JSON
144
+ const markerIdx = output.indexOf(DIRTY_CELLS_MARKER);
145
+ if (markerIdx !== -1) {
146
+ const jsonStr = output.slice(markerIdx + DIRTY_CELLS_MARKER.length).trim();
147
+ output = output.slice(0, markerIdx).trimEnd() || NO_OUTPUT_FALLBACK;
148
+ try {
149
+ dirtyCells = JSON.parse(jsonStr);
150
+ }
151
+ catch {
152
+ // Best-effort — if parsing fails, just skip dirty cells
153
+ }
154
+ }
155
+ result.dirtyCells = dirtyCells;
156
+ // Append the formatted diff summary to the visible output
157
+ if (diffSummary && diffSummary !== NO_CHANGES_MSG) {
158
+ output = output + '\n\n' + diffSummary;
159
+ }
160
+ return {
161
+ content: [{ type: 'text', text: truncateOutput(output) }],
162
+ details: result
163
+ };
164
+ }
165
+ };
166
+ }
167
+ //# sourceMappingURL=excel-exec.js.map
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Excel range parsing utilities.
3
+ *
4
+ * Parses <Workbook:Sheet!Range> notation from excel_exec descriptions.
5
+ */
6
+ /**
7
+ * Extract Excel location from the <...> bracket in a description string.
8
+ * Returns null if no bracketed reference is found.
9
+ */
10
+ export function parseExcelLocation(description) {
11
+ const m = description.match(/<([^>]+)>/);
12
+ if (!m)
13
+ return null;
14
+ const ref = m[1].trim();
15
+ // Workbook:Sheet!Range
16
+ const full = ref.match(/^([^:]+):([^!]+)!(.+)$/);
17
+ if (full) {
18
+ return { workbook: full[1].trim(), sheet: full[2].trim(), range: full[3].trim() };
19
+ }
20
+ // Sheet!Range
21
+ const sr = ref.match(/^([^!]+)!(.+)$/);
22
+ if (sr) {
23
+ return { sheet: sr[1].trim(), range: sr[2].trim() };
24
+ }
25
+ // Bare range (e.g. A1:D10, A1) — check before Workbook:Sheet since "A1:D10" has a colon
26
+ if (/^[A-Z]{1,3}\d+(?::[A-Z]{1,3}\d+)?$/.test(ref)) {
27
+ return { range: ref };
28
+ }
29
+ // Workbook:Sheet (no range)
30
+ const ws = ref.match(/^([^:]+):(.+)$/);
31
+ if (ws) {
32
+ return { workbook: ws[1].trim(), sheet: ws[2].trim() };
33
+ }
34
+ return ref ? { sheet: ref } : null;
35
+ }
36
+ /**
37
+ * Reconstruct the original notation: Workbook:Sheet!Range.
38
+ * Used for string replacement in renderCall labels.
39
+ */
40
+ export function formatNotation(loc) {
41
+ let s = '';
42
+ if (loc.workbook)
43
+ s += loc.workbook;
44
+ if (loc.sheet)
45
+ s += (s ? ':' : '') + loc.sheet;
46
+ if (loc.range)
47
+ s += (s ? '!' : '') + loc.range;
48
+ return s;
49
+ }
50
+ //# sourceMappingURL=excel-range.js.map