@oh-my-pi/pi-coding-agent 6.9.69 → 8.0.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/CHANGELOG.md +219 -51
- package/README.md +1 -1
- package/docs/hooks.md +2 -2
- package/docs/sdk.md +1 -1
- package/examples/sdk/04-skills.ts +1 -1
- package/package.json +10 -10
- package/scripts/format-prompts.ts +143 -0
- package/scripts/generate-template.ts +1 -1
- package/src/cli/args.ts +3 -3
- package/src/cli/config-cli.ts +4 -4
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/list-models.ts +2 -2
- package/src/cli/plugin-cli.ts +3 -3
- package/src/cli/session-picker.ts +2 -2
- package/src/cli/setup-cli.ts +2 -2
- package/src/cli/stats-cli.ts +1 -1
- package/src/cli/update-cli.ts +2 -2
- package/src/{core → config}/keybindings.ts +1 -1
- package/src/{core → config}/model-registry.ts +8 -1
- package/src/{core → config}/model-resolver.ts +3 -3
- package/src/{core → config}/prompt-templates.ts +2 -2
- package/src/{core → config}/settings-manager.ts +6 -6
- package/src/{core/cursor/exec-bridge.ts → cursor.ts} +4 -4
- package/src/discovery/agents-md.ts +4 -4
- package/src/discovery/builtin.ts +17 -17
- package/src/discovery/claude.ts +12 -12
- package/src/discovery/cline.ts +6 -6
- package/src/discovery/codex.ts +21 -21
- package/src/discovery/cursor.ts +9 -9
- package/src/discovery/gemini.ts +9 -9
- package/src/discovery/github.ts +6 -6
- package/src/discovery/helpers.ts +4 -4
- package/src/discovery/index.ts +16 -16
- package/src/discovery/mcp-json.ts +4 -4
- package/src/discovery/ssh.ts +4 -4
- package/src/discovery/vscode.ts +4 -4
- package/src/discovery/windsurf.ts +6 -6
- package/src/{core/tools/exa → exa}/company.ts +2 -3
- package/src/{core/tools/exa → exa}/index.ts +2 -2
- package/src/{core/tools/exa → exa}/linkedin.ts +2 -3
- package/src/{core/tools/exa → exa}/mcp-client.ts +2 -2
- package/src/{core/tools/exa → exa}/render.ts +3 -3
- package/src/{core/tools/exa → exa}/researcher.ts +1 -1
- package/src/{core/tools/exa → exa}/search.ts +2 -10
- package/src/{core/tools/exa → exa}/websets.ts +1 -1
- package/src/{core → exec}/bash-executor.ts +23 -7
- package/src/{core → export}/custom-share.ts +1 -1
- package/src/{core/export-html → export/html}/index.ts +3 -3
- package/src/{core → export}/ttsr.ts +2 -2
- package/src/{core → extensibility}/custom-commands/bundled/review/index.ts +4 -4
- package/src/{core → extensibility}/custom-commands/loader.ts +3 -3
- package/src/{core → extensibility}/custom-commands/types.ts +1 -1
- package/src/{core → extensibility}/custom-tools/loader.ts +9 -9
- package/src/{core → extensibility}/custom-tools/types.ts +6 -6
- package/src/{core → extensibility}/custom-tools/wrapper.ts +1 -1
- package/src/{core → extensibility}/extensions/loader.ts +8 -8
- package/src/{core → extensibility}/extensions/runner.ts +3 -3
- package/src/{core → extensibility}/extensions/types.ts +15 -15
- package/src/{core → extensibility}/extensions/wrapper.ts +1 -1
- package/src/{core → extensibility}/hooks/index.ts +1 -1
- package/src/{core → extensibility}/hooks/loader.ts +7 -7
- package/src/{core → extensibility}/hooks/runner.ts +4 -4
- package/src/{core → extensibility}/hooks/types.ts +9 -9
- package/src/{core → extensibility}/plugins/doctor.ts +1 -1
- package/src/{core → extensibility}/plugins/installer.ts +2 -3
- package/src/{core → extensibility}/plugins/paths.ts +1 -1
- package/src/{core → extensibility}/skills.ts +8 -48
- package/src/{core → extensibility}/slash-commands.ts +6 -6
- package/src/index.ts +127 -128
- package/src/internal-urls/agent-protocol.ts +126 -0
- package/src/internal-urls/artifact-protocol.ts +93 -0
- package/src/internal-urls/index.ts +28 -0
- package/src/internal-urls/json-query.ts +126 -0
- package/src/internal-urls/router.ts +69 -0
- package/src/internal-urls/rule-protocol.ts +56 -0
- package/src/internal-urls/skill-protocol.ts +112 -0
- package/src/internal-urls/types.ts +48 -0
- package/src/{core/python-executor.ts → ipy/executor.ts} +74 -13
- package/src/{core/python-gateway-coordinator.ts → ipy/gateway-coordinator.ts} +40 -270
- package/src/{core/python-kernel.ts → ipy/kernel.ts} +38 -10
- package/src/{core/python-prelude.py → ipy/prelude.py} +201 -8
- package/src/ipy/prelude.ts +3 -0
- package/src/{core/tools/lsp → lsp}/client.ts +7 -6
- package/src/{core/tools/lsp → lsp}/clients/biome-client.ts +1 -1
- package/src/{core/tools/lsp → lsp}/clients/index.ts +1 -1
- package/src/{core/tools/lsp → lsp}/clients/lsp-linter-client.ts +4 -4
- package/src/{core/tools/lsp → lsp}/config.ts +1 -1
- package/src/{core/tools/lsp → lsp}/index.ts +29 -17
- package/src/{core/tools/lsp → lsp}/render.ts +2 -2
- package/src/{core/tools/lsp → lsp}/types.ts +14 -16
- package/src/{core/tools/lsp → lsp}/utils.ts +1 -1
- package/src/main.ts +12 -12
- package/src/{core/mcp → mcp}/config.ts +8 -8
- package/src/{core/mcp → mcp}/loader.ts +5 -6
- package/src/{core/mcp → mcp}/manager.ts +2 -2
- package/src/{core/mcp → mcp}/tool-bridge.ts +35 -6
- package/src/{core/mcp → mcp}/tool-cache.ts +1 -1
- package/src/{core/mcp → mcp}/transports/http.ts +7 -1
- package/src/{core/mcp → mcp}/transports/stdio.ts +1 -1
- package/src/{core/mcp → mcp}/types.ts +1 -1
- package/src/migrations.ts +2 -2
- package/src/modes/{interactive/components → components}/armin.ts +1 -1
- package/src/modes/{interactive/components → components}/assistant-message.ts +1 -1
- package/src/modes/{interactive/components → components}/bash-execution.ts +37 -29
- package/src/modes/{interactive/components → components}/bordered-loader.ts +1 -1
- package/src/modes/{interactive/components → components}/branch-summary-message.ts +2 -2
- package/src/modes/{interactive/components → components}/compaction-summary-message.ts +2 -2
- package/src/modes/{interactive/components → components}/custom-message.ts +3 -3
- package/src/modes/{interactive/components → components}/diff.ts +1 -1
- package/src/modes/{interactive/components → components}/dynamic-border.ts +1 -1
- package/src/modes/{interactive/components → components}/extensions/extension-dashboard.ts +3 -3
- package/src/modes/{interactive/components → components}/extensions/extension-list.ts +2 -2
- package/src/modes/{interactive/components → components}/extensions/inspector-panel.ts +1 -1
- package/src/modes/{interactive/components → components}/extensions/state-manager.ts +11 -17
- package/src/modes/{interactive/components → components}/extensions/types.ts +1 -1
- package/src/modes/{interactive/components → components}/footer.ts +3 -3
- package/src/modes/{interactive/components → components}/history-search.ts +2 -2
- package/src/modes/{interactive/components → components}/hook-editor.ts +1 -1
- package/src/modes/{interactive/components → components}/hook-input.ts +1 -1
- package/src/modes/{interactive/components → components}/hook-message.ts +3 -3
- package/src/modes/{interactive/components → components}/hook-selector.ts +1 -1
- package/src/modes/{interactive/components → components}/keybinding-hints.ts +2 -2
- package/src/modes/{interactive/components → components}/login-dialog.ts +1 -1
- package/src/modes/{interactive/components → components}/model-selector.ts +5 -5
- package/src/modes/{interactive/components → components}/oauth-selector.ts +2 -2
- package/src/modes/{interactive/components → components}/plugin-settings.ts +3 -3
- package/src/modes/{interactive/components → components}/python-execution.ts +35 -24
- package/src/modes/{interactive/components → components}/queue-mode-selector.ts +1 -1
- package/src/modes/{interactive/components → components}/read-tool-group.ts +2 -2
- package/src/modes/{interactive/components → components}/session-selector.ts +3 -3
- package/src/modes/{interactive/components → components}/settings-defs.ts +2 -2
- package/src/modes/{interactive/components → components}/settings-selector.ts +2 -2
- package/src/modes/{interactive/components → components}/show-images-selector.ts +1 -1
- package/src/modes/{interactive/components → components}/status-line/segments.ts +2 -2
- package/src/modes/{interactive/components → components}/status-line/separators.ts +1 -1
- package/src/modes/{interactive/components → components}/status-line/types.ts +2 -2
- package/src/modes/{interactive/components → components}/status-line-segment-editor.ts +2 -2
- package/src/modes/{interactive/components → components}/status-line.ts +3 -3
- package/src/modes/{interactive/components → components}/theme-selector.ts +1 -1
- package/src/modes/{interactive/components → components}/thinking-selector.ts +1 -1
- package/src/modes/{interactive/components → components}/todo-display.ts +3 -4
- package/src/modes/{interactive/components → components}/todo-reminder.ts +2 -2
- package/src/modes/{interactive/components → components}/tool-execution.ts +8 -8
- package/src/modes/{interactive/components → components}/tree-selector.ts +3 -3
- package/src/modes/{interactive/components → components}/ttsr-notification.ts +2 -2
- package/src/modes/{interactive/components → components}/user-message-selector.ts +1 -1
- package/src/modes/{interactive/components → components}/user-message.ts +1 -1
- package/src/modes/{interactive/components → components}/welcome.ts +2 -2
- package/src/modes/{interactive/controllers → controllers}/command-controller.ts +381 -30
- package/src/modes/{interactive/controllers → controllers}/event-controller.ts +9 -9
- package/src/modes/{interactive/controllers → controllers}/extension-ui-controller.ts +8 -8
- package/src/modes/{interactive/controllers → controllers}/input-controller.ts +61 -13
- package/src/modes/{interactive/controllers → controllers}/selector-controller.ts +16 -16
- package/src/modes/index.ts +1 -1
- package/src/modes/{interactive/interactive-mode.ts → interactive-mode.ts} +20 -15
- package/src/modes/print-mode.ts +1 -1
- package/src/modes/rpc/rpc-client.ts +3 -3
- package/src/modes/rpc/rpc-mode.ts +3 -3
- package/src/modes/rpc/rpc-types.ts +3 -3
- package/src/modes/{interactive/theme → theme}/theme.ts +1 -1
- package/src/modes/{interactive/types.ts → types.ts} +10 -10
- package/src/modes/{interactive/utils → utils}/ui-helpers.ts +20 -27
- package/src/{core/tools/patch → patch}/applicator.ts +1 -1
- package/src/{core/tools/patch → patch}/diff.ts +1 -1
- package/src/{core/tools/patch → patch}/index.ts +31 -36
- package/src/{core/tools/patch → patch}/shared.ts +9 -6
- package/src/prompts/agents/explore.md +83 -46
- package/src/prompts/agents/init.md +9 -4
- package/src/prompts/agents/plan.md +8 -7
- package/src/prompts/agents/reviewer.md +36 -18
- package/src/prompts/agents/task.md +4 -4
- package/src/prompts/compaction/branch-summary-preamble.md +0 -1
- package/src/prompts/review-request.md +0 -1
- package/src/prompts/system/custom-system-prompt.md +2 -14
- package/src/prompts/system/file-operations.md +0 -2
- package/src/prompts/system/system-prompt.md +182 -171
- package/src/prompts/system/web-search.md +26 -0
- package/src/prompts/tools/ask.md +31 -24
- package/src/prompts/tools/bash.md +20 -17
- package/src/prompts/tools/calculator.md +9 -5
- package/src/prompts/tools/fetch.md +16 -0
- package/src/prompts/tools/find.md +15 -5
- package/src/prompts/tools/gemini-image.md +21 -6
- package/src/prompts/tools/grep.md +28 -12
- package/src/prompts/tools/lsp.md +35 -14
- package/src/prompts/tools/patch.md +39 -41
- package/src/prompts/tools/python.md +59 -77
- package/src/prompts/tools/read.md +23 -22
- package/src/prompts/tools/replace.md +19 -12
- package/src/prompts/tools/ssh.md +21 -28
- package/src/prompts/tools/task.md +54 -44
- package/src/prompts/tools/todo-write.md +52 -163
- package/src/prompts/tools/web-search.md +16 -9
- package/src/prompts/tools/write.md +13 -2
- package/src/{core/sdk.ts → sdk.ts} +65 -34
- package/src/{core → session}/agent-session.ts +157 -41
- package/src/{core → session}/agent-storage.ts +2 -2
- package/src/session/artifacts.ts +110 -0
- package/src/{core → session}/auth-storage.ts +525 -203
- package/src/{core → session}/compaction/branch-summarization.ts +5 -5
- package/src/{core → session}/compaction/compaction.ts +6 -6
- package/src/{core → session}/compaction/utils.ts +3 -3
- package/src/{core → session}/history-storage.ts +1 -1
- package/src/{core → session}/messages.ts +6 -8
- package/src/{core → session}/session-manager.ts +2 -2
- package/src/{core → session}/storage-migration.ts +2 -2
- package/src/session/streaming-output.ts +177 -0
- package/src/{core/ssh → ssh}/connection-manager.ts +1 -1
- package/src/{core/ssh → ssh}/ssh-executor.ts +19 -4
- package/src/{core/ssh → ssh}/sshfs-mount.ts +1 -1
- package/src/{core/system-prompt.ts → system-prompt.ts} +8 -37
- package/src/{core/tools/task → task}/agents.ts +8 -8
- package/src/{core/tools/task → task}/commands.ts +5 -6
- package/src/{core/tools/task → task}/discovery.ts +3 -3
- package/src/{core/tools/task → task}/executor.ts +34 -44
- package/src/{core/tools/task → task}/index.ts +206 -50
- package/src/{core/tools/task → task}/render.ts +80 -23
- package/src/{core/tools/task → task}/subprocess-tool-registry.ts +1 -1
- package/src/task/template.ts +47 -0
- package/src/{core/tools/task → task}/types.ts +19 -27
- package/src/{core/tools/task → task}/worker-protocol.ts +8 -4
- package/src/{core/tools/task → task}/worker.ts +34 -29
- package/src/task/worktree.ts +166 -0
- package/src/{core/tools → tools}/ask.ts +13 -21
- package/src/{core/tools → tools}/bash-interceptor.ts +1 -1
- package/src/{core/tools → tools}/bash.ts +61 -63
- package/src/{core/tools → tools}/calculator.ts +4 -4
- package/src/{core/tools → tools}/complete.ts +1 -1
- package/src/{core/tools → tools}/context.ts +2 -2
- package/src/{core/tools/web-fetch.ts → tools/fetch.ts} +97 -76
- package/src/{core/tools → tools}/find.ts +96 -107
- package/src/{core/tools → tools}/gemini-image.ts +420 -29
- package/src/{core/tools → tools}/grep.ts +155 -164
- package/src/{core/tools → tools}/index.ts +63 -56
- package/src/tools/list-limit.ts +40 -0
- package/src/{core/tools → tools}/ls.ts +44 -35
- package/src/{core/tools → tools}/notebook.ts +3 -3
- package/src/tools/output-meta.ts +443 -0
- package/src/tools/output-utils.ts +63 -0
- package/src/{core/tools → tools}/python.ts +106 -89
- package/src/tools/read.ts +882 -0
- package/src/{core/tools → tools}/render-utils.ts +1 -1
- package/src/{core/tools → tools}/renderers.ts +8 -10
- package/src/{core/tools → tools}/review.ts +2 -2
- package/src/{core/tools → tools}/ssh.ts +56 -59
- package/src/{core/tools → tools}/todo-write.ts +12 -23
- package/src/tools/tool-errors.ts +95 -0
- package/src/tools/tool-result.ts +92 -0
- package/src/{core/tools → tools}/truncate.ts +2 -2
- package/src/{core/tools → tools}/write.ts +15 -13
- package/src/utils/changelog.ts +1 -1
- package/src/{core → utils}/file-mentions.ts +4 -4
- package/src/utils/image-convert.ts +1 -1
- package/src/utils/image-resize.ts +1 -1
- package/src/utils/shell.ts +1 -1
- package/src/{core → utils}/title-generator.ts +4 -4
- package/src/utils/tools-manager.ts +1 -1
- package/src/{core/tools/web-scrapers → web/scrapers}/choosealicense.ts +1 -1
- package/src/{core/tools/web-scrapers → web/scrapers}/twitter.ts +3 -2
- package/src/{core/tools/web-scrapers → web/scrapers}/types.ts +4 -2
- package/src/{core/tools/web-scrapers → web/scrapers}/utils.ts +1 -1
- package/src/{core/tools/web-scrapers → web/scrapers}/youtube.ts +14 -13
- package/src/{core/tools/web-search → web/search}/auth.ts +4 -4
- package/src/{core/tools/web-search → web/search}/index.ts +22 -71
- package/src/{core/tools/web-search → web/search}/providers/anthropic.ts +7 -10
- package/src/{core/tools/web-search → web/search}/providers/exa.ts +2 -2
- package/src/{core/tools/web-search → web/search}/providers/perplexity.ts +4 -16
- package/src/{core/tools/web-search → web/search}/render.ts +3 -3
- package/scripts/migrate-sessions.sh +0 -93
- package/src/core/index.ts +0 -56
- package/src/core/python-prelude.ts +0 -3
- package/src/core/ssh-executor.ts +0 -5
- package/src/core/streaming-output.ts +0 -115
- package/src/core/tools/output.ts +0 -519
- package/src/core/tools/read.ts +0 -717
- package/src/core/tools/task/template.ts +0 -37
- package/src/prompts/tools/output.md +0 -47
- package/src/prompts/tools/web-fetch.md +0 -9
- /package/src/{core/tools/exa → exa}/types.ts +0 -0
- /package/src/{core → exec}/exec.ts +0 -0
- /package/src/{core/export-html → export/html}/template.css +0 -0
- /package/src/{core/export-html → export/html}/template.generated.ts +0 -0
- /package/src/{core/export-html → export/html}/template.html +0 -0
- /package/src/{core/export-html → export/html}/template.js +0 -0
- /package/src/{core/export-html → export/html}/template.macro.ts +0 -0
- /package/src/{core/export-html → export/html}/vendor/highlight.min.js +0 -0
- /package/src/{core/export-html → export/html}/vendor/marked.min.js +0 -0
- /package/src/{core → extensibility}/custom-commands/index.ts +0 -0
- /package/src/{core → extensibility}/custom-tools/index.ts +0 -0
- /package/src/{core → extensibility}/extensions/index.ts +0 -0
- /package/src/{core → extensibility}/hooks/tool-wrapper.ts +0 -0
- /package/src/{core → extensibility}/plugins/index.ts +0 -0
- /package/src/{core → extensibility}/plugins/loader.ts +0 -0
- /package/src/{core → extensibility}/plugins/manager.ts +0 -0
- /package/src/{core → extensibility}/plugins/parser.ts +0 -0
- /package/src/{core → extensibility}/plugins/types.ts +0 -0
- /package/src/{core/python-modules.ts → ipy/modules.ts} +0 -0
- /package/src/{core/tools/lsp → lsp}/defaults.json +0 -0
- /package/src/{core/tools/lsp → lsp}/edits.ts +0 -0
- /package/src/{core/tools/lsp → lsp}/lspmux.ts +0 -0
- /package/src/{core/tools/lsp → lsp}/rust-analyzer.ts +0 -0
- /package/src/{core/mcp → mcp}/client.ts +0 -0
- /package/src/{core/mcp → mcp}/index.ts +0 -0
- /package/src/{core/mcp → mcp}/json-rpc.ts +0 -0
- /package/src/{core/mcp → mcp}/transports/index.ts +0 -0
- /package/src/modes/{interactive/components → components}/countdown-timer.ts +0 -0
- /package/src/modes/{interactive/components → components}/custom-editor.ts +0 -0
- /package/src/modes/{interactive/components → components}/extensions/index.ts +0 -0
- /package/src/modes/{interactive/components → components}/index.ts +0 -0
- /package/src/modes/{interactive/components → components}/status-line/index.ts +0 -0
- /package/src/modes/{interactive/components → components}/status-line/presets.ts +0 -0
- /package/src/modes/{interactive/components → components}/visual-truncate.ts +0 -0
- /package/src/modes/{interactive/theme → theme}/dark.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/alabaster.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/amethyst.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/anthracite.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/basalt.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/birch.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-abyss.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-arctic.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-aurora.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-catppuccin.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-cavern.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-copper.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-cosmos.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-cyberpunk.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-dracula.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-eclipse.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-ember.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-equinox.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-forest.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-github.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-gruvbox.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-lavender.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-lunar.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-midnight.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-monochrome.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-monokai.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-nebula.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-nord.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-ocean.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-one.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-rainforest.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-reef.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-retro.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-rose-pine.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-sakura.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-slate.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-solarized.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-solstice.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-starfall.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-sunset.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-swamp.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-synthwave.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-taiga.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-terminal.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-tokyo-night.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-tundra.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-twilight.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-volcanic.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/graphite.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/index.ts +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-arctic.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-aurora-day.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-canyon.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-catppuccin.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-cirrus.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-coral.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-cyberpunk.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-dawn.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-dunes.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-eucalyptus.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-forest.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-frost.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-github.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-glacier.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-gruvbox.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-haze.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-honeycomb.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-lagoon.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-lavender.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-meadow.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-mint.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-monochrome.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-ocean.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-one.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-opal.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-orchard.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-paper.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-prism.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-retro.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-sand.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-savanna.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-solarized.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-soleil.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-sunset.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-synthwave.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-tokyo-night.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-wetland.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-zenith.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/limestone.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/mahogany.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/marble.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/obsidian.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/onyx.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/pearl.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/porcelain.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/quartz.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/sandstone.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/titanium.json +0 -0
- /package/src/modes/{interactive/theme → theme}/light.json +0 -0
- /package/src/modes/{interactive/theme → theme}/theme-schema.json +0 -0
- /package/src/{core/tools/patch → patch}/fuzzy.ts +0 -0
- /package/src/{core/tools/patch → patch}/normalize.ts +0 -0
- /package/src/{core/tools/patch → patch}/normative.ts +0 -0
- /package/src/{core/tools/patch → patch}/parser.ts +0 -0
- /package/src/{core/tools/patch → patch}/types.ts +0 -0
- /package/src/{core → session}/compaction/index.ts +0 -0
- /package/src/{core → session}/session-storage.ts +0 -0
- /package/src/{core/tools/task → task}/name-generator.ts +0 -0
- /package/src/{core/tools/task → task}/omp-command.ts +0 -0
- /package/src/{core/tools/task → task}/parallel.ts +0 -0
- /package/src/{core/tools → tools}/jtd-to-json-schema.ts +0 -0
- /package/src/{core/tools → tools}/path-utils.ts +0 -0
- /package/src/{core → utils}/event-bus.ts +0 -0
- /package/src/{core → utils}/frontmatter.ts +0 -0
- /package/src/{core → utils}/terminal-notify.ts +0 -0
- /package/src/{core → utils}/timings.ts +0 -0
- /package/src/{core → utils}/utils.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/artifacthub.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/arxiv.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/aur.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/biorxiv.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/bluesky.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/brew.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/cheatsh.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/chocolatey.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/cisa-kev.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/clojars.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/coingecko.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/crates-io.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/crossref.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/devto.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/discogs.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/discourse.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/dockerhub.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/fdroid.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/firefox-addons.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/flathub.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/github-gist.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/github.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/gitlab.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/go-pkg.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/hackage.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/hackernews.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/hex.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/huggingface.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/iacr.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/index.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/jetbrains-marketplace.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/lemmy.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/lobsters.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/mastodon.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/maven.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/mdn.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/metacpan.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/musicbrainz.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/npm.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/nuget.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/nvd.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/ollama.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/open-vsx.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/opencorporates.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/openlibrary.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/orcid.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/osv.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/packagist.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/pub-dev.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/pubmed.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/pypi.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/rawg.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/readthedocs.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/reddit.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/repology.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/rfc.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/rubygems.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/searchcode.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/sec-edgar.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/semantic-scholar.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/snapcraft.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/sourcegraph.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/spdx.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/spotify.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/stackoverflow.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/terraform.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/tldr.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/vimeo.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/vscode-marketplace.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/w3c.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/wikidata.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/wikipedia.ts +0 -0
- /package/src/{core/tools/web-search → web/search}/types.ts +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol handler for artifact:// URLs.
|
|
3
|
+
*
|
|
4
|
+
* Resolves artifact IDs to files in the session artifacts directory.
|
|
5
|
+
* Unlike agent://, artifacts are raw text with no JSON extraction.
|
|
6
|
+
*
|
|
7
|
+
* URL form:
|
|
8
|
+
* - artifact://<id> - Full artifact content
|
|
9
|
+
*
|
|
10
|
+
* Pagination is handled by the read tool via offset/limit parameters.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as fs from "node:fs";
|
|
14
|
+
import * as path from "node:path";
|
|
15
|
+
import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
|
|
16
|
+
|
|
17
|
+
export interface ArtifactProtocolOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Returns the artifacts directory path, or null if no session.
|
|
20
|
+
*/
|
|
21
|
+
getArtifactsDir: () => string | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* List available artifact IDs in the directory.
|
|
26
|
+
*/
|
|
27
|
+
function listAvailableArtifacts(artifactsDir: string): string[] {
|
|
28
|
+
try {
|
|
29
|
+
const files = fs.readdirSync(artifactsDir);
|
|
30
|
+
return files
|
|
31
|
+
.filter((f) => /^\d+\./.test(f))
|
|
32
|
+
.map((f) => f.split(".")[0])
|
|
33
|
+
.sort((a, b) => Number(a) - Number(b));
|
|
34
|
+
} catch {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Handler for artifact:// URLs.
|
|
41
|
+
*
|
|
42
|
+
* Resolves numeric artifact IDs to their text content.
|
|
43
|
+
* Artifacts are created by tools when output is truncated.
|
|
44
|
+
*/
|
|
45
|
+
export class ArtifactProtocolHandler implements ProtocolHandler {
|
|
46
|
+
readonly scheme = "artifact";
|
|
47
|
+
|
|
48
|
+
constructor(private readonly options: ArtifactProtocolOptions) {}
|
|
49
|
+
|
|
50
|
+
async resolve(url: InternalUrl): Promise<InternalResource> {
|
|
51
|
+
const artifactsDir = this.options.getArtifactsDir();
|
|
52
|
+
if (!artifactsDir) {
|
|
53
|
+
throw new Error("No session - artifacts unavailable");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Extract artifact ID from host
|
|
57
|
+
const id = url.rawHost || url.hostname;
|
|
58
|
+
if (!id) {
|
|
59
|
+
throw new Error("artifact:// URL requires a numeric ID: artifact://0");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Validate ID is numeric
|
|
63
|
+
if (!/^\d+$/.test(id)) {
|
|
64
|
+
throw new Error(`artifact:// ID must be numeric, got: ${id}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check directory exists
|
|
68
|
+
if (!fs.existsSync(artifactsDir)) {
|
|
69
|
+
throw new Error("No artifacts directory found");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Find file matching ID prefix
|
|
73
|
+
const files = fs.readdirSync(artifactsDir);
|
|
74
|
+
const match = files.find((f) => f.startsWith(`${id}.`));
|
|
75
|
+
|
|
76
|
+
if (!match) {
|
|
77
|
+
const available = listAvailableArtifacts(artifactsDir);
|
|
78
|
+
const availableStr = available.length > 0 ? available.join(", ") : "none";
|
|
79
|
+
throw new Error(`Artifact ${id} not found. Available: ${availableStr}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const filePath = path.join(artifactsDir, match);
|
|
83
|
+
const content = await Bun.file(filePath).text();
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
url: url.href,
|
|
87
|
+
content,
|
|
88
|
+
contentType: "text/plain",
|
|
89
|
+
size: Buffer.byteLength(content, "utf-8"),
|
|
90
|
+
sourcePath: filePath,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal URL routing system for agent:// and skill:// URLs.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a unified way to resolve internal URLs without
|
|
5
|
+
* exposing filesystem paths to the agent.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { InternalUrlRouter, AgentProtocolHandler, SkillProtocolHandler } from './internal-urls';
|
|
10
|
+
*
|
|
11
|
+
* const router = new InternalUrlRouter();
|
|
12
|
+
* router.register(new AgentProtocolHandler({ getArtifactsDir: () => sessionDir }));
|
|
13
|
+
* router.register(new SkillProtocolHandler({ getSkills: () => skills }));
|
|
14
|
+
*
|
|
15
|
+
* if (router.canHandle('agent://reviewer_0')) {
|
|
16
|
+
* const resource = await router.resolve('agent://reviewer_0');
|
|
17
|
+
* console.log(resource.content);
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export { AgentProtocolHandler, type AgentProtocolOptions } from "./agent-protocol";
|
|
23
|
+
export { ArtifactProtocolHandler, type ArtifactProtocolOptions } from "./artifact-protocol";
|
|
24
|
+
export { applyQuery, parseQuery, pathToQuery } from "./json-query";
|
|
25
|
+
export { InternalUrlRouter } from "./router";
|
|
26
|
+
export { RuleProtocolHandler, type RuleProtocolOptions } from "./rule-protocol";
|
|
27
|
+
export { SkillProtocolHandler, type SkillProtocolOptions } from "./skill-protocol";
|
|
28
|
+
export type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON query parser and executor for agent:// URL extraction.
|
|
3
|
+
*
|
|
4
|
+
* Supports jq-like syntax: .foo, [0], .foo.bar[0].baz, ["special-key"]
|
|
5
|
+
* Also supports path form: /foo/bar/0 -> .foo.bar[0]
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parse a jq-like query string into tokens.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* parseQuery(".foo.bar[0]") // ["foo", "bar", 0]
|
|
13
|
+
* parseQuery(".foo['special-key']") // ["foo", "special-key"]
|
|
14
|
+
*/
|
|
15
|
+
export function parseQuery(query: string): Array<string | number> {
|
|
16
|
+
let input = query.trim();
|
|
17
|
+
if (!input) return [];
|
|
18
|
+
if (input.startsWith(".")) input = input.slice(1);
|
|
19
|
+
if (!input) return [];
|
|
20
|
+
|
|
21
|
+
const tokens: Array<string | number> = [];
|
|
22
|
+
let i = 0;
|
|
23
|
+
|
|
24
|
+
const isIdentChar = (ch: string) => /[A-Za-z0-9_-]/.test(ch);
|
|
25
|
+
|
|
26
|
+
while (i < input.length) {
|
|
27
|
+
const ch = input[i];
|
|
28
|
+
if (ch === ".") {
|
|
29
|
+
i++;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (ch === "[") {
|
|
33
|
+
const closeIndex = input.indexOf("]", i + 1);
|
|
34
|
+
if (closeIndex === -1) {
|
|
35
|
+
throw new Error(`Invalid query: missing ] in ${query}`);
|
|
36
|
+
}
|
|
37
|
+
const raw = input.slice(i + 1, closeIndex).trim();
|
|
38
|
+
if (!raw) {
|
|
39
|
+
throw new Error(`Invalid query: empty [] in ${query}`);
|
|
40
|
+
}
|
|
41
|
+
const quote = raw[0];
|
|
42
|
+
if ((quote === '"' || quote === "'") && raw.endsWith(quote)) {
|
|
43
|
+
let inner = raw.slice(1, -1);
|
|
44
|
+
inner = inner.replace(/\\(["'\\])/g, "$1");
|
|
45
|
+
tokens.push(inner);
|
|
46
|
+
} else if (/^\d+$/.test(raw)) {
|
|
47
|
+
tokens.push(Number(raw));
|
|
48
|
+
} else {
|
|
49
|
+
tokens.push(raw);
|
|
50
|
+
}
|
|
51
|
+
i = closeIndex + 1;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const start = i;
|
|
56
|
+
while (i < input.length && isIdentChar(input[i])) {
|
|
57
|
+
i++;
|
|
58
|
+
}
|
|
59
|
+
if (start === i) {
|
|
60
|
+
throw new Error(`Invalid query: unexpected token '${input[i]}' in ${query}`);
|
|
61
|
+
}
|
|
62
|
+
const ident = input.slice(start, i);
|
|
63
|
+
tokens.push(ident);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return tokens;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Apply a parsed query to a JSON value.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* applyQuery({ foo: { bar: [1, 2, 3] } }, ".foo.bar[0]") // 1
|
|
74
|
+
*/
|
|
75
|
+
export function applyQuery(data: unknown, query: string): unknown {
|
|
76
|
+
const tokens = parseQuery(query);
|
|
77
|
+
let current: unknown = data;
|
|
78
|
+
for (const token of tokens) {
|
|
79
|
+
if (current === null || current === undefined) return undefined;
|
|
80
|
+
if (typeof token === "number") {
|
|
81
|
+
if (!Array.isArray(current)) return undefined;
|
|
82
|
+
current = current[token];
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (typeof current !== "object") return undefined;
|
|
86
|
+
const record = current as Record<string, unknown>;
|
|
87
|
+
current = record[token];
|
|
88
|
+
}
|
|
89
|
+
return current;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Convert a URL path form to a query string.
|
|
94
|
+
*
|
|
95
|
+
* Path form: /foo/bar/0 -> .foo.bar[0]
|
|
96
|
+
* Trailing slash is normalized (ignored).
|
|
97
|
+
*
|
|
98
|
+
* Segments that are not valid identifiers use bracket notation: ['segment']
|
|
99
|
+
*/
|
|
100
|
+
export function pathToQuery(urlPath: string): string {
|
|
101
|
+
if (!urlPath || urlPath === "/") return "";
|
|
102
|
+
|
|
103
|
+
const segments = urlPath.split("/").filter(Boolean);
|
|
104
|
+
if (segments.length === 0) return "";
|
|
105
|
+
|
|
106
|
+
const parts: string[] = [];
|
|
107
|
+
for (const segment of segments) {
|
|
108
|
+
let decoded = segment;
|
|
109
|
+
try {
|
|
110
|
+
decoded = decodeURIComponent(segment);
|
|
111
|
+
} catch {
|
|
112
|
+
decoded = segment;
|
|
113
|
+
}
|
|
114
|
+
const isIdentifier = /^[A-Za-z0-9_-]+$/.test(decoded);
|
|
115
|
+
if (/^\d+$/.test(decoded)) {
|
|
116
|
+
parts.push(`[${decoded}]`);
|
|
117
|
+
} else if (isIdentifier) {
|
|
118
|
+
parts.push(`.${decoded}`);
|
|
119
|
+
} else {
|
|
120
|
+
const escaped = decoded.replace(/\\/g, String.raw`\\`).replace(/'/g, String.raw`\'`);
|
|
121
|
+
parts.push(`['${escaped}']`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return parts.join("");
|
|
126
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal URL router for resolving agent:// and skill:// URLs.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Router for internal URL schemes.
|
|
9
|
+
*
|
|
10
|
+
* Dispatches URLs like `agent://output_id` or `skill://skill-name` to
|
|
11
|
+
* registered protocol handlers.
|
|
12
|
+
*/
|
|
13
|
+
export class InternalUrlRouter {
|
|
14
|
+
private handlers = new Map<string, ProtocolHandler>();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Register a protocol handler.
|
|
18
|
+
* @param handler Handler to register (uses handler.scheme as key)
|
|
19
|
+
*/
|
|
20
|
+
register(handler: ProtocolHandler): void {
|
|
21
|
+
this.handlers.set(handler.scheme, handler);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Check if the router can handle a URL.
|
|
26
|
+
* @param input URL string to check
|
|
27
|
+
*/
|
|
28
|
+
canHandle(input: string): boolean {
|
|
29
|
+
const match = input.match(/^([a-z][a-z0-9+.-]*):\/\//i);
|
|
30
|
+
if (!match) return false;
|
|
31
|
+
const scheme = match[1].toLowerCase();
|
|
32
|
+
return this.handlers.has(scheme);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Resolve an internal URL to its content.
|
|
37
|
+
* @param input URL string (e.g., "agent://reviewer_0", "skill://notion-pages")
|
|
38
|
+
* @throws Error if scheme is not registered or resolution fails
|
|
39
|
+
*/
|
|
40
|
+
async resolve(input: string): Promise<InternalResource> {
|
|
41
|
+
let parsed: URL;
|
|
42
|
+
try {
|
|
43
|
+
parsed = new URL(input);
|
|
44
|
+
} catch {
|
|
45
|
+
throw new Error(`Invalid URL: ${input}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const hostMatch = input.match(/^([a-z][a-z0-9+.-]*):\/\/([^/?#]*)/i);
|
|
49
|
+
let rawHost = hostMatch ? hostMatch[2] : parsed.hostname;
|
|
50
|
+
try {
|
|
51
|
+
rawHost = decodeURIComponent(rawHost);
|
|
52
|
+
} catch {
|
|
53
|
+
// Leave rawHost as-is if decoding fails.
|
|
54
|
+
}
|
|
55
|
+
(parsed as InternalUrl).rawHost = rawHost;
|
|
56
|
+
|
|
57
|
+
const scheme = parsed.protocol.replace(/:$/, "").toLowerCase();
|
|
58
|
+
const handler = this.handlers.get(scheme);
|
|
59
|
+
|
|
60
|
+
if (!handler) {
|
|
61
|
+
const available = Array.from(this.handlers.keys())
|
|
62
|
+
.map((s) => `${s}://`)
|
|
63
|
+
.join(", ");
|
|
64
|
+
throw new Error(`Unknown protocol: ${scheme}://\nSupported: ${available || "none"}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return handler.resolve(parsed as InternalUrl);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol handler for rule:// URLs.
|
|
3
|
+
*
|
|
4
|
+
* Resolves rule names to their content files.
|
|
5
|
+
*
|
|
6
|
+
* URL forms:
|
|
7
|
+
* - rule://<name> - Reads rule content
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Rule } from "$c/capability/rule";
|
|
11
|
+
import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
|
|
12
|
+
|
|
13
|
+
export interface RuleProtocolOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Returns the currently loaded rules.
|
|
16
|
+
*/
|
|
17
|
+
getRules: () => readonly Rule[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Handler for rule:// URLs.
|
|
22
|
+
*
|
|
23
|
+
* Resolves rule names to their content.
|
|
24
|
+
*/
|
|
25
|
+
export class RuleProtocolHandler implements ProtocolHandler {
|
|
26
|
+
readonly scheme = "rule";
|
|
27
|
+
|
|
28
|
+
constructor(private readonly options: RuleProtocolOptions) {}
|
|
29
|
+
|
|
30
|
+
async resolve(url: InternalUrl): Promise<InternalResource> {
|
|
31
|
+
const rules = this.options.getRules();
|
|
32
|
+
|
|
33
|
+
// Extract rule name from host
|
|
34
|
+
const ruleName = url.rawHost || url.hostname;
|
|
35
|
+
if (!ruleName) {
|
|
36
|
+
throw new Error("rule:// URL requires a rule name: rule://<name>");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Find the rule
|
|
40
|
+
const rule = rules.find((r) => r.name === ruleName);
|
|
41
|
+
if (!rule) {
|
|
42
|
+
const available = rules.map((r) => r.name);
|
|
43
|
+
const availableStr = available.length > 0 ? available.join(", ") : "none";
|
|
44
|
+
throw new Error(`Unknown rule: ${ruleName}\nAvailable: ${availableStr}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
url: url.href,
|
|
49
|
+
content: rule.content,
|
|
50
|
+
contentType: "text/markdown",
|
|
51
|
+
size: Buffer.byteLength(rule.content, "utf-8"),
|
|
52
|
+
sourcePath: rule.path,
|
|
53
|
+
notes: [],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol handler for skill:// URLs.
|
|
3
|
+
*
|
|
4
|
+
* Resolves skill names to their SKILL.md files or relative paths within skill directories.
|
|
5
|
+
*
|
|
6
|
+
* URL forms:
|
|
7
|
+
* - skill://<name> - Reads SKILL.md
|
|
8
|
+
* - skill://<name>/<path> - Reads relative path within skill's baseDir
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import type { Skill } from "$c/extensibility/skills";
|
|
13
|
+
import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
|
|
14
|
+
|
|
15
|
+
export interface SkillProtocolOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Returns the currently loaded skills.
|
|
18
|
+
*/
|
|
19
|
+
getSkills: () => readonly Skill[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get content type based on file extension.
|
|
24
|
+
*/
|
|
25
|
+
function getContentType(filePath: string): InternalResource["contentType"] {
|
|
26
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
27
|
+
if (ext === ".md") return "text/markdown";
|
|
28
|
+
return "text/plain";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Validate that a path is safe (no traversal, no absolute paths).
|
|
33
|
+
*/
|
|
34
|
+
function validateRelativePath(relativePath: string): void {
|
|
35
|
+
if (path.isAbsolute(relativePath)) {
|
|
36
|
+
throw new Error("Absolute paths are not allowed in skill:// URLs");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const normalized = path.normalize(relativePath);
|
|
40
|
+
if (normalized.startsWith("..") || normalized.includes("/../") || normalized.includes("/..")) {
|
|
41
|
+
throw new Error("Path traversal (..) is not allowed in skill:// URLs");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Handler for skill:// URLs.
|
|
47
|
+
*
|
|
48
|
+
* Resolves skill names to their content files.
|
|
49
|
+
*/
|
|
50
|
+
export class SkillProtocolHandler implements ProtocolHandler {
|
|
51
|
+
readonly scheme = "skill";
|
|
52
|
+
|
|
53
|
+
constructor(private readonly options: SkillProtocolOptions) {}
|
|
54
|
+
|
|
55
|
+
async resolve(url: InternalUrl): Promise<InternalResource> {
|
|
56
|
+
const skills = this.options.getSkills();
|
|
57
|
+
|
|
58
|
+
// Extract skill name from host
|
|
59
|
+
const skillName = url.rawHost || url.hostname;
|
|
60
|
+
if (!skillName) {
|
|
61
|
+
throw new Error("skill:// URL requires a skill name: skill://<name>");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Find the skill
|
|
65
|
+
const skill = skills.find((s) => s.name === skillName);
|
|
66
|
+
if (!skill) {
|
|
67
|
+
const available = skills.map((s) => s.name);
|
|
68
|
+
const availableStr = available.length > 0 ? available.join(", ") : "none";
|
|
69
|
+
throw new Error(`Unknown skill: ${skillName}\nAvailable: ${availableStr}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Determine the file to read
|
|
73
|
+
let targetPath: string;
|
|
74
|
+
const urlPath = url.pathname;
|
|
75
|
+
const hasRelativePath = urlPath && urlPath !== "/" && urlPath !== "";
|
|
76
|
+
|
|
77
|
+
if (hasRelativePath) {
|
|
78
|
+
// Read relative path within skill's baseDir
|
|
79
|
+
const relativePath = decodeURIComponent(urlPath.slice(1)); // Remove leading /
|
|
80
|
+
validateRelativePath(relativePath);
|
|
81
|
+
targetPath = path.join(skill.baseDir, relativePath);
|
|
82
|
+
|
|
83
|
+
// Verify the resolved path is still within baseDir
|
|
84
|
+
const resolvedPath = path.resolve(targetPath);
|
|
85
|
+
const resolvedBaseDir = path.resolve(skill.baseDir);
|
|
86
|
+
if (!resolvedPath.startsWith(resolvedBaseDir + path.sep) && resolvedPath !== resolvedBaseDir) {
|
|
87
|
+
throw new Error("Path traversal is not allowed");
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
// Read SKILL.md
|
|
91
|
+
targetPath = skill.filePath;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Read the file
|
|
95
|
+
const file = Bun.file(targetPath);
|
|
96
|
+
if (!(await file.exists())) {
|
|
97
|
+
throw new Error(`File not found: ${targetPath}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const content = await file.text();
|
|
101
|
+
const contentType = getContentType(targetPath);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
url: url.href,
|
|
105
|
+
content,
|
|
106
|
+
contentType,
|
|
107
|
+
size: Buffer.byteLength(content, "utf-8"),
|
|
108
|
+
sourcePath: targetPath,
|
|
109
|
+
notes: [],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the internal URL routing system.
|
|
3
|
+
*
|
|
4
|
+
* Internal URLs (agent://, skill://) are resolved by tools like fetch and read,
|
|
5
|
+
* providing access to agent outputs and skill files without exposing filesystem paths.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolved internal resource returned by protocol handlers.
|
|
10
|
+
*/
|
|
11
|
+
export interface InternalResource {
|
|
12
|
+
/** Canonical URL that was resolved */
|
|
13
|
+
url: string;
|
|
14
|
+
/** Resolved text content */
|
|
15
|
+
content: string;
|
|
16
|
+
/** MIME type: text/markdown, application/json, or text/plain */
|
|
17
|
+
contentType: "text/markdown" | "application/json" | "text/plain";
|
|
18
|
+
/** Content size in bytes */
|
|
19
|
+
size?: number;
|
|
20
|
+
/** Underlying filesystem path (for debugging, not exposed to agent) */
|
|
21
|
+
sourcePath?: string;
|
|
22
|
+
/** Additional notes about resolution */
|
|
23
|
+
notes?: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Parsed internal URL with preserved host casing.
|
|
28
|
+
*/
|
|
29
|
+
export interface InternalUrl extends URL {
|
|
30
|
+
/**
|
|
31
|
+
* Raw host segment extracted from input, preserving case.
|
|
32
|
+
*/
|
|
33
|
+
rawHost: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Handler for a specific internal URL scheme (e.g., agent://, skill://).
|
|
38
|
+
*/
|
|
39
|
+
export interface ProtocolHandler {
|
|
40
|
+
/** The scheme this handler processes (without trailing ://) */
|
|
41
|
+
readonly scheme: string;
|
|
42
|
+
/**
|
|
43
|
+
* Resolve an internal URL to its content.
|
|
44
|
+
* @param url Parsed URL object
|
|
45
|
+
* @throws Error with user-friendly message if resolution fails
|
|
46
|
+
*/
|
|
47
|
+
resolve(url: InternalUrl): Promise<InternalResource>;
|
|
48
|
+
}
|