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.
- package/README.md +59 -0
- package/agent-docs/README.md +397 -0
- package/agent-docs/docs/compaction.md +390 -0
- package/agent-docs/docs/custom-provider.md +580 -0
- package/agent-docs/docs/development.md +69 -0
- package/agent-docs/docs/extensions.md +1971 -0
- package/agent-docs/docs/json.md +79 -0
- package/agent-docs/docs/keybindings.md +174 -0
- package/agent-docs/docs/models.md +293 -0
- package/agent-docs/docs/packages.md +209 -0
- package/agent-docs/docs/prompt-templates.md +67 -0
- package/agent-docs/docs/providers.md +186 -0
- package/agent-docs/docs/rpc.md +1317 -0
- package/agent-docs/docs/sdk.md +962 -0
- package/agent-docs/docs/session.md +412 -0
- package/agent-docs/docs/settings.md +223 -0
- package/agent-docs/docs/shell-aliases.md +13 -0
- package/agent-docs/docs/skills.md +231 -0
- package/agent-docs/docs/terminal-setup.md +70 -0
- package/agent-docs/docs/termux.md +127 -0
- package/agent-docs/docs/themes.md +295 -0
- package/agent-docs/docs/tree.md +219 -0
- package/agent-docs/docs/tui.md +887 -0
- package/agent-docs/docs/windows.md +17 -0
- package/agent-docs/examples/README.md +25 -0
- package/agent-docs/examples/extensions/README.md +205 -0
- package/agent-docs/examples/extensions/antigravity-image-gen.ts +447 -0
- package/agent-docs/examples/extensions/auto-commit-on-exit.ts +49 -0
- package/agent-docs/examples/extensions/bash-spawn-hook.ts +30 -0
- package/agent-docs/examples/extensions/bookmark.ts +50 -0
- package/agent-docs/examples/extensions/built-in-tool-renderer.ts +256 -0
- package/agent-docs/examples/extensions/claude-rules.ts +86 -0
- package/agent-docs/examples/extensions/commands.ts +75 -0
- package/agent-docs/examples/extensions/confirm-destructive.ts +59 -0
- package/agent-docs/examples/extensions/custom-compaction.ts +126 -0
- package/agent-docs/examples/extensions/custom-footer.ts +63 -0
- package/agent-docs/examples/extensions/custom-header.ts +73 -0
- package/agent-docs/examples/extensions/custom-provider-anthropic/index.ts +660 -0
- package/agent-docs/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
- package/agent-docs/examples/extensions/custom-provider-anthropic/package.json +19 -0
- package/agent-docs/examples/extensions/custom-provider-gitlab-duo/index.ts +362 -0
- package/agent-docs/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
- package/agent-docs/examples/extensions/custom-provider-gitlab-duo/test.ts +88 -0
- package/agent-docs/examples/extensions/custom-provider-qwen-cli/index.ts +349 -0
- package/agent-docs/examples/extensions/custom-provider-qwen-cli/package.json +16 -0
- package/agent-docs/examples/extensions/dirty-repo-guard.ts +56 -0
- package/agent-docs/examples/extensions/doom-overlay/README.md +46 -0
- package/agent-docs/examples/extensions/doom-overlay/doom/build.sh +152 -0
- package/agent-docs/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
- package/agent-docs/examples/extensions/doom-overlay/doom-component.ts +133 -0
- package/agent-docs/examples/extensions/doom-overlay/doom-engine.ts +186 -0
- package/agent-docs/examples/extensions/doom-overlay/doom-keys.ts +108 -0
- package/agent-docs/examples/extensions/doom-overlay/index.ts +74 -0
- package/agent-docs/examples/extensions/doom-overlay/wad-finder.ts +51 -0
- package/agent-docs/examples/extensions/dynamic-resources/SKILL.md +8 -0
- package/agent-docs/examples/extensions/dynamic-resources/dynamic.json +79 -0
- package/agent-docs/examples/extensions/dynamic-resources/dynamic.md +5 -0
- package/agent-docs/examples/extensions/dynamic-resources/index.ts +15 -0
- package/agent-docs/examples/extensions/dynamic-tools.ts +77 -0
- package/agent-docs/examples/extensions/event-bus.ts +43 -0
- package/agent-docs/examples/extensions/file-trigger.ts +41 -0
- package/agent-docs/examples/extensions/git-checkpoint.ts +53 -0
- package/agent-docs/examples/extensions/handoff.ts +155 -0
- package/agent-docs/examples/extensions/hello.ts +25 -0
- package/agent-docs/examples/extensions/inline-bash.ts +94 -0
- package/agent-docs/examples/extensions/input-transform.ts +43 -0
- package/agent-docs/examples/extensions/interactive-shell.ts +209 -0
- package/agent-docs/examples/extensions/mac-system-theme.ts +47 -0
- package/agent-docs/examples/extensions/message-renderer.ts +59 -0
- package/agent-docs/examples/extensions/minimal-mode.ts +430 -0
- package/agent-docs/examples/extensions/modal-editor.ts +90 -0
- package/agent-docs/examples/extensions/model-status.ts +31 -0
- package/agent-docs/examples/extensions/notify.ts +55 -0
- package/agent-docs/examples/extensions/overlay-qa-tests.ts +936 -0
- package/agent-docs/examples/extensions/overlay-test.ts +159 -0
- package/agent-docs/examples/extensions/permission-gate.ts +37 -0
- package/agent-docs/examples/extensions/pirate.ts +47 -0
- package/agent-docs/examples/extensions/plan-mode/README.md +65 -0
- package/agent-docs/examples/extensions/plan-mode/index.ts +363 -0
- package/agent-docs/examples/extensions/plan-mode/utils.ts +173 -0
- package/agent-docs/examples/extensions/preset.ts +418 -0
- package/agent-docs/examples/extensions/protected-paths.ts +30 -0
- package/agent-docs/examples/extensions/qna.ts +122 -0
- package/agent-docs/examples/extensions/question.ts +278 -0
- package/agent-docs/examples/extensions/questionnaire.ts +440 -0
- package/agent-docs/examples/extensions/rainbow-editor.ts +90 -0
- package/agent-docs/examples/extensions/reload-runtime.ts +37 -0
- package/agent-docs/examples/extensions/rpc-demo.ts +124 -0
- package/agent-docs/examples/extensions/sandbox/index.ts +324 -0
- package/agent-docs/examples/extensions/sandbox/package-lock.json +92 -0
- package/agent-docs/examples/extensions/sandbox/package.json +19 -0
- package/agent-docs/examples/extensions/send-user-message.ts +97 -0
- package/agent-docs/examples/extensions/session-name.ts +27 -0
- package/agent-docs/examples/extensions/shutdown-command.ts +69 -0
- package/agent-docs/examples/extensions/snake.ts +343 -0
- package/agent-docs/examples/extensions/space-invaders.ts +566 -0
- package/agent-docs/examples/extensions/ssh.ts +233 -0
- package/agent-docs/examples/extensions/status-line.ts +40 -0
- package/agent-docs/examples/extensions/subagent/README.md +172 -0
- package/agent-docs/examples/extensions/subagent/agents/planner.md +37 -0
- package/agent-docs/examples/extensions/subagent/agents/reviewer.md +35 -0
- package/agent-docs/examples/extensions/subagent/agents/scout.md +50 -0
- package/agent-docs/examples/extensions/subagent/agents/worker.md +24 -0
- package/agent-docs/examples/extensions/subagent/agents.ts +130 -0
- package/agent-docs/examples/extensions/subagent/index.ts +1068 -0
- package/agent-docs/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
- package/agent-docs/examples/extensions/subagent/prompts/implement.md +10 -0
- package/agent-docs/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
- package/agent-docs/examples/extensions/summarize.ts +206 -0
- package/agent-docs/examples/extensions/system-prompt-header.ts +17 -0
- package/agent-docs/examples/extensions/timed-confirm.ts +72 -0
- package/agent-docs/examples/extensions/titlebar-spinner.ts +58 -0
- package/agent-docs/examples/extensions/todo.ts +314 -0
- package/agent-docs/examples/extensions/tool-override.ts +146 -0
- package/agent-docs/examples/extensions/tools.ts +145 -0
- package/agent-docs/examples/extensions/trigger-compact.ts +40 -0
- package/agent-docs/examples/extensions/truncated-tool.ts +194 -0
- package/agent-docs/examples/extensions/widget-placement.ts +17 -0
- package/agent-docs/examples/extensions/with-deps/index.ts +37 -0
- package/agent-docs/examples/extensions/with-deps/package-lock.json +31 -0
- package/agent-docs/examples/extensions/with-deps/package.json +22 -0
- package/agent-docs/examples/rpc-extension-ui.ts +654 -0
- package/agent-docs/examples/sdk/01-minimal.ts +22 -0
- package/agent-docs/examples/sdk/02-custom-model.ts +48 -0
- package/agent-docs/examples/sdk/03-custom-prompt.ts +55 -0
- package/agent-docs/examples/sdk/04-skills.ts +53 -0
- package/agent-docs/examples/sdk/05-tools.ts +56 -0
- package/agent-docs/examples/sdk/06-extensions.ts +88 -0
- package/agent-docs/examples/sdk/07-context-files.ts +40 -0
- package/agent-docs/examples/sdk/08-prompt-templates.ts +47 -0
- package/agent-docs/examples/sdk/09-api-keys-and-oauth.ts +48 -0
- package/agent-docs/examples/sdk/10-settings.ts +54 -0
- package/agent-docs/examples/sdk/11-sessions.ts +48 -0
- package/agent-docs/examples/sdk/12-full-control.ts +82 -0
- package/agent-docs/examples/sdk/README.md +144 -0
- package/agent-docs/xll-skill.md +61 -0
- package/agent-docs/xll-spec.md +110 -0
- package/dist/cli/args.js +290 -0
- package/dist/cli/config-selector.js +31 -0
- package/dist/cli/file-processor.js +79 -0
- package/dist/cli/list-models.js +92 -0
- package/dist/cli/package-commands.js +210 -0
- package/dist/cli/report-settings-errors.js +11 -0
- package/dist/cli/session-picker.js +34 -0
- package/dist/cli.js +19 -0
- package/dist/config.js +288 -0
- package/dist/core/abort.js +15 -0
- package/dist/core/agent-loop.js +352 -0
- package/dist/core/agent-session.js +2019 -0
- package/dist/core/agent.js +410 -0
- package/dist/core/auth-storage.js +456 -0
- package/dist/core/bash-executor.js +222 -0
- package/dist/core/compaction/branch-summarization.js +242 -0
- package/dist/core/compaction/compaction.js +610 -0
- package/dist/core/compaction/index.js +7 -0
- package/dist/core/compaction/utils.js +139 -0
- package/dist/core/defaults.js +6 -0
- package/dist/core/diagnostics.js +2 -0
- package/dist/core/event-bus.js +25 -0
- package/dist/core/exec.js +71 -0
- package/dist/core/export-html/ansi-to-html.js +256 -0
- package/dist/core/export-html/index.js +238 -0
- package/dist/core/export-html/session-view-model.js +342 -0
- package/dist/core/export-html/template.css +1110 -0
- package/dist/core/export-html/template.html +76 -0
- package/dist/core/export-html/template.js +1990 -0
- package/dist/core/export-html/tool-renderer.js +63 -0
- package/dist/core/export-html/vendor/highlight.min.js +7725 -0
- package/dist/core/export-html/vendor/marked.min.js +1803 -0
- package/dist/core/extensions/index.js +9 -0
- package/dist/core/extensions/loader.js +422 -0
- package/dist/core/extensions/runner.js +651 -0
- package/dist/core/extensions/types.js +35 -0
- package/dist/core/extensions/wrapper.js +102 -0
- package/dist/core/footer-data-provider.js +162 -0
- package/dist/core/index.js +9 -0
- package/dist/core/keybindings.js +153 -0
- package/dist/core/messages.js +133 -0
- package/dist/core/model-registry.js +539 -0
- package/dist/core/model-resolver.js +370 -0
- package/dist/core/package-manager.js +1485 -0
- package/dist/core/prompt-templates.js +253 -0
- package/dist/core/resolve-config-value.js +59 -0
- package/dist/core/resource-loader.js +700 -0
- package/dist/core/sdk.js +197 -0
- package/dist/core/session-bash.js +99 -0
- package/dist/core/session-compaction.js +165 -0
- package/dist/core/session-manager.js +1153 -0
- package/dist/core/session-models.js +99 -0
- package/dist/core/session-retry.js +155 -0
- package/dist/core/settings-manager.js +572 -0
- package/dist/core/skills.js +382 -0
- package/dist/core/slash-commands.js +31 -0
- package/dist/core/system-prompt.js +161 -0
- package/dist/core/theme.js +770 -0
- package/dist/core/timings.js +26 -0
- package/dist/core/tools/bash.js +258 -0
- package/dist/core/tools/edit-diff.js +245 -0
- package/dist/core/tools/edit.js +148 -0
- package/dist/core/tools/find.js +208 -0
- package/dist/core/tools/grep.js +246 -0
- package/dist/core/tools/index.js +67 -0
- package/dist/core/tools/ls.js +123 -0
- package/dist/core/tools/path-utils.js +81 -0
- package/dist/core/tools/read.js +160 -0
- package/dist/core/tools/truncate.js +70 -0
- package/dist/core/tools/write.js +82 -0
- package/dist/custom/agents/action.js +13 -0
- package/dist/custom/agents/document-reader.js +70 -0
- package/dist/custom/agents/general.js +26 -0
- package/dist/custom/agents/index.js +49 -0
- package/dist/custom/agents/installation.js +13 -0
- package/dist/custom/agents/types.js +7 -0
- package/dist/custom/auth/refresh-timer.js +33 -0
- package/dist/custom/auth/shortcut-oauth.js +145 -0
- package/dist/custom/constants.js +21 -0
- package/dist/custom/context/workbook-summary.js +73 -0
- package/dist/custom/credits/shortcut-credits.js +29 -0
- package/dist/custom/cron/cron-daemon-entry.js +18 -0
- package/dist/custom/cron/daemon-ipc.js +131 -0
- package/dist/custom/cron/daemon.js +224 -0
- package/dist/custom/cron/jobs.js +226 -0
- package/dist/custom/cron/run-log.js +51 -0
- package/dist/custom/cron/schedule.js +72 -0
- package/dist/custom/cron/status-line.js +98 -0
- package/dist/custom/cron/store.js +87 -0
- package/dist/custom/cron/types.js +8 -0
- package/dist/custom/dev/index.js +59 -0
- package/dist/custom/dev/trace-export.js +58 -0
- package/dist/custom/ensure-excel.js +63 -0
- package/dist/custom/excel-config.js +36 -0
- package/dist/custom/preflight.js +422 -0
- package/dist/custom/prompts/action.js +100 -0
- package/dist/custom/prompts/api.js +66 -0
- package/dist/custom/prompts/installation.js +124 -0
- package/dist/custom/prompts/shared.js +138 -0
- package/dist/custom/providers/llm-usage.js +42 -0
- package/dist/custom/providers/message-converter.js +74 -0
- package/dist/custom/providers/provider-ids.js +9 -0
- package/dist/custom/providers/register-openai-codex-provider.js +27 -0
- package/dist/custom/providers/register-shortcut-provider.js +52 -0
- package/dist/custom/providers/shortcut-invoke.js +117 -0
- package/dist/custom/providers/shortcut-stream.js +252 -0
- package/dist/custom/providers/sse-protocol.js +38 -0
- package/dist/custom/sync-xll.js +130 -0
- package/dist/custom/tools/cron.js +413 -0
- package/dist/custom/tools/excel-exec.js +167 -0
- package/dist/custom/tools/excel-range.js +50 -0
- package/dist/custom/tools/llm-analysis.js +265 -0
- package/dist/custom/tools/render-helpers.js +38 -0
- package/dist/custom/tools/switch-mode.js +94 -0
- package/dist/custom/tools/task/agents.js +6 -0
- package/dist/custom/tools/task/index.js +8 -0
- package/dist/custom/tools/task/render.js +348 -0
- package/dist/custom/tools/task/subprocess.js +320 -0
- package/dist/custom/tools/task/task.js +205 -0
- package/dist/custom/tools/todo-list.js +195 -0
- package/dist/custom/tracing/session-upload.js +93 -0
- package/dist/index.js +45 -0
- package/dist/main.js +613 -0
- package/dist/migrations.js +265 -0
- package/dist/modes/index.js +8 -0
- package/dist/modes/interactive/components/armin.js +337 -0
- package/dist/modes/interactive/components/assistant-message.js +94 -0
- package/dist/modes/interactive/components/bash-execution.js +171 -0
- package/dist/modes/interactive/components/bordered-loader.js +51 -0
- package/dist/modes/interactive/components/branch-summary-message.js +45 -0
- package/dist/modes/interactive/components/compaction-summary-message.js +46 -0
- package/dist/modes/interactive/components/config-selector.js +488 -0
- package/dist/modes/interactive/components/countdown-timer.js +33 -0
- package/dist/modes/interactive/components/custom-editor.js +93 -0
- package/dist/modes/interactive/components/custom-message.js +81 -0
- package/dist/modes/interactive/components/daxnuts.js +140 -0
- package/dist/modes/interactive/components/diff.js +133 -0
- package/dist/modes/interactive/components/dynamic-border.js +21 -0
- package/dist/modes/interactive/components/extension-editor.js +105 -0
- package/dist/modes/interactive/components/extension-input.js +61 -0
- package/dist/modes/interactive/components/extension-selector.js +78 -0
- package/dist/modes/interactive/components/footer.js +309 -0
- package/dist/modes/interactive/components/index.js +33 -0
- package/dist/modes/interactive/components/keybinding-hints.js +61 -0
- package/dist/modes/interactive/components/layout.js +64 -0
- package/dist/modes/interactive/components/login-dialog.js +148 -0
- package/dist/modes/interactive/components/model-selector.js +237 -0
- package/dist/modes/interactive/components/oauth-selector.js +111 -0
- package/dist/modes/interactive/components/session-selector-search.js +157 -0
- package/dist/modes/interactive/components/session-selector.js +860 -0
- package/dist/modes/interactive/components/settings-selector.js +123 -0
- package/dist/modes/interactive/components/show-images-selector.js +35 -0
- package/dist/modes/interactive/components/skill-invocation-message.js +48 -0
- package/dist/modes/interactive/components/theme-selector.js +47 -0
- package/dist/modes/interactive/components/thinking-selector.js +47 -0
- package/dist/modes/interactive/components/tool-execution.js +789 -0
- package/dist/modes/interactive/components/tool-group.js +106 -0
- package/dist/modes/interactive/components/tree-selector.js +962 -0
- package/dist/modes/interactive/components/user-message-selector.js +115 -0
- package/dist/modes/interactive/components/user-message.js +48 -0
- package/dist/modes/interactive/components/visual-truncate.js +33 -0
- package/dist/modes/interactive/file-attachments.js +135 -0
- package/dist/modes/interactive/interactive-mode.js +3775 -0
- package/dist/modes/interactive/theme/dark.json +85 -0
- package/dist/modes/interactive/theme/light.json +85 -0
- package/dist/modes/interactive/theme/theme-schema.json +335 -0
- package/dist/modes/interactive/theme/theme.js +177 -0
- package/dist/modes/print-mode.js +101 -0
- package/dist/modes/rpc/rpc-client.js +387 -0
- package/dist/modes/rpc/rpc-mode.js +509 -0
- package/dist/modes/rpc/rpc-types.js +8 -0
- package/dist/subagent-entry.js +145 -0
- package/dist/tool-names.js +34 -0
- package/dist/tui/autocomplete.js +596 -0
- package/dist/tui/components/box.js +104 -0
- package/dist/tui/components/cancellable-loader.js +35 -0
- package/dist/tui/components/editor.js +1679 -0
- package/dist/tui/components/image.js +69 -0
- package/dist/tui/components/input.js +433 -0
- package/dist/tui/components/loader.js +49 -0
- package/dist/tui/components/markdown.js +629 -0
- package/dist/tui/components/select-list.js +152 -0
- package/dist/tui/components/settings-list.js +185 -0
- package/dist/tui/components/spacer.js +23 -0
- package/dist/tui/components/text.js +89 -0
- package/dist/tui/components/truncated-text.js +51 -0
- package/dist/tui/editor-component.js +2 -0
- package/dist/tui/fuzzy.js +107 -0
- package/dist/tui/get-east-asian-width/index.js +32 -0
- package/dist/tui/get-east-asian-width/lookup.js +404 -0
- package/dist/tui/index.js +32 -0
- package/dist/tui/keybindings.js +114 -0
- package/dist/tui/keys.js +959 -0
- package/dist/tui/kill-ring.js +44 -0
- package/dist/tui/stdin-buffer.js +317 -0
- package/dist/tui/terminal-image.js +288 -0
- package/dist/tui/terminal.js +249 -0
- package/dist/tui/tui/autocomplete.js +596 -0
- package/dist/tui/tui/components/box.js +106 -0
- package/dist/tui/tui/components/cancellable-loader.js +35 -0
- package/dist/tui/tui/components/editor.js +1679 -0
- package/dist/tui/tui/components/image.js +69 -0
- package/dist/tui/tui/components/input.js +433 -0
- package/dist/tui/tui/components/loader.js +49 -0
- package/dist/tui/tui/components/markdown.js +629 -0
- package/dist/tui/tui/components/select-list.js +152 -0
- package/dist/tui/tui/components/settings-list.js +185 -0
- package/dist/tui/tui/components/spacer.js +23 -0
- package/dist/tui/tui/components/text.js +91 -0
- package/dist/tui/tui/components/truncated-text.js +51 -0
- package/dist/tui/tui/editor-component.js +2 -0
- package/dist/tui/tui/fuzzy.js +107 -0
- package/dist/tui/tui/get-east-asian-width/index.js +32 -0
- package/dist/tui/tui/get-east-asian-width/lookup.js +404 -0
- package/dist/tui/tui/index.js +32 -0
- package/dist/tui/tui/keybindings.js +114 -0
- package/dist/tui/tui/keys.js +959 -0
- package/dist/tui/tui/kill-ring.js +44 -0
- package/dist/tui/tui/stdin-buffer.js +317 -0
- package/dist/tui/tui/terminal-image.js +288 -0
- package/dist/tui/tui/terminal.js +249 -0
- package/dist/tui/tui/tui.js +955 -0
- package/dist/tui/tui/undo-stack.js +25 -0
- package/dist/tui/tui/utils.js +800 -0
- package/dist/tui/tui.js +955 -0
- package/dist/tui/undo-stack.js +25 -0
- package/dist/tui/utils.js +800 -0
- package/dist/utils/changelog.js +87 -0
- package/dist/utils/clipboard-image.js +164 -0
- package/dist/utils/clipboard-native.js +14 -0
- package/dist/utils/clipboard.js +67 -0
- package/dist/utils/frontmatter.js +26 -0
- package/dist/utils/git.js +166 -0
- package/dist/utils/image-convert.js +35 -0
- package/dist/utils/image-resize.js +183 -0
- package/dist/utils/mime.js +26 -0
- package/dist/utils/photon.js +121 -0
- package/dist/utils/shell.js +217 -0
- package/dist/utils/sleep.js +17 -0
- package/dist/utils/tools-manager.js +259 -0
- package/package.json +78 -0
- package/skills/excel-com-api/SKILL.md +74 -0
- package/skills/excel-com-api/excel-type-library.py +27767 -0
- package/skills/excel-com-api/office-type-library.py +10867 -0
- package/skills/integrations/SKILL.md +138 -0
- package/skills/integrations/alphasense.md +457 -0
- package/skills/integrations/bloomberg.md +803 -0
- package/skills/integrations/calcbench.md +315 -0
- package/skills/integrations/capiq.md +848 -0
- package/skills/integrations/dynamics-365-finance.md +354 -0
- package/skills/integrations/earnings_transcripts.md +387 -0
- package/skills/integrations/factset.md +758 -0
- package/skills/integrations/ice-fixed-income.md +344 -0
- package/skills/integrations/moodys-analytics.md +313 -0
- package/skills/integrations/morningstar.md +433 -0
- package/skills/integrations/nasdaq-data-link.md +249 -0
- package/skills/integrations/pitchbook.md +413 -0
- package/skills/integrations/preqin.md +422 -0
- package/skills/integrations/quickbooks.md +289 -0
- package/skills/integrations/quickfs.md +314 -0
- package/skills/integrations/refinitiv.md +473 -0
- package/skills/integrations/sage-intacct.md +401 -0
- package/skills/integrations/visible-alpha.md +320 -0
- package/skills/integrations/xero.md +393 -0
- package/skills/integrations/ycharts.md +306 -0
- package/skills/pdf-creation/SKILL.md +93 -0
- package/skills/pdf-extraction/SKILL.md +32 -0
- package/skills/powerpoint-creation/SKILL.md +110 -0
- package/skills/sec-edgar/SKILL.md +127 -0
- package/skills/sec-edgar/sec_to_pdf.py +109 -0
- package/xll/ShortcutXL.xll +0 -0
- package/xll/modules/debug_render.py +272 -0
- package/xll/modules/gameboy.py +241 -0
- package/xll/modules/pong.py +188 -0
- package/xll/modules/shortcut_xl/__init__.py +18 -0
- package/xll/modules/shortcut_xl/_categorize.py +200 -0
- package/xll/modules/shortcut_xl/_com.py +108 -0
- package/xll/modules/shortcut_xl/_format.py +252 -0
- package/xll/modules/shortcut_xl/_log.py +12 -0
- package/xll/modules/shortcut_xl/_managed.py +116 -0
- package/xll/modules/shortcut_xl/_registry.py +44 -0
- package/xll/modules/shortcut_xl/_threading.py +161 -0
- package/xll/modules/shortcut_xl/_tracking.py +283 -0
- package/xll/modules/stocks.py +100 -0
- package/xll/python3.dll +0 -0
- package/xll/python312.dll +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { loadPhoton } from './photon.js';
|
|
2
|
+
// 4.5MB - provides headroom below Anthropic's 5MB limit
|
|
3
|
+
const DEFAULT_MAX_BYTES = 4.5 * 1024 * 1024;
|
|
4
|
+
const DEFAULT_OPTIONS = {
|
|
5
|
+
maxWidth: 2000,
|
|
6
|
+
maxHeight: 2000,
|
|
7
|
+
maxBytes: DEFAULT_MAX_BYTES,
|
|
8
|
+
jpegQuality: 80
|
|
9
|
+
};
|
|
10
|
+
/** Helper to pick the smaller of two buffers */
|
|
11
|
+
function pickSmaller(a, b) {
|
|
12
|
+
return a.buffer.length <= b.buffer.length ? a : b;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Resize an image to fit within the specified max dimensions and file size.
|
|
16
|
+
* Returns the original image if it already fits within the limits.
|
|
17
|
+
*
|
|
18
|
+
* Uses Photon (Rust/WASM) for image processing. If Photon is not available,
|
|
19
|
+
* returns the original image unchanged.
|
|
20
|
+
*
|
|
21
|
+
* Strategy for staying under maxBytes:
|
|
22
|
+
* 1. First resize to maxWidth/maxHeight
|
|
23
|
+
* 2. Try both PNG and JPEG formats, pick the smaller one
|
|
24
|
+
* 3. If still too large, try JPEG with decreasing quality
|
|
25
|
+
* 4. If still too large, progressively reduce dimensions
|
|
26
|
+
*/
|
|
27
|
+
export async function resizeImage(img, options) {
|
|
28
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
29
|
+
const inputBuffer = Buffer.from(img.data, 'base64');
|
|
30
|
+
const photon = await loadPhoton();
|
|
31
|
+
if (!photon) {
|
|
32
|
+
// Photon not available, return original image
|
|
33
|
+
return {
|
|
34
|
+
data: img.data,
|
|
35
|
+
mimeType: img.mimeType,
|
|
36
|
+
originalWidth: 0,
|
|
37
|
+
originalHeight: 0,
|
|
38
|
+
width: 0,
|
|
39
|
+
height: 0,
|
|
40
|
+
wasResized: false
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
let image;
|
|
44
|
+
try {
|
|
45
|
+
image = photon.PhotonImage.new_from_byteslice(new Uint8Array(inputBuffer));
|
|
46
|
+
const originalWidth = image.get_width();
|
|
47
|
+
const originalHeight = image.get_height();
|
|
48
|
+
const format = img.mimeType?.split('/')[1] ?? 'png';
|
|
49
|
+
// Check if already within all limits (dimensions AND size)
|
|
50
|
+
const originalSize = inputBuffer.length;
|
|
51
|
+
if (originalWidth <= opts.maxWidth &&
|
|
52
|
+
originalHeight <= opts.maxHeight &&
|
|
53
|
+
originalSize <= opts.maxBytes) {
|
|
54
|
+
return {
|
|
55
|
+
data: img.data,
|
|
56
|
+
mimeType: img.mimeType ?? `image/${format}`,
|
|
57
|
+
originalWidth,
|
|
58
|
+
originalHeight,
|
|
59
|
+
width: originalWidth,
|
|
60
|
+
height: originalHeight,
|
|
61
|
+
wasResized: false
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// Calculate initial dimensions respecting max limits
|
|
65
|
+
let targetWidth = originalWidth;
|
|
66
|
+
let targetHeight = originalHeight;
|
|
67
|
+
if (targetWidth > opts.maxWidth) {
|
|
68
|
+
targetHeight = Math.round((targetHeight * opts.maxWidth) / targetWidth);
|
|
69
|
+
targetWidth = opts.maxWidth;
|
|
70
|
+
}
|
|
71
|
+
if (targetHeight > opts.maxHeight) {
|
|
72
|
+
targetWidth = Math.round((targetWidth * opts.maxHeight) / targetHeight);
|
|
73
|
+
targetHeight = opts.maxHeight;
|
|
74
|
+
}
|
|
75
|
+
// Helper to resize and encode in both formats, returning the smaller one
|
|
76
|
+
function tryBothFormats(width, height, jpegQuality) {
|
|
77
|
+
const resized = photon.resize(image, width, height, photon.SamplingFilter.Lanczos3);
|
|
78
|
+
try {
|
|
79
|
+
const pngBuffer = resized.get_bytes();
|
|
80
|
+
const jpegBuffer = resized.get_bytes_jpeg(jpegQuality);
|
|
81
|
+
return pickSmaller({ buffer: pngBuffer, mimeType: 'image/png' }, { buffer: jpegBuffer, mimeType: 'image/jpeg' });
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
resized.free();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Try to produce an image under maxBytes
|
|
88
|
+
const qualitySteps = [85, 70, 55, 40];
|
|
89
|
+
const scaleSteps = [1.0, 0.75, 0.5, 0.35, 0.25];
|
|
90
|
+
let best;
|
|
91
|
+
let finalWidth = targetWidth;
|
|
92
|
+
let finalHeight = targetHeight;
|
|
93
|
+
// First attempt: resize to target dimensions, try both formats
|
|
94
|
+
best = tryBothFormats(targetWidth, targetHeight, opts.jpegQuality);
|
|
95
|
+
if (best.buffer.length <= opts.maxBytes) {
|
|
96
|
+
return {
|
|
97
|
+
data: Buffer.from(best.buffer).toString('base64'),
|
|
98
|
+
mimeType: best.mimeType,
|
|
99
|
+
originalWidth,
|
|
100
|
+
originalHeight,
|
|
101
|
+
width: finalWidth,
|
|
102
|
+
height: finalHeight,
|
|
103
|
+
wasResized: true
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// Still too large - try JPEG with decreasing quality
|
|
107
|
+
for (const quality of qualitySteps) {
|
|
108
|
+
best = tryBothFormats(targetWidth, targetHeight, quality);
|
|
109
|
+
if (best.buffer.length <= opts.maxBytes) {
|
|
110
|
+
return {
|
|
111
|
+
data: Buffer.from(best.buffer).toString('base64'),
|
|
112
|
+
mimeType: best.mimeType,
|
|
113
|
+
originalWidth,
|
|
114
|
+
originalHeight,
|
|
115
|
+
width: finalWidth,
|
|
116
|
+
height: finalHeight,
|
|
117
|
+
wasResized: true
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Still too large - reduce dimensions progressively
|
|
122
|
+
for (const scale of scaleSteps) {
|
|
123
|
+
finalWidth = Math.round(targetWidth * scale);
|
|
124
|
+
finalHeight = Math.round(targetHeight * scale);
|
|
125
|
+
if (finalWidth < 100 || finalHeight < 100) {
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
for (const quality of qualitySteps) {
|
|
129
|
+
best = tryBothFormats(finalWidth, finalHeight, quality);
|
|
130
|
+
if (best.buffer.length <= opts.maxBytes) {
|
|
131
|
+
return {
|
|
132
|
+
data: Buffer.from(best.buffer).toString('base64'),
|
|
133
|
+
mimeType: best.mimeType,
|
|
134
|
+
originalWidth,
|
|
135
|
+
originalHeight,
|
|
136
|
+
width: finalWidth,
|
|
137
|
+
height: finalHeight,
|
|
138
|
+
wasResized: true
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Last resort: return smallest version we produced
|
|
144
|
+
return {
|
|
145
|
+
data: Buffer.from(best.buffer).toString('base64'),
|
|
146
|
+
mimeType: best.mimeType,
|
|
147
|
+
originalWidth,
|
|
148
|
+
originalHeight,
|
|
149
|
+
width: finalWidth,
|
|
150
|
+
height: finalHeight,
|
|
151
|
+
wasResized: true
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// Failed to load image
|
|
156
|
+
return {
|
|
157
|
+
data: img.data,
|
|
158
|
+
mimeType: img.mimeType,
|
|
159
|
+
originalWidth: 0,
|
|
160
|
+
originalHeight: 0,
|
|
161
|
+
width: 0,
|
|
162
|
+
height: 0,
|
|
163
|
+
wasResized: false
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
if (image) {
|
|
168
|
+
image.free();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Format a dimension note for resized images.
|
|
174
|
+
* This helps the model understand the coordinate mapping.
|
|
175
|
+
*/
|
|
176
|
+
export function formatDimensionNote(result) {
|
|
177
|
+
if (!result.wasResized) {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
const scale = result.originalWidth / result.width;
|
|
181
|
+
return `[Image: original ${result.originalWidth}x${result.originalHeight}, displayed at ${result.width}x${result.height}. Multiply coordinates by ${scale.toFixed(2)} to map to original image.]`;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=image-resize.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { fileTypeFromBuffer } from 'file-type';
|
|
2
|
+
import { open } from 'node:fs/promises';
|
|
3
|
+
const IMAGE_MIME_TYPES = new Set(['image/jpeg', 'image/png', 'image/gif', 'image/webp']);
|
|
4
|
+
const FILE_TYPE_SNIFF_BYTES = 4100;
|
|
5
|
+
export async function detectSupportedImageMimeTypeFromFile(filePath) {
|
|
6
|
+
const fileHandle = await open(filePath, 'r');
|
|
7
|
+
try {
|
|
8
|
+
const buffer = Buffer.alloc(FILE_TYPE_SNIFF_BYTES);
|
|
9
|
+
const { bytesRead } = await fileHandle.read(buffer, 0, FILE_TYPE_SNIFF_BYTES, 0);
|
|
10
|
+
if (bytesRead === 0) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const fileType = await fileTypeFromBuffer(buffer.subarray(0, bytesRead));
|
|
14
|
+
if (!fileType) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
if (!IMAGE_MIME_TYPES.has(fileType.mime)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return fileType.mime;
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
await fileHandle.close();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=mime.js.map
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Photon image processing wrapper.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a unified interface to @silvia-odwyer/photon-node that works in:
|
|
5
|
+
* 1. Node.js (development, npm run build)
|
|
6
|
+
* 2. Bun compiled binaries (standalone distribution)
|
|
7
|
+
*
|
|
8
|
+
* The challenge: photon-node's CJS entry uses fs.readFileSync(__dirname + '/photon_rs_bg.wasm')
|
|
9
|
+
* which bakes the build machine's absolute path into Bun compiled binaries.
|
|
10
|
+
*
|
|
11
|
+
* Solution:
|
|
12
|
+
* 1. Patch fs.readFileSync to redirect missing photon_rs_bg.wasm reads
|
|
13
|
+
* 2. Copy photon_rs_bg.wasm next to the executable in build:binary
|
|
14
|
+
*/
|
|
15
|
+
import { createRequire } from 'module';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import { fileURLToPath } from 'url';
|
|
18
|
+
const require = createRequire(import.meta.url);
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const WASM_FILENAME = 'photon_rs_bg.wasm';
|
|
21
|
+
// Lazy-loaded photon module
|
|
22
|
+
let photonModule = null;
|
|
23
|
+
let loadPromise = null;
|
|
24
|
+
function pathOrNull(file) {
|
|
25
|
+
if (typeof file === 'string') {
|
|
26
|
+
return file;
|
|
27
|
+
}
|
|
28
|
+
if (file instanceof URL) {
|
|
29
|
+
return fileURLToPath(file);
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
function getFallbackWasmPaths() {
|
|
34
|
+
const execDir = path.dirname(process.execPath);
|
|
35
|
+
return [
|
|
36
|
+
path.join(execDir, WASM_FILENAME),
|
|
37
|
+
path.join(execDir, 'photon', WASM_FILENAME),
|
|
38
|
+
path.join(process.cwd(), WASM_FILENAME)
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
function patchPhotonWasmRead() {
|
|
42
|
+
const originalReadFileSync = fs.readFileSync.bind(fs);
|
|
43
|
+
const fallbackPaths = getFallbackWasmPaths();
|
|
44
|
+
const mutableFs = fs;
|
|
45
|
+
const patchedReadFileSync = ((...args) => {
|
|
46
|
+
const [file, options] = args;
|
|
47
|
+
const resolvedPath = pathOrNull(file);
|
|
48
|
+
if (resolvedPath?.endsWith(WASM_FILENAME)) {
|
|
49
|
+
try {
|
|
50
|
+
return originalReadFileSync(...args);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
const err = error;
|
|
54
|
+
if (err?.code && err.code !== 'ENOENT') {
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
for (const fallbackPath of fallbackPaths) {
|
|
58
|
+
if (!fs.existsSync(fallbackPath)) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (options === undefined) {
|
|
62
|
+
return originalReadFileSync(fallbackPath);
|
|
63
|
+
}
|
|
64
|
+
return originalReadFileSync(fallbackPath, options);
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return originalReadFileSync(...args);
|
|
70
|
+
});
|
|
71
|
+
try {
|
|
72
|
+
mutableFs.readFileSync = patchedReadFileSync;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
Object.defineProperty(fs, 'readFileSync', {
|
|
76
|
+
value: patchedReadFileSync,
|
|
77
|
+
writable: true,
|
|
78
|
+
configurable: true
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return () => {
|
|
82
|
+
try {
|
|
83
|
+
mutableFs.readFileSync = originalReadFileSync;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
Object.defineProperty(fs, 'readFileSync', {
|
|
87
|
+
value: originalReadFileSync,
|
|
88
|
+
writable: true,
|
|
89
|
+
configurable: true
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Load the photon module asynchronously.
|
|
96
|
+
* Returns cached module on subsequent calls.
|
|
97
|
+
*/
|
|
98
|
+
export async function loadPhoton() {
|
|
99
|
+
if (photonModule) {
|
|
100
|
+
return photonModule;
|
|
101
|
+
}
|
|
102
|
+
if (loadPromise) {
|
|
103
|
+
return loadPromise;
|
|
104
|
+
}
|
|
105
|
+
loadPromise = (async () => {
|
|
106
|
+
const restoreReadFileSync = patchPhotonWasmRead();
|
|
107
|
+
try {
|
|
108
|
+
photonModule = await import('@silvia-odwyer/photon-node');
|
|
109
|
+
return photonModule;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
photonModule = null;
|
|
113
|
+
return photonModule;
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
restoreReadFileSync();
|
|
117
|
+
}
|
|
118
|
+
})();
|
|
119
|
+
return loadPromise;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=photon.js.map
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { spawn, spawnSync } from 'child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { delimiter } from 'node:path';
|
|
4
|
+
import { getBinDir, getSettingsPath } from '../config.js';
|
|
5
|
+
let cachedShellConfig = null;
|
|
6
|
+
/**
|
|
7
|
+
* Clear cached shell config so the next getShellConfig() call re-detects.
|
|
8
|
+
* Call after installing Git so subsequent commands use the new bash.
|
|
9
|
+
*/
|
|
10
|
+
export function resetShellConfig() {
|
|
11
|
+
cachedShellConfig = null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Find bash executable on PATH (cross-platform)
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Verify a bash executable actually works by running a simple command.
|
|
18
|
+
* Guards against WSL stubs that fall back to cmd.exe when WSL isn't configured.
|
|
19
|
+
*/
|
|
20
|
+
function isBashWorking(bashPath) {
|
|
21
|
+
try {
|
|
22
|
+
const result = spawnSync(bashPath, ['-c', 'echo __bash_ok__'], {
|
|
23
|
+
encoding: 'utf-8',
|
|
24
|
+
timeout: 5000,
|
|
25
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
26
|
+
});
|
|
27
|
+
return result.status === 0 && result.stdout.includes('__bash_ok__');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function findBashOnPath() {
|
|
34
|
+
if (process.platform === 'win32') {
|
|
35
|
+
// Windows: Use 'where' and verify file exists (where can return non-existent paths)
|
|
36
|
+
try {
|
|
37
|
+
const result = spawnSync('where', ['bash.exe'], { encoding: 'utf-8', timeout: 5000 });
|
|
38
|
+
if (result.status === 0 && result.stdout) {
|
|
39
|
+
// Try each match — skip WSL stubs and broken installs
|
|
40
|
+
for (const match of result.stdout.trim().split(/\r?\n/)) {
|
|
41
|
+
const trimmed = match.trim();
|
|
42
|
+
if (trimmed && existsSync(trimmed) && isBashWorking(trimmed)) {
|
|
43
|
+
return trimmed;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Ignore errors
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
// Unix: Use 'which' and trust its output (handles Termux and special filesystems)
|
|
54
|
+
try {
|
|
55
|
+
const result = spawnSync('which', ['bash'], { encoding: 'utf-8', timeout: 5000 });
|
|
56
|
+
if (result.status === 0 && result.stdout) {
|
|
57
|
+
const firstMatch = result.stdout.trim().split(/\r?\n/)[0];
|
|
58
|
+
if (firstMatch) {
|
|
59
|
+
return firstMatch;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Ignore errors
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get shell configuration based on platform.
|
|
70
|
+
* Resolution order:
|
|
71
|
+
* 1. User-specified shellPath in settings.json
|
|
72
|
+
* 2. On Windows: Git Bash in known locations, then bash on PATH
|
|
73
|
+
* 3. On Unix: /bin/bash, then bash on PATH, then fallback to sh
|
|
74
|
+
*/
|
|
75
|
+
export function getShellConfig(customShellPath) {
|
|
76
|
+
if (cachedShellConfig) {
|
|
77
|
+
return cachedShellConfig;
|
|
78
|
+
}
|
|
79
|
+
// 1. Check user-specified shell path
|
|
80
|
+
if (customShellPath) {
|
|
81
|
+
// Reject cmd.exe / powershell — the bash tool requires a POSIX shell
|
|
82
|
+
const shellBasename = customShellPath.replace(/.*[/\\]/, '').toLowerCase();
|
|
83
|
+
if (['cmd.exe', 'cmd', 'powershell.exe', 'powershell', 'pwsh.exe', 'pwsh'].includes(shellBasename)) {
|
|
84
|
+
throw new Error(`shellPath is set to "${customShellPath}" but Shortcut requires a bash-compatible shell.\n` +
|
|
85
|
+
`Please remove shellPath from ${getSettingsPath()} or set it to a bash executable.\n` +
|
|
86
|
+
`Install Git for Windows if needed: https://git-scm.com/download/win`);
|
|
87
|
+
}
|
|
88
|
+
if (existsSync(customShellPath)) {
|
|
89
|
+
cachedShellConfig = { shell: customShellPath, args: ['-c'] };
|
|
90
|
+
return cachedShellConfig;
|
|
91
|
+
}
|
|
92
|
+
throw new Error(`Custom shell path not found: ${customShellPath}\nPlease update shellPath in ${getSettingsPath()}`);
|
|
93
|
+
}
|
|
94
|
+
if (process.platform === 'win32') {
|
|
95
|
+
// 2. Try Git Bash in known locations
|
|
96
|
+
const paths = [];
|
|
97
|
+
const programFiles = process.env.ProgramFiles;
|
|
98
|
+
if (programFiles) {
|
|
99
|
+
paths.push(`${programFiles}\\Git\\bin\\bash.exe`);
|
|
100
|
+
}
|
|
101
|
+
const programFilesX86 = process.env['ProgramFiles(x86)'];
|
|
102
|
+
if (programFilesX86) {
|
|
103
|
+
paths.push(`${programFilesX86}\\Git\\bin\\bash.exe`);
|
|
104
|
+
}
|
|
105
|
+
for (const path of paths) {
|
|
106
|
+
if (existsSync(path)) {
|
|
107
|
+
cachedShellConfig = { shell: path, args: ['-c'] };
|
|
108
|
+
return cachedShellConfig;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// 3. Fallback: search bash.exe on PATH (Cygwin, MSYS2, WSL, etc.)
|
|
112
|
+
const bashOnPath = findBashOnPath();
|
|
113
|
+
if (bashOnPath) {
|
|
114
|
+
cachedShellConfig = { shell: bashOnPath, args: ['-c'] };
|
|
115
|
+
return cachedShellConfig;
|
|
116
|
+
}
|
|
117
|
+
// 4. No bash at all — fall back to cmd.exe for bootstrapping.
|
|
118
|
+
// Do NOT cache this result so we auto-detect bash once Git is installed.
|
|
119
|
+
return { shell: 'cmd.exe', args: ['/c'] };
|
|
120
|
+
}
|
|
121
|
+
// Unix: try /bin/bash, then bash on PATH, then fallback to sh
|
|
122
|
+
if (existsSync('/bin/bash')) {
|
|
123
|
+
cachedShellConfig = { shell: '/bin/bash', args: ['-c'] };
|
|
124
|
+
return cachedShellConfig;
|
|
125
|
+
}
|
|
126
|
+
const bashOnPath = findBashOnPath();
|
|
127
|
+
if (bashOnPath) {
|
|
128
|
+
cachedShellConfig = { shell: bashOnPath, args: ['-c'] };
|
|
129
|
+
return cachedShellConfig;
|
|
130
|
+
}
|
|
131
|
+
cachedShellConfig = { shell: 'sh', args: ['-c'] };
|
|
132
|
+
return cachedShellConfig;
|
|
133
|
+
}
|
|
134
|
+
export function getShellEnv() {
|
|
135
|
+
const binDir = getBinDir();
|
|
136
|
+
const pathKey = Object.keys(process.env).find((key) => key.toLowerCase() === 'path') ?? 'PATH';
|
|
137
|
+
const currentPath = process.env[pathKey] ?? '';
|
|
138
|
+
const pathEntries = currentPath.split(delimiter).filter(Boolean);
|
|
139
|
+
const hasBinDir = pathEntries.includes(binDir);
|
|
140
|
+
const updatedPath = hasBinDir
|
|
141
|
+
? currentPath
|
|
142
|
+
: [binDir, currentPath].filter(Boolean).join(delimiter);
|
|
143
|
+
return {
|
|
144
|
+
...process.env,
|
|
145
|
+
[pathKey]: updatedPath
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Sanitize binary output for display/storage.
|
|
150
|
+
* Removes characters that crash string-width or cause display issues:
|
|
151
|
+
* - Control characters (except tab, newline, carriage return)
|
|
152
|
+
* - Lone surrogates
|
|
153
|
+
* - Unicode Format characters (crash string-width due to a bug)
|
|
154
|
+
* - Characters with undefined code points
|
|
155
|
+
*/
|
|
156
|
+
export function sanitizeBinaryOutput(str) {
|
|
157
|
+
// Use Array.from to properly iterate over code points (not code units)
|
|
158
|
+
// This handles surrogate pairs correctly and catches edge cases where
|
|
159
|
+
// codePointAt() might return undefined
|
|
160
|
+
return Array.from(str)
|
|
161
|
+
.filter((char) => {
|
|
162
|
+
// Filter out characters that cause string-width to crash
|
|
163
|
+
// This includes:
|
|
164
|
+
// - Unicode format characters
|
|
165
|
+
// - Lone surrogates (already filtered by Array.from)
|
|
166
|
+
// - Control chars except \t \n \r
|
|
167
|
+
// - Characters with undefined code points
|
|
168
|
+
const code = char.codePointAt(0);
|
|
169
|
+
// Skip if code point is undefined (edge case with invalid strings)
|
|
170
|
+
if (code === undefined)
|
|
171
|
+
return false;
|
|
172
|
+
// Allow tab, newline, carriage return
|
|
173
|
+
if (code === 0x09 || code === 0x0a || code === 0x0d)
|
|
174
|
+
return true;
|
|
175
|
+
// Filter out control characters (0x00-0x1F, except 0x09, 0x0a, 0x0x0d)
|
|
176
|
+
if (code <= 0x1f)
|
|
177
|
+
return false;
|
|
178
|
+
// Filter out Unicode format characters
|
|
179
|
+
if (code >= 0xfff9 && code <= 0xfffb)
|
|
180
|
+
return false;
|
|
181
|
+
return true;
|
|
182
|
+
})
|
|
183
|
+
.join('');
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Kill a process and all its children (cross-platform)
|
|
187
|
+
*/
|
|
188
|
+
export function killProcessTree(pid) {
|
|
189
|
+
if (process.platform === 'win32') {
|
|
190
|
+
// Use taskkill on Windows to kill process tree
|
|
191
|
+
try {
|
|
192
|
+
spawn('taskkill', ['/F', '/T', '/PID', String(pid)], {
|
|
193
|
+
stdio: 'ignore',
|
|
194
|
+
detached: true
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// Ignore errors if taskkill fails
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// Use SIGKILL on Unix/Linux/Mac
|
|
203
|
+
try {
|
|
204
|
+
process.kill(-pid, 'SIGKILL');
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// Fallback to killing just the child if process group kill fails
|
|
208
|
+
try {
|
|
209
|
+
process.kill(pid, 'SIGKILL');
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Process already dead
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=shell.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sleep helper that respects abort signal.
|
|
3
|
+
*/
|
|
4
|
+
export function sleep(ms, signal) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
if (signal?.aborted) {
|
|
7
|
+
reject(new Error('Aborted'));
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const timeout = setTimeout(resolve, ms);
|
|
11
|
+
signal?.addEventListener('abort', () => {
|
|
12
|
+
clearTimeout(timeout);
|
|
13
|
+
reject(new Error('Aborted'));
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=sleep.js.map
|