@toolkit-cli/toolkode 1.3.7
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/AGENTS.md +69 -0
- package/BUN_SHELL_MIGRATION_PLAN.md +136 -0
- package/Dockerfile +18 -0
- package/README.md +15 -0
- package/bin/opencode +179 -0
- package/bin/toolkode +17 -0
- package/bin/toolkode.cjs +190 -0
- package/bunfig.toml +7 -0
- package/drizzle.config.ts +10 -0
- package/git +0 -0
- package/migration/20260127222353_familiar_lady_ursula/migration.sql +90 -0
- package/migration/20260127222353_familiar_lady_ursula/snapshot.json +796 -0
- package/migration/20260211171708_add_project_commands/migration.sql +1 -0
- package/migration/20260211171708_add_project_commands/snapshot.json +806 -0
- package/migration/20260213144116_wakeful_the_professor/migration.sql +11 -0
- package/migration/20260213144116_wakeful_the_professor/snapshot.json +897 -0
- package/migration/20260225215848_workspace/migration.sql +7 -0
- package/migration/20260225215848_workspace/snapshot.json +959 -0
- package/migration/20260227213759_add_session_workspace_id/migration.sql +2 -0
- package/migration/20260227213759_add_session_workspace_id/snapshot.json +983 -0
- package/migration/20260228203230_blue_harpoon/migration.sql +17 -0
- package/migration/20260228203230_blue_harpoon/snapshot.json +1102 -0
- package/migration/20260303231226_add_workspace_fields/migration.sql +5 -0
- package/migration/20260303231226_add_workspace_fields/snapshot.json +1013 -0
- package/migration/20260309230000_move_org_to_state/migration.sql +3 -0
- package/migration/20260309230000_move_org_to_state/snapshot.json +1156 -0
- package/migration/20260312043431_session_message_cursor/migration.sql +4 -0
- package/migration/20260312043431_session_message_cursor/snapshot.json +1168 -0
- package/migration/20260323234822_events/migration.sql +13 -0
- package/migration/20260323234822_events/snapshot.json +1271 -0
- package/package.json +160 -0
- package/parsers-config.ts +290 -0
- package/script/build-node.ts +54 -0
- package/script/build.ts +276 -0
- package/script/check-migrations.ts +16 -0
- package/script/postinstall.mjs +131 -0
- package/script/publish.ts +181 -0
- package/script/schema.ts +63 -0
- package/script/seed-e2e.ts +60 -0
- package/script/upgrade-opentui.ts +64 -0
- package/specs/effect-migration.md +293 -0
- package/specs/tui-plugins.md +389 -0
- package/src/account/account.sql.ts +39 -0
- package/src/account/index.ts +397 -0
- package/src/account/repo.ts +163 -0
- package/src/account/schema.ts +91 -0
- package/src/acp/README.md +174 -0
- package/src/acp/agent.ts +1743 -0
- package/src/acp/session.ts +116 -0
- package/src/acp/types.ts +24 -0
- package/src/agent/agent.ts +418 -0
- package/src/agent/generate.txt +75 -0
- package/src/agent/prompt/compaction.txt +14 -0
- package/src/agent/prompt/explore.txt +18 -0
- package/src/agent/prompt/summary.txt +11 -0
- package/src/agent/prompt/title.txt +44 -0
- package/src/auth/index.ts +115 -0
- package/src/bun/index.ts +128 -0
- package/src/bun/registry.ts +50 -0
- package/src/bus/bus-event.ts +40 -0
- package/src/bus/global.ts +10 -0
- package/src/bus/index.ts +184 -0
- package/src/channel/index.ts +231 -0
- package/src/cli/bootstrap.ts +17 -0
- package/src/cli/cmd/account.ts +257 -0
- package/src/cli/cmd/acp.ts +70 -0
- package/src/cli/cmd/agent.ts +245 -0
- package/src/cli/cmd/cmd.ts +7 -0
- package/src/cli/cmd/db.ts +119 -0
- package/src/cli/cmd/debug/agent.ts +167 -0
- package/src/cli/cmd/debug/config.ts +16 -0
- package/src/cli/cmd/debug/file.ts +97 -0
- package/src/cli/cmd/debug/index.ts +48 -0
- package/src/cli/cmd/debug/lsp.ts +53 -0
- package/src/cli/cmd/debug/ripgrep.ts +87 -0
- package/src/cli/cmd/debug/scrap.ts +16 -0
- package/src/cli/cmd/debug/skill.ts +16 -0
- package/src/cli/cmd/debug/snapshot.ts +52 -0
- package/src/cli/cmd/export.ts +89 -0
- package/src/cli/cmd/generate.ts +38 -0
- package/src/cli/cmd/github.ts +1646 -0
- package/src/cli/cmd/import.ts +207 -0
- package/src/cli/cmd/mcp.ts +754 -0
- package/src/cli/cmd/models.ts +78 -0
- package/src/cli/cmd/plug.ts +231 -0
- package/src/cli/cmd/pr.ts +127 -0
- package/src/cli/cmd/providers.ts +482 -0
- package/src/cli/cmd/run.ts +738 -0
- package/src/cli/cmd/serve.ts +42 -0
- package/src/cli/cmd/session.ts +159 -0
- package/src/cli/cmd/stats.ts +410 -0
- package/src/cli/cmd/tui/app.tsx +1255 -0
- package/src/cli/cmd/tui/attach.ts +88 -0
- package/src/cli/cmd/tui/component/border.tsx +21 -0
- package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-command.tsx +171 -0
- package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-model.tsx +264 -0
- package/src/cli/cmd/tui/component/dialog-provider.tsx +334 -0
- package/src/cli/cmd/tui/component/dialog-session-list.tsx +108 -0
- package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-skill.tsx +36 -0
- package/src/cli/cmd/tui/component/dialog-stash.tsx +87 -0
- package/src/cli/cmd/tui/component/dialog-status.tsx +168 -0
- package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
- package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
- package/src/cli/cmd/tui/component/dialog-variant.tsx +29 -0
- package/src/cli/cmd/tui/component/dialog-workspace-list.tsx +320 -0
- package/src/cli/cmd/tui/component/error-component.tsx +91 -0
- package/src/cli/cmd/tui/component/logo.tsx +86 -0
- package/src/cli/cmd/tui/component/plugin-route-missing.tsx +14 -0
- package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +667 -0
- package/src/cli/cmd/tui/component/prompt/frecency.tsx +90 -0
- package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
- package/src/cli/cmd/tui/component/prompt/index.tsx +1353 -0
- package/src/cli/cmd/tui/component/prompt/part.ts +16 -0
- package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
- package/src/cli/cmd/tui/component/spinner.tsx +24 -0
- package/src/cli/cmd/tui/component/startup-loading.tsx +63 -0
- package/src/cli/cmd/tui/component/textarea-keybindings.ts +73 -0
- package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
- package/src/cli/cmd/tui/component/workspace/dialog-session-list.tsx +151 -0
- package/src/cli/cmd/tui/context/args.tsx +15 -0
- package/src/cli/cmd/tui/context/directory.ts +13 -0
- package/src/cli/cmd/tui/context/exit.tsx +60 -0
- package/src/cli/cmd/tui/context/helper.tsx +25 -0
- package/src/cli/cmd/tui/context/keybind.tsx +105 -0
- package/src/cli/cmd/tui/context/kv.tsx +52 -0
- package/src/cli/cmd/tui/context/local.tsx +406 -0
- package/src/cli/cmd/tui/context/plugin-keybinds.ts +41 -0
- package/src/cli/cmd/tui/context/prompt.tsx +18 -0
- package/src/cli/cmd/tui/context/route.tsx +52 -0
- package/src/cli/cmd/tui/context/sdk.tsx +128 -0
- package/src/cli/cmd/tui/context/sync.tsx +504 -0
- package/src/cli/cmd/tui/context/theme/amber.json +245 -0
- package/src/cli/cmd/tui/context/theme/amiga.json +245 -0
- package/src/cli/cmd/tui/context/theme/atari.json +245 -0
- package/src/cli/cmd/tui/context/theme/aura.json +69 -0
- package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
- package/src/cli/cmd/tui/context/theme/borland.json +245 -0
- package/src/cli/cmd/tui/context/theme/carbonfox.json +248 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
- package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
- package/src/cli/cmd/tui/context/theme/commodore.json +245 -0
- package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
- package/src/cli/cmd/tui/context/theme/dos-edit.json +245 -0
- package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
- package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
- package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
- package/src/cli/cmd/tui/context/theme/github.json +233 -0
- package/src/cli/cmd/tui/context/theme/gnu.json +245 -0
- package/src/cli/cmd/tui/context/theme/gruvbox.json +242 -0
- package/src/cli/cmd/tui/context/theme/hacker.json +245 -0
- package/src/cli/cmd/tui/context/theme/irix.json +245 -0
- package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
- package/src/cli/cmd/tui/context/theme/lucent-orng.json +237 -0
- package/src/cli/cmd/tui/context/theme/mac84.json +245 -0
- package/src/cli/cmd/tui/context/theme/material.json +235 -0
- package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
- package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
- package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
- package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
- package/src/cli/cmd/tui/context/theme/nord.json +223 -0
- package/src/cli/cmd/tui/context/theme/norton.json +245 -0
- package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
- package/src/cli/cmd/tui/context/theme/opencode.json +245 -0
- package/src/cli/cmd/tui/context/theme/orng.json +249 -0
- package/src/cli/cmd/tui/context/theme/osaka-jade.json +93 -0
- package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
- package/src/cli/cmd/tui/context/theme/pine.json +245 -0
- package/src/cli/cmd/tui/context/theme/retrowave.json +245 -0
- package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
- package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
- package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
- package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
- package/src/cli/cmd/tui/context/theme/toolkode.json +245 -0
- package/src/cli/cmd/tui/context/theme/tron.json +245 -0
- package/src/cli/cmd/tui/context/theme/ubuntu.json +245 -0
- package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
- package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
- package/src/cli/cmd/tui/context/theme/vt100.json +245 -0
- package/src/cli/cmd/tui/context/theme/xcode.json +245 -0
- package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
- package/src/cli/cmd/tui/context/theme.tsx +1288 -0
- package/src/cli/cmd/tui/context/tui-config.tsx +9 -0
- package/src/cli/cmd/tui/event.ts +49 -0
- package/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx +152 -0
- package/src/cli/cmd/tui/feature-plugins/home/tips.tsx +50 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/agents-panel.tsx +95 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/btw-panel.tsx +105 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/commands-panel.tsx +40 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx +63 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/files.tsx +62 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/footer.tsx +93 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/git-panel.tsx +36 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/loop-panel.tsx +124 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/lsp.tsx +66 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/mcp.tsx +96 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/session-panel.tsx +48 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/todo.tsx +48 -0
- package/src/cli/cmd/tui/feature-plugins/system/plugins.tsx +270 -0
- package/src/cli/cmd/tui/plugin/api.tsx +420 -0
- package/src/cli/cmd/tui/plugin/index.ts +3 -0
- package/src/cli/cmd/tui/plugin/internal.ts +37 -0
- package/src/cli/cmd/tui/plugin/runtime.ts +967 -0
- package/src/cli/cmd/tui/plugin/slots.tsx +61 -0
- package/src/cli/cmd/tui/routes/home.tsx +173 -0
- package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +65 -0
- package/src/cli/cmd/tui/routes/session/dialog-message.tsx +110 -0
- package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
- package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
- package/src/cli/cmd/tui/routes/session/footer.tsx +91 -0
- package/src/cli/cmd/tui/routes/session/index.tsx +2229 -0
- package/src/cli/cmd/tui/routes/session/permission.tsx +685 -0
- package/src/cli/cmd/tui/routes/session/question.tsx +467 -0
- package/src/cli/cmd/tui/routes/session/sidebar.tsx +72 -0
- package/src/cli/cmd/tui/routes/session/subagent-footer.tsx +131 -0
- package/src/cli/cmd/tui/thread.ts +232 -0
- package/src/cli/cmd/tui/ui/dialog-alert.tsx +59 -0
- package/src/cli/cmd/tui/ui/dialog-confirm.tsx +89 -0
- package/src/cli/cmd/tui/ui/dialog-export-options.tsx +208 -0
- package/src/cli/cmd/tui/ui/dialog-help.tsx +40 -0
- package/src/cli/cmd/tui/ui/dialog-prompt.tsx +106 -0
- package/src/cli/cmd/tui/ui/dialog-select.tsx +402 -0
- package/src/cli/cmd/tui/ui/dialog.tsx +192 -0
- package/src/cli/cmd/tui/ui/link.tsx +28 -0
- package/src/cli/cmd/tui/ui/spinner.ts +368 -0
- package/src/cli/cmd/tui/ui/toast.tsx +100 -0
- package/src/cli/cmd/tui/util/clipboard.ts +192 -0
- package/src/cli/cmd/tui/util/editor.ts +37 -0
- package/src/cli/cmd/tui/util/selection.ts +25 -0
- package/src/cli/cmd/tui/util/signal.ts +7 -0
- package/src/cli/cmd/tui/util/terminal.ts +114 -0
- package/src/cli/cmd/tui/util/transcript.ts +98 -0
- package/src/cli/cmd/tui/win32.ts +129 -0
- package/src/cli/cmd/tui/worker.ts +204 -0
- package/src/cli/cmd/uninstall.ts +353 -0
- package/src/cli/cmd/upgrade.ts +73 -0
- package/src/cli/cmd/web.ts +81 -0
- package/src/cli/effect/prompt.ts +25 -0
- package/src/cli/error.ts +46 -0
- package/src/cli/logo.ts +7 -0
- package/src/cli/network.ts +60 -0
- package/src/cli/ui.ts +116 -0
- package/src/cli/upgrade.ts +31 -0
- package/src/command/index.ts +195 -0
- package/src/command/template/initialize.txt +10 -0
- package/src/command/template/review.txt +101 -0
- package/src/config/config.ts +1693 -0
- package/src/config/markdown.ts +99 -0
- package/src/config/migrate-tui-config.ts +155 -0
- package/src/config/paths.ts +174 -0
- package/src/config/tui-schema.ts +36 -0
- package/src/config/tui.ts +212 -0
- package/src/control-plane/adaptors/index.ts +20 -0
- package/src/control-plane/adaptors/worktree.ts +38 -0
- package/src/control-plane/schema.ts +17 -0
- package/src/control-plane/sse.ts +66 -0
- package/src/control-plane/types.ts +21 -0
- package/src/control-plane/workspace.sql.ts +17 -0
- package/src/control-plane/workspace.ts +154 -0
- package/src/cron/index.ts +241 -0
- package/src/cron/parse.ts +189 -0
- package/src/effect/cross-spawn-spawner.ts +479 -0
- package/src/effect/instance-registry.ts +12 -0
- package/src/effect/instance-state.ts +47 -0
- package/src/effect/run-service.ts +19 -0
- package/src/env/index.ts +28 -0
- package/src/file/ignore.ts +82 -0
- package/src/file/index.ts +693 -0
- package/src/file/protected.ts +59 -0
- package/src/file/ripgrep.ts +376 -0
- package/src/file/time.ts +128 -0
- package/src/file/watcher.ts +171 -0
- package/src/filesystem/index.ts +226 -0
- package/src/flag/flag.ts +157 -0
- package/src/format/formatter.ts +396 -0
- package/src/format/index.ts +199 -0
- package/src/global/index.ts +54 -0
- package/src/hooks/index.ts +302 -0
- package/src/id/id.ts +85 -0
- package/src/ide/index.ts +74 -0
- package/src/index.ts +243 -0
- package/src/installation/index.ts +363 -0
- package/src/lsp/client.ts +252 -0
- package/src/lsp/index.ts +558 -0
- package/src/lsp/language.ts +120 -0
- package/src/lsp/launch.ts +21 -0
- package/src/lsp/server.ts +2093 -0
- package/src/mcp/auth.ts +181 -0
- package/src/mcp/index.ts +926 -0
- package/src/mcp/oauth-callback.ts +215 -0
- package/src/mcp/oauth-provider.ts +185 -0
- package/src/node.ts +1 -0
- package/src/patch/index.ts +680 -0
- package/src/permission/arity.ts +163 -0
- package/src/permission/evaluate.ts +15 -0
- package/src/permission/index.ts +322 -0
- package/src/permission/schema.ts +17 -0
- package/src/plugin/codex.ts +628 -0
- package/src/plugin/copilot.ts +343 -0
- package/src/plugin/index.ts +331 -0
- package/src/plugin/install.ts +384 -0
- package/src/plugin/meta.ts +165 -0
- package/src/plugin/shared.ts +172 -0
- package/src/project/bootstrap.ts +31 -0
- package/src/project/instance.ts +167 -0
- package/src/project/project.sql.ts +16 -0
- package/src/project/project.ts +519 -0
- package/src/project/schema.ts +16 -0
- package/src/project/state.ts +70 -0
- package/src/project/vcs.ts +124 -0
- package/src/provider/auth.ts +252 -0
- package/src/provider/error.ts +197 -0
- package/src/provider/models.ts +138 -0
- package/src/provider/provider.ts +1593 -0
- package/src/provider/schema.ts +39 -0
- package/src/provider/sdk/copilot/README.md +5 -0
- package/src/provider/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +170 -0
- package/src/provider/sdk/copilot/chat/get-response-metadata.ts +15 -0
- package/src/provider/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +19 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-api-types.ts +64 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts +815 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-chat-options.ts +28 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-metadata-extractor.ts +44 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-prepare-tools.ts +83 -0
- package/src/provider/sdk/copilot/copilot-provider.ts +100 -0
- package/src/provider/sdk/copilot/index.ts +2 -0
- package/src/provider/sdk/copilot/openai-compatible-error.ts +27 -0
- package/src/provider/sdk/copilot/responses/convert-to-openai-responses-input.ts +335 -0
- package/src/provider/sdk/copilot/responses/map-openai-responses-finish-reason.ts +22 -0
- package/src/provider/sdk/copilot/responses/openai-config.ts +18 -0
- package/src/provider/sdk/copilot/responses/openai-error.ts +22 -0
- package/src/provider/sdk/copilot/responses/openai-responses-api-types.ts +214 -0
- package/src/provider/sdk/copilot/responses/openai-responses-language-model.ts +1769 -0
- package/src/provider/sdk/copilot/responses/openai-responses-prepare-tools.ts +173 -0
- package/src/provider/sdk/copilot/responses/openai-responses-settings.ts +1 -0
- package/src/provider/sdk/copilot/responses/tool/code-interpreter.ts +87 -0
- package/src/provider/sdk/copilot/responses/tool/file-search.ts +127 -0
- package/src/provider/sdk/copilot/responses/tool/image-generation.ts +114 -0
- package/src/provider/sdk/copilot/responses/tool/local-shell.ts +64 -0
- package/src/provider/sdk/copilot/responses/tool/web-search-preview.ts +103 -0
- package/src/provider/sdk/copilot/responses/tool/web-search.ts +102 -0
- package/src/provider/toolkit-manifest.ts +110 -0
- package/src/provider/transform.ts +1045 -0
- package/src/pty/index.ts +397 -0
- package/src/pty/schema.ts +17 -0
- package/src/question/index.ts +221 -0
- package/src/question/schema.ts +17 -0
- package/src/server/error.ts +36 -0
- package/src/server/event.ts +7 -0
- package/src/server/instance.ts +285 -0
- package/src/server/mdns.ts +60 -0
- package/src/server/middleware.ts +29 -0
- package/src/server/projectors.ts +28 -0
- package/src/server/router.ts +99 -0
- package/src/server/routes/config.ts +92 -0
- package/src/server/routes/event.ts +83 -0
- package/src/server/routes/experimental.ts +271 -0
- package/src/server/routes/file.ts +197 -0
- package/src/server/routes/global.ts +339 -0
- package/src/server/routes/mcp.ts +225 -0
- package/src/server/routes/permission.ts +69 -0
- package/src/server/routes/project.ts +118 -0
- package/src/server/routes/provider.ts +171 -0
- package/src/server/routes/pty.ts +211 -0
- package/src/server/routes/question.ts +99 -0
- package/src/server/routes/session.ts +1031 -0
- package/src/server/routes/tui.ts +379 -0
- package/src/server/routes/workspace.ts +94 -0
- package/src/server/server.ts +312 -0
- package/src/session/compaction.ts +424 -0
- package/src/session/index.ts +882 -0
- package/src/session/instruction.ts +321 -0
- package/src/session/llm.ts +341 -0
- package/src/session/message-v2.ts +1030 -0
- package/src/session/message.ts +191 -0
- package/src/session/overflow.ts +22 -0
- package/src/session/processor.ts +554 -0
- package/src/session/projectors.ts +135 -0
- package/src/session/prompt/anthropic.txt +105 -0
- package/src/session/prompt/beast.txt +147 -0
- package/src/session/prompt/build-switch.txt +5 -0
- package/src/session/prompt/codex.txt +79 -0
- package/src/session/prompt/copilot-gpt-5.txt +143 -0
- package/src/session/prompt/default.txt +108 -0
- package/src/session/prompt/gemini.txt +155 -0
- package/src/session/prompt/gpt.txt +107 -0
- package/src/session/prompt/max-steps.txt +16 -0
- package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
- package/src/session/prompt/plan.txt +26 -0
- package/src/session/prompt/trinity.txt +97 -0
- package/src/session/prompt.ts +2058 -0
- package/src/session/retry.ts +106 -0
- package/src/session/revert.ts +138 -0
- package/src/session/schema.ts +38 -0
- package/src/session/session.sql.ts +103 -0
- package/src/session/status.ts +102 -0
- package/src/session/summary.ts +170 -0
- package/src/session/system.ts +74 -0
- package/src/session/todo.ts +57 -0
- package/src/share/share-next.ts +288 -0
- package/src/share/share.sql.ts +13 -0
- package/src/shell/shell.ts +73 -0
- package/src/skill/discovery.ts +116 -0
- package/src/skill/index.ts +284 -0
- package/src/skills-marketplace/index.ts +305 -0
- package/src/snapshot/index.ts +489 -0
- package/src/sql.d.ts +4 -0
- package/src/storage/db.bun.ts +8 -0
- package/src/storage/db.node.ts +8 -0
- package/src/storage/db.ts +177 -0
- package/src/storage/json-migration.ts +425 -0
- package/src/storage/schema.sql.ts +10 -0
- package/src/storage/schema.ts +5 -0
- package/src/storage/storage.ts +217 -0
- package/src/sync/README.md +179 -0
- package/src/sync/event.sql.ts +16 -0
- package/src/sync/index.ts +263 -0
- package/src/sync/schema.ts +14 -0
- package/src/team/index.ts +428 -0
- package/src/tool/apply_patch.ts +281 -0
- package/src/tool/apply_patch.txt +33 -0
- package/src/tool/bash.ts +271 -0
- package/src/tool/bash.txt +115 -0
- package/src/tool/batch.ts +183 -0
- package/src/tool/batch.txt +24 -0
- package/src/tool/codesearch.ts +132 -0
- package/src/tool/codesearch.txt +12 -0
- package/src/tool/cron-create.ts +54 -0
- package/src/tool/cron-create.txt +16 -0
- package/src/tool/cron-delete.ts +29 -0
- package/src/tool/cron-delete.txt +1 -0
- package/src/tool/cron-list.ts +41 -0
- package/src/tool/cron-list.txt +1 -0
- package/src/tool/edit.ts +667 -0
- package/src/tool/edit.txt +10 -0
- package/src/tool/external-directory.ts +32 -0
- package/src/tool/glob.ts +78 -0
- package/src/tool/glob.txt +6 -0
- package/src/tool/grep.ts +156 -0
- package/src/tool/grep.txt +8 -0
- package/src/tool/invalid.ts +17 -0
- package/src/tool/ls.ts +121 -0
- package/src/tool/ls.txt +1 -0
- package/src/tool/lsp.ts +97 -0
- package/src/tool/lsp.txt +19 -0
- package/src/tool/multiedit.ts +46 -0
- package/src/tool/multiedit.txt +41 -0
- package/src/tool/plan-enter.txt +14 -0
- package/src/tool/plan-exit.txt +13 -0
- package/src/tool/plan.ts +131 -0
- package/src/tool/question.ts +33 -0
- package/src/tool/question.txt +10 -0
- package/src/tool/read.ts +293 -0
- package/src/tool/read.txt +14 -0
- package/src/tool/registry.ts +232 -0
- package/src/tool/schema.ts +17 -0
- package/src/tool/send-message.ts +59 -0
- package/src/tool/send-message.txt +7 -0
- package/src/tool/skill.ts +105 -0
- package/src/tool/task.ts +230 -0
- package/src/tool/task.txt +62 -0
- package/src/tool/team.ts +235 -0
- package/src/tool/team.txt +22 -0
- package/src/tool/todo.ts +31 -0
- package/src/tool/todowrite.txt +167 -0
- package/src/tool/tool.ts +90 -0
- package/src/tool/truncate.ts +144 -0
- package/src/tool/truncation-dir.ts +4 -0
- package/src/tool/webfetch.ts +206 -0
- package/src/tool/webfetch.txt +13 -0
- package/src/tool/websearch.ts +150 -0
- package/src/tool/websearch.txt +14 -0
- package/src/tool/write.ts +84 -0
- package/src/tool/write.txt +8 -0
- package/src/util/abort.ts +35 -0
- package/src/util/archive.ts +17 -0
- package/src/util/color.ts +19 -0
- package/src/util/context.ts +25 -0
- package/src/util/data-url.ts +9 -0
- package/src/util/defer.ts +12 -0
- package/src/util/effect-http-client.ts +11 -0
- package/src/util/effect-zod.ts +98 -0
- package/src/util/error.ts +77 -0
- package/src/util/filesystem.ts +203 -0
- package/src/util/flock.ts +333 -0
- package/src/util/fn.ts +21 -0
- package/src/util/format.ts +20 -0
- package/src/util/git.ts +35 -0
- package/src/util/glob.ts +34 -0
- package/src/util/hash.ts +7 -0
- package/src/util/iife.ts +3 -0
- package/src/util/keybind.ts +103 -0
- package/src/util/lazy.ts +23 -0
- package/src/util/locale.ts +81 -0
- package/src/util/lock.ts +98 -0
- package/src/util/log.ts +182 -0
- package/src/util/network.ts +9 -0
- package/src/util/process.ts +172 -0
- package/src/util/queue.ts +32 -0
- package/src/util/record.ts +3 -0
- package/src/util/rpc.ts +66 -0
- package/src/util/schema.ts +53 -0
- package/src/util/scrap.ts +10 -0
- package/src/util/signal.ts +12 -0
- package/src/util/timeout.ts +14 -0
- package/src/util/token.ts +7 -0
- package/src/util/update-schema.ts +13 -0
- package/src/util/which.ts +14 -0
- package/src/util/wildcard.ts +59 -0
- package/src/worktree/index.ts +638 -0
- package/sst-env.d.ts +10 -0
- package/test/AGENTS.md +81 -0
- package/test/account/repo.test.ts +326 -0
- package/test/account/service.test.ts +282 -0
- package/test/acp/agent-interface.test.ts +51 -0
- package/test/acp/event-subscription.test.ts +685 -0
- package/test/agent/agent.test.ts +717 -0
- package/test/auth/auth.test.ts +58 -0
- package/test/bun.test.ts +53 -0
- package/test/bus/bus-effect.test.ts +164 -0
- package/test/bus/bus-integration.test.ts +87 -0
- package/test/bus/bus.test.ts +219 -0
- package/test/cli/account.test.ts +26 -0
- package/test/cli/cmd/tui/prompt-part.test.ts +47 -0
- package/test/cli/github-action.test.ts +198 -0
- package/test/cli/github-remote.test.ts +80 -0
- package/test/cli/import.test.ts +54 -0
- package/test/cli/plugin-auth-picker.test.ts +120 -0
- package/test/cli/tui/keybind-plugin.test.ts +90 -0
- package/test/cli/tui/plugin-add.test.ts +61 -0
- package/test/cli/tui/plugin-install.test.ts +95 -0
- package/test/cli/tui/plugin-lifecycle.test.ts +225 -0
- package/test/cli/tui/plugin-loader-entrypoint.test.ts +189 -0
- package/test/cli/tui/plugin-loader-pure.test.ts +71 -0
- package/test/cli/tui/plugin-loader.test.ts +563 -0
- package/test/cli/tui/plugin-toggle.test.ts +157 -0
- package/test/cli/tui/theme-store.test.ts +51 -0
- package/test/cli/tui/thread.test.ts +128 -0
- package/test/cli/tui/transcript.test.ts +322 -0
- package/test/config/agent-color.test.ts +71 -0
- package/test/config/config.test.ts +2187 -0
- package/test/config/fixtures/empty-frontmatter.md +4 -0
- package/test/config/fixtures/frontmatter.md +28 -0
- package/test/config/fixtures/markdown-header.md +11 -0
- package/test/config/fixtures/no-frontmatter.md +1 -0
- package/test/config/fixtures/weird-model-id.md +13 -0
- package/test/config/markdown.test.ts +228 -0
- package/test/config/tui.test.ts +667 -0
- package/test/control-plane/sse.test.ts +56 -0
- package/test/effect/cross-spawn-spawner.test.ts +402 -0
- package/test/effect/instance-state.test.ts +384 -0
- package/test/effect/run-service.test.ts +46 -0
- package/test/file/fsmonitor.test.ts +62 -0
- package/test/file/ignore.test.ts +10 -0
- package/test/file/index.test.ts +946 -0
- package/test/file/path-traversal.test.ts +198 -0
- package/test/file/ripgrep.test.ts +54 -0
- package/test/file/time.test.ts +354 -0
- package/test/file/watcher.test.ts +247 -0
- package/test/filesystem/filesystem.test.ts +319 -0
- package/test/fixture/db.ts +11 -0
- package/test/fixture/fixture.test.ts +26 -0
- package/test/fixture/fixture.ts +141 -0
- package/test/fixture/flock-worker.ts +72 -0
- package/test/fixture/lsp/fake-lsp-server.js +77 -0
- package/test/fixture/plug-worker.ts +93 -0
- package/test/fixture/plugin-meta-worker.ts +26 -0
- package/test/fixture/skills/agents-sdk/SKILL.md +152 -0
- package/test/fixture/skills/agents-sdk/references/callable.md +92 -0
- package/test/fixture/skills/cloudflare/SKILL.md +211 -0
- package/test/fixture/skills/index.json +6 -0
- package/test/fixture/tui-plugin.ts +335 -0
- package/test/fixture/tui-runtime.ts +34 -0
- package/test/format/format.test.ts +179 -0
- package/test/ide/ide.test.ts +82 -0
- package/test/installation/installation.test.ts +151 -0
- package/test/keybind.test.ts +421 -0
- package/test/lib/effect.ts +37 -0
- package/test/lib/filesystem.ts +10 -0
- package/test/lsp/client.test.ts +95 -0
- package/test/lsp/index.test.ts +55 -0
- package/test/lsp/launch.test.ts +22 -0
- package/test/lsp/lifecycle.test.ts +147 -0
- package/test/mcp/headers.test.ts +153 -0
- package/test/mcp/lifecycle.test.ts +750 -0
- package/test/mcp/oauth-auto-connect.test.ts +199 -0
- package/test/mcp/oauth-browser.test.ts +249 -0
- package/test/memory/abort-leak.test.ts +137 -0
- package/test/patch/patch.test.ts +348 -0
- package/test/permission/arity.test.ts +33 -0
- package/test/permission/next.test.ts +1148 -0
- package/test/permission-task.test.ts +323 -0
- package/test/plugin/auth-override.test.ts +74 -0
- package/test/plugin/codex.test.ts +123 -0
- package/test/plugin/install-concurrency.test.ts +134 -0
- package/test/plugin/install.test.ts +504 -0
- package/test/plugin/loader-shared.test.ts +625 -0
- package/test/plugin/meta.test.ts +137 -0
- package/test/plugin/trigger.test.ts +111 -0
- package/test/preload.ts +90 -0
- package/test/project/migrate-global.test.ts +140 -0
- package/test/project/project.test.ts +459 -0
- package/test/project/state.test.ts +115 -0
- package/test/project/vcs.test.ts +116 -0
- package/test/project/worktree-remove.test.ts +96 -0
- package/test/project/worktree.test.ts +173 -0
- package/test/provider/amazon-bedrock.test.ts +447 -0
- package/test/provider/copilot/convert-to-copilot-messages.test.ts +523 -0
- package/test/provider/copilot/copilot-chat-model.test.ts +592 -0
- package/test/provider/gitlab-duo.test.ts +412 -0
- package/test/provider/provider.test.ts +2284 -0
- package/test/provider/transform.test.ts +2758 -0
- package/test/pty/pty-output-isolation.test.ts +141 -0
- package/test/pty/pty-session.test.ts +92 -0
- package/test/question/question.test.ts +453 -0
- package/test/server/global-session-list.test.ts +89 -0
- package/test/server/project-init-git.test.ts +121 -0
- package/test/server/session-list.test.ts +90 -0
- package/test/server/session-messages.test.ts +132 -0
- package/test/server/session-select.test.ts +78 -0
- package/test/session/compaction.test.ts +1094 -0
- package/test/session/instruction.test.ts +170 -0
- package/test/session/llm.test.ts +882 -0
- package/test/session/message-v2.test.ts +957 -0
- package/test/session/messages-pagination.test.ts +115 -0
- package/test/session/processor-effect.test.ts +838 -0
- package/test/session/prompt.test.ts +518 -0
- package/test/session/retry.test.ts +232 -0
- package/test/session/revert-compact.test.ts +286 -0
- package/test/session/session.test.ts +142 -0
- package/test/session/structured-output-integration.test.ts +233 -0
- package/test/session/structured-output.test.ts +391 -0
- package/test/session/system.test.ts +59 -0
- package/test/share/share-next.test.ts +76 -0
- package/test/skill/discovery.test.ts +116 -0
- package/test/skill/skill.test.ts +392 -0
- package/test/snapshot/snapshot.test.ts +1235 -0
- package/test/storage/db.test.ts +14 -0
- package/test/storage/json-migration.test.ts +849 -0
- package/test/sync/index.test.ts +191 -0
- package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
- package/test/tool/apply_patch.test.ts +567 -0
- package/test/tool/bash.test.ts +403 -0
- package/test/tool/edit.test.ts +681 -0
- package/test/tool/external-directory.test.ts +128 -0
- package/test/tool/fixtures/large-image.png +0 -0
- package/test/tool/fixtures/models-api.json +38413 -0
- package/test/tool/grep.test.ts +111 -0
- package/test/tool/question.test.ts +108 -0
- package/test/tool/read.test.ts +509 -0
- package/test/tool/registry.test.ts +126 -0
- package/test/tool/skill.test.ts +167 -0
- package/test/tool/task.test.ts +49 -0
- package/test/tool/truncation.test.ts +161 -0
- package/test/tool/webfetch.test.ts +101 -0
- package/test/tool/write.test.ts +353 -0
- package/test/util/data-url.test.ts +14 -0
- package/test/util/effect-zod.test.ts +61 -0
- package/test/util/error.test.ts +38 -0
- package/test/util/filesystem.test.ts +558 -0
- package/test/util/flock.test.ts +383 -0
- package/test/util/format.test.ts +59 -0
- package/test/util/glob.test.ts +164 -0
- package/test/util/iife.test.ts +36 -0
- package/test/util/lazy.test.ts +50 -0
- package/test/util/lock.test.ts +72 -0
- package/test/util/module.test.ts +59 -0
- package/test/util/process.test.ts +128 -0
- package/test/util/timeout.test.ts +21 -0
- package/test/util/which.test.ts +100 -0
- package/test/util/wildcard.test.ts +90 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { NamedError } from "@toolkit-cli/util/error"
|
|
2
|
+
import matter from "gray-matter"
|
|
3
|
+
import { z } from "zod"
|
|
4
|
+
import { Filesystem } from "../util/filesystem"
|
|
5
|
+
|
|
6
|
+
export namespace ConfigMarkdown {
|
|
7
|
+
export const FILE_REGEX = /(?<![\w`])@(\.?[^\s`,.]*(?:\.[^\s`,.]+)*)/g
|
|
8
|
+
export const SHELL_REGEX = /!`([^`]+)`/g
|
|
9
|
+
|
|
10
|
+
export function files(template: string) {
|
|
11
|
+
return Array.from(template.matchAll(FILE_REGEX))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function shell(template: string) {
|
|
15
|
+
return Array.from(template.matchAll(SHELL_REGEX))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// other coding agents like claude code allow invalid yaml in their
|
|
19
|
+
// frontmatter, we need to fallback to a more permissive parser for those cases
|
|
20
|
+
export function fallbackSanitization(content: string): string {
|
|
21
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/)
|
|
22
|
+
if (!match) return content
|
|
23
|
+
|
|
24
|
+
const frontmatter = match[1]
|
|
25
|
+
const lines = frontmatter.split(/\r?\n/)
|
|
26
|
+
const result: string[] = []
|
|
27
|
+
|
|
28
|
+
for (const line of lines) {
|
|
29
|
+
// skip comments and empty lines
|
|
30
|
+
if (line.trim().startsWith("#") || line.trim() === "") {
|
|
31
|
+
result.push(line)
|
|
32
|
+
continue
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// skip lines that are continuations (indented)
|
|
36
|
+
if (line.match(/^\s+/)) {
|
|
37
|
+
result.push(line)
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// match key: value pattern
|
|
42
|
+
const kvMatch = line.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.*)$/)
|
|
43
|
+
if (!kvMatch) {
|
|
44
|
+
result.push(line)
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const key = kvMatch[1]
|
|
49
|
+
const value = kvMatch[2].trim()
|
|
50
|
+
|
|
51
|
+
// skip if value is empty, already quoted, or uses block scalar
|
|
52
|
+
if (value === "" || value === ">" || value === "|" || value.startsWith('"') || value.startsWith("'")) {
|
|
53
|
+
result.push(line)
|
|
54
|
+
continue
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// if value contains a colon, convert to block scalar
|
|
58
|
+
if (value.includes(":")) {
|
|
59
|
+
result.push(`${key}: |-`)
|
|
60
|
+
result.push(` ${value}`)
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
result.push(line)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const processed = result.join("\n")
|
|
68
|
+
return content.replace(frontmatter, () => processed)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function parse(filePath: string) {
|
|
72
|
+
const template = await Filesystem.readText(filePath)
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const md = matter(template)
|
|
76
|
+
return md
|
|
77
|
+
} catch {
|
|
78
|
+
try {
|
|
79
|
+
return matter(fallbackSanitization(template))
|
|
80
|
+
} catch (err) {
|
|
81
|
+
throw new FrontmatterError(
|
|
82
|
+
{
|
|
83
|
+
path: filePath,
|
|
84
|
+
message: `${filePath}: Failed to parse YAML frontmatter: ${err instanceof Error ? err.message : String(err)}`,
|
|
85
|
+
},
|
|
86
|
+
{ cause: err },
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const FrontmatterError = NamedError.create(
|
|
93
|
+
"ConfigFrontmatterError",
|
|
94
|
+
z.object({
|
|
95
|
+
path: z.string(),
|
|
96
|
+
message: z.string(),
|
|
97
|
+
}),
|
|
98
|
+
)
|
|
99
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import path from "path"
|
|
2
|
+
import { type ParseError as JsoncParseError, applyEdits, modify, parse as parseJsonc } from "jsonc-parser"
|
|
3
|
+
import { unique } from "remeda"
|
|
4
|
+
import z from "zod"
|
|
5
|
+
import { ConfigPaths } from "./paths"
|
|
6
|
+
import { TuiInfo, TuiOptions } from "./tui-schema"
|
|
7
|
+
import { Instance } from "@/project/instance"
|
|
8
|
+
import { Flag } from "@/flag/flag"
|
|
9
|
+
import { Log } from "@/util/log"
|
|
10
|
+
import { Filesystem } from "@/util/filesystem"
|
|
11
|
+
import { Global } from "@/global"
|
|
12
|
+
|
|
13
|
+
const log = Log.create({ service: "tui.migrate" })
|
|
14
|
+
|
|
15
|
+
const TUI_SCHEMA_URL = "https://toolkode.com/tui.json"
|
|
16
|
+
|
|
17
|
+
const LegacyTheme = TuiInfo.shape.theme.optional()
|
|
18
|
+
const LegacyRecord = z.record(z.string(), z.unknown()).optional()
|
|
19
|
+
|
|
20
|
+
const TuiLegacy = z
|
|
21
|
+
.object({
|
|
22
|
+
scroll_speed: TuiOptions.shape.scroll_speed.catch(undefined),
|
|
23
|
+
scroll_acceleration: TuiOptions.shape.scroll_acceleration.catch(undefined),
|
|
24
|
+
diff_style: TuiOptions.shape.diff_style.catch(undefined),
|
|
25
|
+
})
|
|
26
|
+
.strip()
|
|
27
|
+
|
|
28
|
+
interface MigrateInput {
|
|
29
|
+
directories: string[]
|
|
30
|
+
custom?: string
|
|
31
|
+
managed: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Migrates tui-specific keys (theme, keybinds, tui) from toolkode.json files
|
|
36
|
+
* into dedicated tui.json files. Migration is performed per-directory and
|
|
37
|
+
* skips only locations where a tui.json already exists.
|
|
38
|
+
*/
|
|
39
|
+
export async function migrateTuiConfig(input: MigrateInput) {
|
|
40
|
+
const opencode = await opencodeFiles(input)
|
|
41
|
+
for (const file of opencode) {
|
|
42
|
+
const source = await Filesystem.readText(file).catch((error) => {
|
|
43
|
+
log.warn("failed to read config for tui migration", { path: file, error })
|
|
44
|
+
return undefined
|
|
45
|
+
})
|
|
46
|
+
if (!source) continue
|
|
47
|
+
const errors: JsoncParseError[] = []
|
|
48
|
+
const data = parseJsonc(source, errors, { allowTrailingComma: true })
|
|
49
|
+
if (errors.length || !data || typeof data !== "object" || Array.isArray(data)) continue
|
|
50
|
+
|
|
51
|
+
const theme = LegacyTheme.safeParse("theme" in data ? data.theme : undefined)
|
|
52
|
+
const keybinds = LegacyRecord.safeParse("keybinds" in data ? data.keybinds : undefined)
|
|
53
|
+
const legacyTui = LegacyRecord.safeParse("tui" in data ? data.tui : undefined)
|
|
54
|
+
const extracted = {
|
|
55
|
+
theme: theme.success ? theme.data : undefined,
|
|
56
|
+
keybinds: keybinds.success ? keybinds.data : undefined,
|
|
57
|
+
tui: legacyTui.success ? legacyTui.data : undefined,
|
|
58
|
+
}
|
|
59
|
+
const tui = extracted.tui ? normalizeTui(extracted.tui) : undefined
|
|
60
|
+
if (extracted.theme === undefined && extracted.keybinds === undefined && !tui) continue
|
|
61
|
+
|
|
62
|
+
const target = path.join(path.dirname(file), "tui.json")
|
|
63
|
+
const targetExists = await Filesystem.exists(target)
|
|
64
|
+
if (targetExists) continue
|
|
65
|
+
|
|
66
|
+
const payload: Record<string, unknown> = {
|
|
67
|
+
$schema: TUI_SCHEMA_URL,
|
|
68
|
+
}
|
|
69
|
+
if (extracted.theme !== undefined) payload.theme = extracted.theme
|
|
70
|
+
if (extracted.keybinds !== undefined) payload.keybinds = extracted.keybinds
|
|
71
|
+
if (tui) Object.assign(payload, tui)
|
|
72
|
+
|
|
73
|
+
const wrote = await Filesystem.write(target, JSON.stringify(payload, null, 2))
|
|
74
|
+
.then(() => true)
|
|
75
|
+
.catch((error) => {
|
|
76
|
+
log.warn("failed to write tui migration target", { from: file, to: target, error })
|
|
77
|
+
return false
|
|
78
|
+
})
|
|
79
|
+
if (!wrote) continue
|
|
80
|
+
|
|
81
|
+
const stripped = await backupAndStripLegacy(file, source)
|
|
82
|
+
if (!stripped) {
|
|
83
|
+
log.warn("tui config migrated but source file was not stripped", { from: file, to: target })
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
log.info("migrated tui config", { from: file, to: target })
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function normalizeTui(data: Record<string, unknown>) {
|
|
91
|
+
const parsed = TuiLegacy.parse(data)
|
|
92
|
+
if (
|
|
93
|
+
parsed.scroll_speed === undefined &&
|
|
94
|
+
parsed.diff_style === undefined &&
|
|
95
|
+
parsed.scroll_acceleration === undefined
|
|
96
|
+
) {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
return parsed
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function backupAndStripLegacy(file: string, source: string) {
|
|
103
|
+
const backup = file + ".tui-migration.bak"
|
|
104
|
+
const hasBackup = await Filesystem.exists(backup)
|
|
105
|
+
const backed = hasBackup
|
|
106
|
+
? true
|
|
107
|
+
: await Filesystem.write(backup, source)
|
|
108
|
+
.then(() => true)
|
|
109
|
+
.catch((error) => {
|
|
110
|
+
log.warn("failed to backup source config during tui migration", { path: file, backup, error })
|
|
111
|
+
return false
|
|
112
|
+
})
|
|
113
|
+
if (!backed) return false
|
|
114
|
+
|
|
115
|
+
const text = ["theme", "keybinds", "tui"].reduce((acc, key) => {
|
|
116
|
+
const edits = modify(acc, [key], undefined, {
|
|
117
|
+
formattingOptions: {
|
|
118
|
+
insertSpaces: true,
|
|
119
|
+
tabSize: 2,
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
if (!edits.length) return acc
|
|
123
|
+
return applyEdits(acc, edits)
|
|
124
|
+
}, source)
|
|
125
|
+
|
|
126
|
+
return Filesystem.write(file, text)
|
|
127
|
+
.then(() => {
|
|
128
|
+
log.info("stripped tui keys from server config", { path: file, backup })
|
|
129
|
+
return true
|
|
130
|
+
})
|
|
131
|
+
.catch((error) => {
|
|
132
|
+
log.warn("failed to strip legacy tui keys from server config", { path: file, backup, error })
|
|
133
|
+
return false
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function opencodeFiles(input: { directories: string[]; managed: string }) {
|
|
138
|
+
const project = Flag.OPENCODE_DISABLE_PROJECT_CONFIG
|
|
139
|
+
? []
|
|
140
|
+
: await ConfigPaths.projectFiles("toolkode", Instance.directory, Instance.worktree)
|
|
141
|
+
const files = [...project, ...ConfigPaths.fileInDirectory(Global.Path.config, "toolkode")]
|
|
142
|
+
for (const dir of unique(input.directories)) {
|
|
143
|
+
files.push(...ConfigPaths.fileInDirectory(dir, "toolkode"))
|
|
144
|
+
}
|
|
145
|
+
if (Flag.OPENCODE_CONFIG) files.push(Flag.OPENCODE_CONFIG)
|
|
146
|
+
files.push(...ConfigPaths.fileInDirectory(input.managed, "toolkode"))
|
|
147
|
+
|
|
148
|
+
const existing = await Promise.all(
|
|
149
|
+
unique(files).map(async (file) => {
|
|
150
|
+
const ok = await Filesystem.exists(file)
|
|
151
|
+
return ok ? file : undefined
|
|
152
|
+
}),
|
|
153
|
+
)
|
|
154
|
+
return existing.filter((file): file is string => !!file)
|
|
155
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import path from "path"
|
|
2
|
+
import os from "os"
|
|
3
|
+
import z from "zod"
|
|
4
|
+
import { type ParseError as JsoncParseError, parse as parseJsonc, printParseErrorCode } from "jsonc-parser"
|
|
5
|
+
import { NamedError } from "@toolkit-cli/util/error"
|
|
6
|
+
import { Filesystem } from "@/util/filesystem"
|
|
7
|
+
import { Flag } from "@/flag/flag"
|
|
8
|
+
import { Global } from "@/global"
|
|
9
|
+
|
|
10
|
+
export namespace ConfigPaths {
|
|
11
|
+
export async function projectFiles(name: string, directory: string, worktree: string) {
|
|
12
|
+
const files: string[] = []
|
|
13
|
+
for (const file of [`${name}.jsonc`, `${name}.json`]) {
|
|
14
|
+
const found = await Filesystem.findUp(file, directory, worktree)
|
|
15
|
+
for (const resolved of found.toReversed()) {
|
|
16
|
+
files.push(resolved)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return files
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function directories(directory: string, worktree: string) {
|
|
23
|
+
return [
|
|
24
|
+
Global.Path.config,
|
|
25
|
+
...(!Flag.OPENCODE_DISABLE_PROJECT_CONFIG
|
|
26
|
+
? await Array.fromAsync(
|
|
27
|
+
Filesystem.up({
|
|
28
|
+
targets: [".toolkode"],
|
|
29
|
+
start: directory,
|
|
30
|
+
stop: worktree,
|
|
31
|
+
}),
|
|
32
|
+
)
|
|
33
|
+
: []),
|
|
34
|
+
...(await Array.fromAsync(
|
|
35
|
+
Filesystem.up({
|
|
36
|
+
targets: [".toolkode"],
|
|
37
|
+
start: Global.Path.home,
|
|
38
|
+
stop: Global.Path.home,
|
|
39
|
+
}),
|
|
40
|
+
)),
|
|
41
|
+
...(Flag.OPENCODE_CONFIG_DIR ? [Flag.OPENCODE_CONFIG_DIR] : []),
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function fileInDirectory(dir: string, name: string) {
|
|
46
|
+
return [path.join(dir, `${name}.jsonc`), path.join(dir, `${name}.json`)]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const JsonError = NamedError.create(
|
|
50
|
+
"ConfigJsonError",
|
|
51
|
+
z.object({
|
|
52
|
+
path: z.string(),
|
|
53
|
+
message: z.string().optional(),
|
|
54
|
+
}),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
export const InvalidError = NamedError.create(
|
|
58
|
+
"ConfigInvalidError",
|
|
59
|
+
z.object({
|
|
60
|
+
path: z.string(),
|
|
61
|
+
issues: z.custom<z.core.$ZodIssue[]>().optional(),
|
|
62
|
+
message: z.string().optional(),
|
|
63
|
+
}),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
/** Read a config file, returning undefined for missing files and throwing JsonError for other failures. */
|
|
67
|
+
export async function readFile(filepath: string) {
|
|
68
|
+
return Filesystem.readText(filepath).catch((err: NodeJS.ErrnoException) => {
|
|
69
|
+
if (err.code === "ENOENT") return
|
|
70
|
+
throw new JsonError({ path: filepath }, { cause: err })
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
type ParseSource = string | { source: string; dir: string }
|
|
75
|
+
|
|
76
|
+
function source(input: ParseSource) {
|
|
77
|
+
return typeof input === "string" ? input : input.source
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function dir(input: ParseSource) {
|
|
81
|
+
return typeof input === "string" ? path.dirname(input) : input.dir
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Apply {env:VAR} and {file:path} substitutions to config text. */
|
|
85
|
+
async function substitute(text: string, input: ParseSource, missing: "error" | "empty" = "error") {
|
|
86
|
+
text = text.replace(/\{env:([^}]+)\}/g, (_, varName) => {
|
|
87
|
+
return process.env[varName] || ""
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const fileMatches = Array.from(text.matchAll(/\{file:[^}]+\}/g))
|
|
91
|
+
if (!fileMatches.length) return text
|
|
92
|
+
|
|
93
|
+
const configDir = dir(input)
|
|
94
|
+
const configSource = source(input)
|
|
95
|
+
let out = ""
|
|
96
|
+
let cursor = 0
|
|
97
|
+
|
|
98
|
+
for (const match of fileMatches) {
|
|
99
|
+
const token = match[0]
|
|
100
|
+
const index = match.index!
|
|
101
|
+
out += text.slice(cursor, index)
|
|
102
|
+
|
|
103
|
+
const lineStart = text.lastIndexOf("\n", index - 1) + 1
|
|
104
|
+
const prefix = text.slice(lineStart, index).trimStart()
|
|
105
|
+
if (prefix.startsWith("//")) {
|
|
106
|
+
out += token
|
|
107
|
+
cursor = index + token.length
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let filePath = token.replace(/^\{file:/, "").replace(/\}$/, "")
|
|
112
|
+
if (filePath.startsWith("~/")) {
|
|
113
|
+
filePath = path.join(os.homedir(), filePath.slice(2))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(configDir, filePath)
|
|
117
|
+
const fileContent = (
|
|
118
|
+
await Filesystem.readText(resolvedPath).catch((error: NodeJS.ErrnoException) => {
|
|
119
|
+
if (missing === "empty") return ""
|
|
120
|
+
|
|
121
|
+
const errMsg = `bad file reference: "${token}"`
|
|
122
|
+
if (error.code === "ENOENT") {
|
|
123
|
+
throw new InvalidError(
|
|
124
|
+
{
|
|
125
|
+
path: configSource,
|
|
126
|
+
message: errMsg + ` ${resolvedPath} does not exist`,
|
|
127
|
+
},
|
|
128
|
+
{ cause: error },
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
throw new InvalidError({ path: configSource, message: errMsg }, { cause: error })
|
|
132
|
+
})
|
|
133
|
+
).trim()
|
|
134
|
+
|
|
135
|
+
out += JSON.stringify(fileContent).slice(1, -1)
|
|
136
|
+
cursor = index + token.length
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
out += text.slice(cursor)
|
|
140
|
+
return out
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Substitute and parse JSONC text, throwing JsonError on syntax errors. */
|
|
144
|
+
export async function parseText(text: string, input: ParseSource, missing: "error" | "empty" = "error") {
|
|
145
|
+
const configSource = source(input)
|
|
146
|
+
text = await substitute(text, input, missing)
|
|
147
|
+
|
|
148
|
+
const errors: JsoncParseError[] = []
|
|
149
|
+
const data = parseJsonc(text, errors, { allowTrailingComma: true })
|
|
150
|
+
if (errors.length) {
|
|
151
|
+
const lines = text.split("\n")
|
|
152
|
+
const errorDetails = errors
|
|
153
|
+
.map((e) => {
|
|
154
|
+
const beforeOffset = text.substring(0, e.offset).split("\n")
|
|
155
|
+
const line = beforeOffset.length
|
|
156
|
+
const column = beforeOffset[beforeOffset.length - 1].length + 1
|
|
157
|
+
const problemLine = lines[line - 1]
|
|
158
|
+
|
|
159
|
+
const error = `${printParseErrorCode(e.error)} at line ${line}, column ${column}`
|
|
160
|
+
if (!problemLine) return error
|
|
161
|
+
|
|
162
|
+
return `${error}\n Line ${line}: ${problemLine}\n${"".padStart(column + 9)}^`
|
|
163
|
+
})
|
|
164
|
+
.join("\n")
|
|
165
|
+
|
|
166
|
+
throw new JsonError({
|
|
167
|
+
path: configSource,
|
|
168
|
+
message: `\n--- JSONC Input ---\n${text}\n--- Errors ---\n${errorDetails}\n--- End ---`,
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return data
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import z from "zod"
|
|
2
|
+
import { Config } from "./config"
|
|
3
|
+
|
|
4
|
+
const KeybindOverride = z
|
|
5
|
+
.object(
|
|
6
|
+
Object.fromEntries(Object.keys(Config.Keybinds.shape).map((key) => [key, z.string().optional()])) as Record<
|
|
7
|
+
string,
|
|
8
|
+
z.ZodOptional<z.ZodString>
|
|
9
|
+
>,
|
|
10
|
+
)
|
|
11
|
+
.strict()
|
|
12
|
+
|
|
13
|
+
export const TuiOptions = z.object({
|
|
14
|
+
scroll_speed: z.number().min(0.001).optional().describe("TUI scroll speed"),
|
|
15
|
+
scroll_acceleration: z
|
|
16
|
+
.object({
|
|
17
|
+
enabled: z.boolean().describe("Enable scroll acceleration"),
|
|
18
|
+
})
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("Scroll acceleration settings"),
|
|
21
|
+
diff_style: z
|
|
22
|
+
.enum(["auto", "stacked"])
|
|
23
|
+
.optional()
|
|
24
|
+
.describe("Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column"),
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
export const TuiInfo = z
|
|
28
|
+
.object({
|
|
29
|
+
$schema: z.string().optional(),
|
|
30
|
+
theme: z.string().optional(),
|
|
31
|
+
keybinds: KeybindOverride.optional(),
|
|
32
|
+
plugin: Config.PluginSpec.array().optional(),
|
|
33
|
+
plugin_enabled: z.record(z.string(), z.boolean()).optional(),
|
|
34
|
+
})
|
|
35
|
+
.extend(TuiOptions.shape)
|
|
36
|
+
.strict()
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { existsSync } from "fs"
|
|
2
|
+
import z from "zod"
|
|
3
|
+
import { mergeDeep, unique } from "remeda"
|
|
4
|
+
import { Config } from "./config"
|
|
5
|
+
import { ConfigPaths } from "./paths"
|
|
6
|
+
import { migrateTuiConfig } from "./migrate-tui-config"
|
|
7
|
+
import { TuiInfo } from "./tui-schema"
|
|
8
|
+
import { Instance } from "@/project/instance"
|
|
9
|
+
import { Flag } from "@/flag/flag"
|
|
10
|
+
import { Log } from "@/util/log"
|
|
11
|
+
import { isRecord } from "@/util/record"
|
|
12
|
+
import { Global } from "@/global"
|
|
13
|
+
import { parsePluginSpecifier } from "@/plugin/shared"
|
|
14
|
+
|
|
15
|
+
export namespace TuiConfig {
|
|
16
|
+
const log = Log.create({ service: "tui.config" })
|
|
17
|
+
|
|
18
|
+
export const Info = TuiInfo
|
|
19
|
+
|
|
20
|
+
export type PluginMeta = {
|
|
21
|
+
scope: "global" | "local"
|
|
22
|
+
source: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type PluginEntry = {
|
|
26
|
+
item: Config.PluginSpec
|
|
27
|
+
meta: PluginMeta
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type Acc = {
|
|
31
|
+
result: Info
|
|
32
|
+
entries: PluginEntry[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type Info = z.output<typeof Info> & {
|
|
36
|
+
plugin_meta?: Record<string, PluginMeta>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function pluginScope(file: string): PluginMeta["scope"] {
|
|
40
|
+
if (Instance.containsPath(file)) return "local"
|
|
41
|
+
return "global"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function dedupePlugins(list: PluginEntry[]) {
|
|
45
|
+
const seen = new Set<string>()
|
|
46
|
+
const result: PluginEntry[] = []
|
|
47
|
+
for (const item of list.toReversed()) {
|
|
48
|
+
const spec = Config.pluginSpecifier(item.item)
|
|
49
|
+
const name = spec.startsWith("file://") ? spec : parsePluginSpecifier(spec).pkg
|
|
50
|
+
if (seen.has(name)) continue
|
|
51
|
+
seen.add(name)
|
|
52
|
+
result.push(item)
|
|
53
|
+
}
|
|
54
|
+
return result.toReversed()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function mergeInfo(target: Info, source: Info): Info {
|
|
58
|
+
const merged = mergeDeep(target, source)
|
|
59
|
+
if (target.plugin && source.plugin) {
|
|
60
|
+
merged.plugin = [...target.plugin, ...source.plugin]
|
|
61
|
+
}
|
|
62
|
+
return merged
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function customPath() {
|
|
66
|
+
return Flag.OPENCODE_TUI_CONFIG
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function normalize(raw: Record<string, unknown>) {
|
|
70
|
+
const data = { ...raw }
|
|
71
|
+
if (!("tui" in data)) return data
|
|
72
|
+
if (!isRecord(data.tui)) {
|
|
73
|
+
delete data.tui
|
|
74
|
+
return data
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const tui = data.tui
|
|
78
|
+
delete data.tui
|
|
79
|
+
return {
|
|
80
|
+
...tui,
|
|
81
|
+
...data,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function installDeps(dir: string): Promise<void> {
|
|
86
|
+
return Config.installDependencies(dir)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function mergeFile(acc: Acc, file: string) {
|
|
90
|
+
const data = await loadFile(file)
|
|
91
|
+
acc.result = mergeInfo(acc.result, data)
|
|
92
|
+
if (!data.plugin?.length) return
|
|
93
|
+
|
|
94
|
+
const scope = pluginScope(file)
|
|
95
|
+
for (const item of data.plugin) {
|
|
96
|
+
acc.entries.push({
|
|
97
|
+
item,
|
|
98
|
+
meta: {
|
|
99
|
+
scope,
|
|
100
|
+
source: file,
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const state = Instance.state(async () => {
|
|
107
|
+
let projectFiles = Flag.OPENCODE_DISABLE_PROJECT_CONFIG
|
|
108
|
+
? []
|
|
109
|
+
: await ConfigPaths.projectFiles("tui", Instance.directory, Instance.worktree)
|
|
110
|
+
const directories = await ConfigPaths.directories(Instance.directory, Instance.worktree)
|
|
111
|
+
const custom = customPath()
|
|
112
|
+
const managed = Config.managedConfigDir()
|
|
113
|
+
await migrateTuiConfig({ directories, custom, managed })
|
|
114
|
+
// Re-compute after migration since migrateTuiConfig may have created new tui.json files
|
|
115
|
+
projectFiles = Flag.OPENCODE_DISABLE_PROJECT_CONFIG
|
|
116
|
+
? []
|
|
117
|
+
: await ConfigPaths.projectFiles("tui", Instance.directory, Instance.worktree)
|
|
118
|
+
|
|
119
|
+
const acc: Acc = {
|
|
120
|
+
result: {},
|
|
121
|
+
entries: [],
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
for (const file of ConfigPaths.fileInDirectory(Global.Path.config, "tui")) {
|
|
125
|
+
await mergeFile(acc, file)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (custom) {
|
|
129
|
+
await mergeFile(acc, custom)
|
|
130
|
+
log.debug("loaded custom tui config", { path: custom })
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
for (const file of projectFiles) {
|
|
134
|
+
await mergeFile(acc, file)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (const dir of unique(directories)) {
|
|
138
|
+
if (!dir.endsWith(".toolkode") && dir !== Flag.OPENCODE_CONFIG_DIR) continue
|
|
139
|
+
for (const file of ConfigPaths.fileInDirectory(dir, "tui")) {
|
|
140
|
+
await mergeFile(acc, file)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (existsSync(managed)) {
|
|
145
|
+
for (const file of ConfigPaths.fileInDirectory(managed, "tui")) {
|
|
146
|
+
await mergeFile(acc, file)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const merged = dedupePlugins(acc.entries)
|
|
151
|
+
acc.result.keybinds = Config.Keybinds.parse(acc.result.keybinds ?? {})
|
|
152
|
+
acc.result.plugin = merged.map((item) => item.item)
|
|
153
|
+
acc.result.plugin_meta = merged.length
|
|
154
|
+
? Object.fromEntries(merged.map((item) => [Config.pluginSpecifier(item.item), item.meta]))
|
|
155
|
+
: undefined
|
|
156
|
+
|
|
157
|
+
const deps: Promise<void>[] = []
|
|
158
|
+
if (acc.result.plugin?.length) {
|
|
159
|
+
for (const dir of unique(directories)) {
|
|
160
|
+
if (!dir.endsWith(".toolkode") && dir !== Flag.OPENCODE_CONFIG_DIR) continue
|
|
161
|
+
deps.push(installDeps(dir))
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
config: acc.result,
|
|
167
|
+
deps,
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
export async function get() {
|
|
172
|
+
return state().then((x) => x.config)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function waitForDependencies() {
|
|
176
|
+
const deps = await state().then((x) => x.deps)
|
|
177
|
+
await Promise.all(deps)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function loadFile(filepath: string): Promise<Info> {
|
|
181
|
+
const text = await ConfigPaths.readFile(filepath)
|
|
182
|
+
if (!text) return {}
|
|
183
|
+
return load(text, filepath).catch((error) => {
|
|
184
|
+
log.warn("failed to load tui config", { path: filepath, error })
|
|
185
|
+
return {}
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function load(text: string, configFilepath: string): Promise<Info> {
|
|
190
|
+
const raw = await ConfigPaths.parseText(text, configFilepath, "empty")
|
|
191
|
+
if (!isRecord(raw)) return {}
|
|
192
|
+
|
|
193
|
+
// Flatten a nested "tui" key so users who wrote `{ "tui": { ... } }` inside tui.json
|
|
194
|
+
// (mirroring the old toolkode.json shape) still get their settings applied.
|
|
195
|
+
const normalized = normalize(raw)
|
|
196
|
+
|
|
197
|
+
const parsed = Info.safeParse(normalized)
|
|
198
|
+
if (!parsed.success) {
|
|
199
|
+
log.warn("invalid tui config", { path: configFilepath, issues: parsed.error.issues })
|
|
200
|
+
return {}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const data = parsed.data
|
|
204
|
+
if (data.plugin) {
|
|
205
|
+
for (let i = 0; i < data.plugin.length; i++) {
|
|
206
|
+
data.plugin[i] = await Config.resolvePluginSpec(data.plugin[i], configFilepath)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return data
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { lazy } from "@/util/lazy"
|
|
2
|
+
import type { Adaptor } from "../types"
|
|
3
|
+
|
|
4
|
+
const ADAPTORS: Record<string, () => Promise<Adaptor>> = {
|
|
5
|
+
worktree: lazy(async () => (await import("./worktree")).WorktreeAdaptor),
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function getAdaptor(type: string): Promise<Adaptor> {
|
|
9
|
+
return ADAPTORS[type]()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function installAdaptor(type: string, adaptor: Adaptor) {
|
|
13
|
+
// This is experimental: mostly used for testing right now, but we
|
|
14
|
+
// will likely allow this in the future. Need to figure out the
|
|
15
|
+
// TypeScript story
|
|
16
|
+
|
|
17
|
+
// @ts-expect-error we force the builtin types right now, but we
|
|
18
|
+
// will implement a way to extend the types for custom adaptors
|
|
19
|
+
ADAPTORS[type] = () => adaptor
|
|
20
|
+
}
|