diff --git a/.opencode/agent/duplicate-pr.md b/.opencode/agent/duplicate-pr.md deleted file mode 100644 index c9c932ef790d..000000000000 --- a/.opencode/agent/duplicate-pr.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -mode: primary -hidden: true -model: opencode/claude-haiku-4-5 -color: "#E67E22" -tools: - "*": false - "github-pr-search": true ---- - -You are a duplicate PR detection agent. When a PR is opened, your job is to search for potentially duplicate or related open PRs. - -Use the github-pr-search tool to search for PRs that might be addressing the same issue or feature. - -IMPORTANT: The input will contain a line `CURRENT_PR_NUMBER: NNNN`. This is the current PR number, you should not mark that the current PR as a duplicate of itself. - -Search using keywords from the PR title and description. Try multiple searches with different relevant terms. - -If you find potential duplicates: - -- List them with their titles and URLs -- Briefly explain why they might be related - -If no duplicates are found, say so clearly. BUT ONLY SAY "No duplicate PRs found" (don't say anything else if no dups) - -Keep your response concise and actionable. diff --git a/.opencode/agent/translator.md b/.opencode/agent/translator.md deleted file mode 100644 index a987d01927b6..000000000000 --- a/.opencode/agent/translator.md +++ /dev/null @@ -1,900 +0,0 @@ ---- -description: Translate content for a specified locale while preserving technical terms -mode: subagent -model: opencode/gpt-5.4 ---- - -You are a professional translator and localization specialist. - -Translate the user's content into the requested target locale (language + region, e.g. fr-FR, de-DE). - -Requirements: - -- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure). -- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks. -- Also preserve every term listed in the Do-Not-Translate glossary below. -- Also apply locale-specific guidance from `.opencode/glossary/.md` when available (for example, `zh-cn.md`). -- Do not modify fenced code blocks. -- Output ONLY the translation (no commentary). - -If the target locale is missing, ask the user to provide it. -If no locale-specific glossary exists, use the global glossary only. - ---- - -# Locale-Specific Glossaries - -When a locale glossary exists, use it to: - -- Apply preferred wording for recurring UI/docs terms in that locale -- Preserve locale-specific do-not-translate terms and casing decisions -- Prefer natural phrasing over literal translation when the locale file calls it out -- If the repo uses a locale alias slug, apply that file too (for example, `pt-BR` maps to `br.md` in this repo) - -Locale guidance does not override code/command preservation rules or the global Do-Not-Translate glossary below. - ---- - -# Do-Not-Translate Terms (OpenCode Docs) - -Generated from: `packages/web/src/content/docs/*.mdx` (default English docs) -Generated on: 2026-02-10 - -Use this as a translation QA checklist / glossary. Preserve listed terms exactly (spelling, casing, punctuation). - -General rules (verbatim, even if not listed below): - -- Anything inside inline code (single backticks) or fenced code blocks (triple backticks) -- MDX/JS code in docs: `import ... from "..."`, component tags, identifiers -- CLI commands, flags, config keys/values, file paths, URLs/domains, and env vars - -## Proper nouns and product names - -Additional (not reliably captured via link text): - -```text -Astro -Bun -Chocolatey -Cursor -Docker -Git -GitHub Actions -GitLab CI -GNOME Terminal -Homebrew -Mise -Neovim -Node.js -npm -Obsidian -opencode -opencode-ai -Paru -pnpm -ripgrep -Scoop -SST -Starlight -Visual Studio Code -VS Code -VSCodium -Windsurf -Windows Terminal -Yarn -Zellij -Zed -anomalyco -``` - -Extracted from link labels in the English docs (review and prune as desired): - -```text -@openspoon/subtask2 -302.AI console -ACP progress report -Agent Client Protocol -Agent Skills -Agentic -AGENTS.md -AI SDK -Alacritty -Anthropic -Anthropic's Data Policies -Atom One -Avante.nvim -Ayu -Azure AI Foundry -Azure portal -Baseten -built-in GITHUB_TOKEN -Bun.$ -Catppuccin -Cerebras console -ChatGPT Plus or Pro -Cloudflare dashboard -CodeCompanion.nvim -CodeNomad -Configuring Adapters: Environment Variables -Context7 MCP server -Cortecs console -Deep Infra dashboard -DeepSeek console -Duo Agent Platform -Everforest -Fireworks AI console -Firmware dashboard -Ghostty -GitLab CLI agents docs -GitLab docs -GitLab User Settings > Access Tokens -Granular Rules (Object Syntax) -Grep by Vercel -Groq console -Gruvbox -Helicone -Helicone documentation -Helicone Header Directory -Helicone's Model Directory -Hugging Face Inference Providers -Hugging Face settings -install WSL -IO.NET console -JetBrains IDE -Kanagawa -Kitty -MiniMax API Console -Models.dev -Moonshot AI console -Nebius Token Factory console -Nord -OAuth -Ollama integration docs -OpenAI's Data Policies -OpenChamber -OpenCode -OpenCode config -OpenCode Config -OpenCode TUI with the opencode theme -OpenCode Web - Active Session -OpenCode Web - New Session -OpenCode Web - See Servers -OpenCode Zen -OpenCode-Obsidian -OpenRouter dashboard -OpenWork -OVHcloud panel -Pro+ subscription -SAP BTP Cockpit -Scaleway Console IAM settings -Scaleway Generative APIs -SDK documentation -Sentry MCP server -shell API -Together AI console -Tokyonight -Unified Billing -Venice AI console -Vercel dashboard -WezTerm -Windows Subsystem for Linux (WSL) -WSL -WSL (Windows Subsystem for Linux) -WSL extension -xAI console -Z.AI API console -Zed -ZenMux dashboard -Zod -``` - -## Acronyms and initialisms - -```text -ACP -AGENTS -AI -AI21 -ANSI -API -AST -AWS -BTP -CD -CDN -CI -CLI -CMD -CORS -DEBUG -EKS -ERROR -FAQ -GLM -GNOME -GPT -HTML -HTTP -HTTPS -IAM -ID -IDE -INFO -IO -IP -IRSA -JS -JSON -JSONC -K2 -LLM -LM -LSP -M2 -MCP -MR -NET -NPM -NTLM -OIDC -OS -PAT -PATH -PHP -PR -PTY -README -RFC -RPC -SAP -SDK -SKILL -SSE -SSO -TS -TTY -TUI -UI -URL -US -UX -VCS -VPC -VPN -VS -WARN -WSL -X11 -YAML -``` - -## Code identifiers used in prose (CamelCase, mixedCase) - -```text -apiKey -AppleScript -AssistantMessage -baseURL -BurntSushi -ChatGPT -ClangFormat -CodeCompanion -CodeNomad -DeepSeek -DefaultV2 -FileContent -FileDiff -FileNode -fineGrained -FormatterStatus -GitHub -GitLab -iTerm2 -JavaScript -JetBrains -macOS -mDNS -MiniMax -NeuralNomadsAI -NickvanDyke -NoeFabris -OpenAI -OpenAPI -OpenChamber -OpenCode -OpenRouter -OpenTUI -OpenWork -ownUserPermissions -PowerShell -ProviderAuthAuthorization -ProviderAuthMethod -ProviderInitError -SessionStatus -TabItem -tokenType -ToolIDs -ToolList -TypeScript -typesUrl -UserMessage -VcsInfo -WebView2 -WezTerm -xAI -ZenMux -``` - -## OpenCode CLI commands (as shown in docs) - -```text -opencode -opencode [project] -opencode /path/to/project -opencode acp -opencode agent [command] -opencode agent create -opencode agent list -opencode attach [url] -opencode attach http://10.20.30.40:4096 -opencode attach http://localhost:4096 -opencode auth [command] -opencode auth list -opencode auth login -opencode auth logout -opencode auth ls -opencode export [sessionID] -opencode github [command] -opencode github install -opencode github run -opencode import -opencode import https://opncd.ai/s/abc123 -opencode import session.json -opencode mcp [command] -opencode mcp add -opencode mcp auth [name] -opencode mcp auth list -opencode mcp auth ls -opencode mcp auth my-oauth-server -opencode mcp auth sentry -opencode mcp debug -opencode mcp debug my-oauth-server -opencode mcp list -opencode mcp logout [name] -opencode mcp logout my-oauth-server -opencode mcp ls -opencode models --refresh -opencode models [provider] -opencode models anthropic -opencode run [message..] -opencode run Explain the use of context in Go -opencode serve -opencode serve --cors http://localhost:5173 --cors https://app.example.com -opencode serve --hostname 0.0.0.0 --port 4096 -opencode serve [--port ] [--hostname ] [--cors ] -opencode session [command] -opencode session list -opencode session delete -opencode stats -opencode uninstall -opencode upgrade -opencode upgrade [target] -opencode upgrade v0.1.48 -opencode web -opencode web --cors https://example.com -opencode web --hostname 0.0.0.0 -opencode web --mdns -opencode web --mdns --mdns-domain myproject.local -opencode web --port 4096 -opencode web --port 4096 --hostname 0.0.0.0 -opencode.server.close() -``` - -## Slash commands and routes - -```text -/agent -/auth/:id -/clear -/command -/config -/config/providers -/connect -/continue -/doc -/editor -/event -/experimental/tool?provider=

&model= -/experimental/tool/ids -/export -/file?path= -/file/content?path=

-/file/status -/find?pattern= -/find/file -/find/file?query= -/find/symbol?query= -/formatter -/global/event -/global/health -/help -/init -/instance/dispose -/log -/lsp -/mcp -/mnt/ -/mnt/c/ -/mnt/d/ -/models -/oc -/opencode -/path -/project -/project/current -/provider -/provider/{id}/oauth/authorize -/provider/{id}/oauth/callback -/provider/auth -/q -/quit -/redo -/resume -/session -/session/:id -/session/:id/abort -/session/:id/children -/session/:id/command -/session/:id/diff -/session/:id/fork -/session/:id/init -/session/:id/message -/session/:id/message/:messageID -/session/:id/permissions/:permissionID -/session/:id/prompt_async -/session/:id/revert -/session/:id/share -/session/:id/shell -/session/:id/summarize -/session/:id/todo -/session/:id/unrevert -/session/status -/share -/summarize -/theme -/tui -/tui/append-prompt -/tui/clear-prompt -/tui/control/next -/tui/control/response -/tui/execute-command -/tui/open-help -/tui/open-models -/tui/open-sessions -/tui/open-themes -/tui/show-toast -/tui/submit-prompt -/undo -/Users/username -/Users/username/projects/* -/vcs -``` - -## CLI flags and short options - -```text ---agent ---attach ---command ---continue ---cors ---cwd ---days ---dir ---dry-run ---event ---file ---force ---fork ---format ---help ---hostname ---hostname 0.0.0.0 ---keep-config ---keep-data ---log-level ---max-count ---mdns ---mdns-domain ---method ---model ---models ---port ---print-logs ---project ---prompt ---refresh ---session ---share ---title ---token ---tools ---verbose ---version ---wait - --c --d --f --h --m --n --s --v -``` - -## Environment variables - -```text -AI_API_URL -AI_FLOW_CONTEXT -AI_FLOW_EVENT -AI_FLOW_INPUT -AICORE_DEPLOYMENT_ID -AICORE_RESOURCE_GROUP -AICORE_SERVICE_KEY -ANTHROPIC_API_KEY -AWS_ACCESS_KEY_ID -AWS_BEARER_TOKEN_BEDROCK -AWS_PROFILE -AWS_REGION -AWS_ROLE_ARN -AWS_SECRET_ACCESS_KEY -AWS_WEB_IDENTITY_TOKEN_FILE -AZURE_COGNITIVE_SERVICES_RESOURCE_NAME -AZURE_RESOURCE_NAME -CI_PROJECT_DIR -CI_SERVER_FQDN -CI_WORKLOAD_REF -CLOUDFLARE_ACCOUNT_ID -CLOUDFLARE_API_TOKEN -CLOUDFLARE_GATEWAY_ID -CONTEXT7_API_KEY -GITHUB_TOKEN -GITLAB_AI_GATEWAY_URL -GITLAB_HOST -GITLAB_INSTANCE_URL -GITLAB_OAUTH_CLIENT_ID -GITLAB_TOKEN -GITLAB_TOKEN_OPENCODE -GOOGLE_APPLICATION_CREDENTIALS -GOOGLE_CLOUD_PROJECT -HTTP_PROXY -HTTPS_PROXY -K2_ -MY_API_KEY -MY_ENV_VAR -MY_MCP_CLIENT_ID -MY_MCP_CLIENT_SECRET -NO_PROXY -NODE_ENV -NODE_EXTRA_CA_CERTS -NPM_AUTH_TOKEN -OC_ALLOW_WAYLAND -OPENCODE_API_KEY -OPENCODE_AUTH_JSON -OPENCODE_AUTO_SHARE -OPENCODE_CLIENT -OPENCODE_CONFIG -OPENCODE_CONFIG_CONTENT -OPENCODE_CONFIG_DIR -OPENCODE_DISABLE_AUTOCOMPACT -OPENCODE_DISABLE_AUTOUPDATE -OPENCODE_DISABLE_CLAUDE_CODE -OPENCODE_DISABLE_CLAUDE_CODE_PROMPT -OPENCODE_DISABLE_CLAUDE_CODE_SKILLS -OPENCODE_DISABLE_DEFAULT_PLUGINS -OPENCODE_DISABLE_FILETIME_CHECK -OPENCODE_DISABLE_LSP_DOWNLOAD -OPENCODE_DISABLE_MODELS_FETCH -OPENCODE_DISABLE_PRUNE -OPENCODE_DISABLE_TERMINAL_TITLE -OPENCODE_ENABLE_EXA -OPENCODE_ENABLE_EXPERIMENTAL_MODELS -OPENCODE_EXPERIMENTAL -OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS -OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT -OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER -OPENCODE_EXPERIMENTAL_EXA -OPENCODE_EXPERIMENTAL_FILEWATCHER -OPENCODE_EXPERIMENTAL_ICON_DISCOVERY -OPENCODE_EXPERIMENTAL_LSP_TOOL -OPENCODE_EXPERIMENTAL_LSP_TY -OPENCODE_EXPERIMENTAL_MARKDOWN -OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX -OPENCODE_EXPERIMENTAL_OXFMT -OPENCODE_EXPERIMENTAL_PLAN_MODE -OPENCODE_ENABLE_QUESTION_TOOL -OPENCODE_FAKE_VCS -OPENCODE_GIT_BASH_PATH -OPENCODE_MODEL -OPENCODE_MODELS_URL -OPENCODE_PERMISSION -OPENCODE_PORT -OPENCODE_SERVER_PASSWORD -OPENCODE_SERVER_USERNAME -PROJECT_ROOT -RESOURCE_NAME -RUST_LOG -VARIABLE_NAME -VERTEX_LOCATION -XDG_CONFIG_HOME -``` - -## Package/module identifiers - -```text -../../../config.mjs -@astrojs/starlight/components -@opencode-ai/plugin -@opencode-ai/sdk -path -shescape -zod - -@ -@ai-sdk/anthropic -@ai-sdk/cerebras -@ai-sdk/google -@ai-sdk/openai -@ai-sdk/openai-compatible -@File#L37-42 -@modelcontextprotocol/server-everything -@opencode -``` - -## GitHub owner/repo slugs referenced in docs - -```text -24601/opencode-zellij-namer -angristan/opencode-wakatime -anomalyco/opencode -apps/opencode-agent -athal7/opencode-devcontainers -awesome-opencode/awesome-opencode -backnotprop/plannotator -ben-vargas/ai-sdk-provider-opencode-sdk -btriapitsyn/openchamber -BurntSushi/ripgrep -Cluster444/agentic -code-yeongyu/oh-my-opencode -darrenhinde/opencode-agents -different-ai/opencode-scheduler -different-ai/openwork -features/copilot -folke/tokyonight.nvim -franlol/opencode-md-table-formatter -ggml-org/llama.cpp -ghoulr/opencode-websearch-cited.git -H2Shami/opencode-helicone-session -hosenur/portal -jamesmurdza/daytona -jenslys/opencode-gemini-auth -JRedeker/opencode-morph-fast-apply -JRedeker/opencode-shell-strategy -kdcokenny/ocx -kdcokenny/opencode-background-agents -kdcokenny/opencode-notify -kdcokenny/opencode-workspace -kdcokenny/opencode-worktree -login/device -mohak34/opencode-notifier -morhetz/gruvbox -mtymek/opencode-obsidian -NeuralNomadsAI/CodeNomad -nick-vi/opencode-type-inject -NickvanDyke/opencode.nvim -NoeFabris/opencode-antigravity-auth -nordtheme/nord -numman-ali/opencode-openai-codex-auth -olimorris/codecompanion.nvim -panta82/opencode-notificator -rebelot/kanagawa.nvim -remorses/kimaki -sainnhe/everforest -shekohex/opencode-google-antigravity-auth -shekohex/opencode-pty.git -spoons-and-mirrors/subtask2 -sudo-tee/opencode.nvim -supermemoryai/opencode-supermemory -Tarquinen/opencode-dynamic-context-pruning -Th3Whit3Wolf/one-nvim -upstash/context7 -vtemian/micode -vtemian/octto -yetone/avante.nvim -zenobi-us/opencode-plugin-template -zenobi-us/opencode-skillful -``` - -## Paths, filenames, globs, and URLs - -```text -./.opencode/themes/*.json -.//storage/ -./config/#custom-directory -./global/storage/ -.agents/skills/*/SKILL.md -.agents/skills//SKILL.md -.clang-format -.claude -.claude/skills -.claude/skills/*/SKILL.md -.claude/skills//SKILL.md -.env -.github/workflows/opencode.yml -.gitignore -.gitlab-ci.yml -.ignore -.NET SDK -.npmrc -.ocamlformat -.opencode -.opencode/ -.opencode/agents/ -.opencode/commands/ -.opencode/commands/test.md -.opencode/modes/ -.opencode/plans/*.md -.opencode/plugins/ -.opencode/skills//SKILL.md -.opencode/skills/git-release/SKILL.md -.opencode/tools/ -.well-known/opencode -{ type: "raw" \| "patch", content: string } -{file:path/to/file} -**/*.js -%USERPROFILE%/intelephense/license.txt -%USERPROFILE%\.cache\opencode -%USERPROFILE%\.config\opencode\opencode.jsonc -%USERPROFILE%\.config\opencode\plugins -%USERPROFILE%\.local\share\opencode -%USERPROFILE%\.local\share\opencode\log -/.opencode/themes/*.json -/ -/.opencode/plugins/ -~ -~/... -~/.agents/skills/*/SKILL.md -~/.agents/skills//SKILL.md -~/.aws/credentials -~/.bashrc -~/.cache/opencode -~/.cache/opencode/node_modules/ -~/.claude/CLAUDE.md -~/.claude/skills/ -~/.claude/skills/*/SKILL.md -~/.claude/skills//SKILL.md -~/.config/opencode -~/.config/opencode/AGENTS.md -~/.config/opencode/agents/ -~/.config/opencode/commands/ -~/.config/opencode/modes/ -~/.config/opencode/opencode.json -~/.config/opencode/opencode.jsonc -~/.config/opencode/plugins/ -~/.config/opencode/skills/*/SKILL.md -~/.config/opencode/skills//SKILL.md -~/.config/opencode/themes/*.json -~/.config/opencode/tools/ -~/.config/zed/settings.json -~/.local/share -~/.local/share/opencode/ -~/.local/share/opencode/auth.json -~/.local/share/opencode/log/ -~/.local/share/opencode/mcp-auth.json -~/.local/share/opencode/opencode.jsonc -~/.npmrc -~/.zshrc -~/code/ -~/Library/Application Support -~/projects/* -~/projects/personal/ -${config.github}/blob/dev/packages/sdk/js/src/gen/types.gen.ts -$HOME/intelephense/license.txt -$HOME/projects/* -$XDG_CONFIG_HOME/opencode/themes/*.json -agent/ -agents/ -build/ -commands/ -dist/ -http://:4096 -http://127.0.0.1:8080/callback -http://localhost: -http://localhost:4096 -http://localhost:4096/doc -https://app.example.com -https://AZURE_COGNITIVE_SERVICES_RESOURCE_NAME.cognitiveservices.azure.com/ -https://opencode.ai/zen/v1/chat/completions -https://opencode.ai/zen/v1/messages -https://opencode.ai/zen/v1/models/gemini-3-flash -https://opencode.ai/zen/v1/models/gemini-3-pro -https://opencode.ai/zen/v1/responses -https://RESOURCE_NAME.openai.azure.com/ -laravel/pint -log/ -model: "anthropic/claude-sonnet-4-5" -modes/ -node_modules/ -openai/gpt-4.1 -opencode.ai/config.json -opencode/ -opencode/gpt-5.1-codex -opencode/gpt-5.2-codex -opencode/kimi-k2 -openrouter/google/gemini-2.5-flash -opncd.ai/s/ -packages/*/AGENTS.md -plugins/ -project/ -provider_id/model_id -provider/model -provider/model-id -rm -rf ~/.cache/opencode -skills/ -skills/*/SKILL.md -src/**/*.ts -themes/ -tools/ -``` - -## Keybind strings - -```text -alt+b -Alt+Ctrl+K -alt+d -alt+f -Cmd+Esc -Cmd+Option+K -Cmd+Shift+Esc -Cmd+Shift+G -Cmd+Shift+P -ctrl+a -ctrl+b -ctrl+d -ctrl+e -Ctrl+Esc -ctrl+f -ctrl+g -ctrl+k -Ctrl+Shift+Esc -Ctrl+Shift+P -ctrl+t -ctrl+u -ctrl+w -ctrl+x -DELETE -Shift+Enter -WIN+R -``` - -## Model ID strings referenced - -```text -{env:OPENCODE_MODEL} -anthropic/claude-3-5-sonnet-20241022 -anthropic/claude-haiku-4-20250514 -anthropic/claude-haiku-4-5 -anthropic/claude-sonnet-4-20250514 -anthropic/claude-sonnet-4-5 -gitlab/duo-chat-haiku-4-5 -lmstudio/google/gemma-3n-e4b -openai/gpt-4.1 -openai/gpt-5 -opencode/gpt-5.1-codex -opencode/gpt-5.2-codex -opencode/kimi-k2 -openrouter/google/gemini-2.5-flash -``` diff --git a/.opencode/agent/triage.md b/.opencode/agent/triage.md deleted file mode 100644 index a77b92737bc9..000000000000 --- a/.opencode/agent/triage.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -mode: primary -hidden: true -model: opencode/minimax-m2.5 -color: "#44BA81" -tools: - "*": false - "github-triage": true ---- - -You are a triage agent responsible for triaging github issues. - -Use your github-triage tool to triage issues. - -This file is the source of truth for ownership/routing rules. - -## Labels - -### windows - -Use for any issue that mentions Windows (the OS). Be sure they are saying that they are on Windows. - -- Use if they mention WSL too - -#### perf - -Performance-related issues: - -- Slow performance -- High RAM usage -- High CPU usage - -**Only** add if it's likely a RAM or CPU issue. **Do not** add for LLM slowness. - -#### desktop - -Desktop app issues: - -- `opencode web` command -- The desktop app itself - -**Only** add if it's specifically about the Desktop application or `opencode web` view. **Do not** add for terminal, TUI, or general opencode issues. - -#### nix - -**Only** add if the issue explicitly mentions nix. - -If the issue does not mention nix, do not add nix. - -If the issue mentions nix, assign to `rekram1-node`. - -#### zen - -**Only** add if the issue mentions "zen" or "opencode zen" or "opencode black". - -If the issue doesn't have "zen" or "opencode black" in it then don't add zen label - -#### core - -Use for core server issues in `packages/opencode/`, excluding `packages/opencode/src/cli/cmd/tui/`. - -Examples: - -- LSP server behavior -- Harness behavior (agent + tools) -- Feature requests for server behavior -- Agent context construction -- API endpoints -- Provider integration issues -- New, broken, or poor-quality models - -#### acp - -If the issue mentions acp support, assign acp label. - -#### docs - -Add if the issue requests better documentation or docs updates. - -#### opentui - -TUI issues potentially caused by our underlying TUI library: - -- Keybindings not working -- Scroll speed issues (too fast/slow/laggy) -- Screen flickering -- Crashes with opentui in the log - -**Do not** add for general TUI bugs. - -When assigning to people here are the following rules: - -Desktop / Web: -Use for desktop-labeled issues only. - -- adamdotdevin -- iamdavidhill -- Brendonovich -- nexxeln - -Zen: -ONLY assign if the issue will have the "zen" label. - -- fwang -- MrMushrooooom - -TUI (`packages/opencode/src/cli/cmd/tui/...`): - -- thdxr for TUI UX/UI product decisions and interaction flow -- kommander for OpenTUI engine issues: rendering artifacts, keybind handling, terminal compatibility, SSH behavior, and low-level perf bottlenecks -- rekram1-node for TUI bugs that are not clearly OpenTUI engine issues - -Core (`packages/opencode/...`, excluding TUI subtree): - -- thdxr for sqlite/snapshot/memory bugs and larger architectural core features -- jlongster for opencode server + API feature work (tool currently remaps jlongster -> thdxr until assignable) -- rekram1-node for harness issues, provider issues, and other bug-squashing - -For core bugs that do not clearly map, either thdxr or rekram1-node is acceptable. - -Docs: - -- R44VC0RP - -Windows: - -- Hona (assign any issue that mentions Windows or is likely Windows-specific) - -Determinism rules: - -- If title + body does not contain "zen", do not add the "zen" label -- If "nix" label is added but title + body does not mention nix/nixos, the tool will drop "nix" -- If title + body mentions nix/nixos, assign to `rekram1-node` -- If "desktop" label is added, the tool will override assignee and randomly pick one Desktop / Web owner - -In all other cases, choose the team/section with the most overlap with the issue and assign a member from that team at random. - -ACP: - -- rekram1-node (assign any acp issues to rekram1-node) diff --git a/.opencode/command/ai-deps.md b/.opencode/command/ai-deps.md deleted file mode 100644 index 83783d5b9be0..000000000000 --- a/.opencode/command/ai-deps.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -description: "Bump AI sdk dependencies minor / patch versions only" ---- - -Please read @package.json and @packages/opencode/package.json. - -Your job is to look into AI SDK dependencies, figure out if they have versions that can be upgraded (minor or patch versions ONLY no major ignore major changes). - -I want a report of every dependency and the version that can be upgraded to. -What would be even better is if you can give me brief summary of the changes for each dep and a link to the changelog for each dependency, or at least some reference info so I can see what bugs were fixed or new features were added. - -Consider using subagents for each dep to save your context window. - -Here is a short list of some deps (please be comprehensive tho): - -- "ai" -- "@ai-sdk/openai" -- "@ai-sdk/anthropic" -- "@openrouter/ai-sdk-provider" -- etc, etc - -DO NOT upgrade the dependencies yet, just make a list of all dependencies and their versions that can be upgraded to minor or patch versions only. - -Write up your findings to ai-sdk-updates.md diff --git a/.opencode/command/changelog.md b/.opencode/command/changelog.md deleted file mode 100644 index 4cd30a704a4a..000000000000 --- a/.opencode/command/changelog.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -model: opencode/gpt-5.4 ---- - -Create `UPCOMING_CHANGELOG.md` from the structured changelog input below. -If `UPCOMING_CHANGELOG.md` already exists, ignore its current contents completely. -Do not preserve, merge, or reuse text from the existing file. - -The input already contains the exact commit range since the last non-draft release. -The commits are already filtered to the release-relevant packages and grouped into -the release sections. Do not fetch GitHub releases, PRs, or build your own commit list. -The input may also include a `## Community Contributors Input` section. - -Before writing any entry you keep, inspect the real diff with -`git show --stat --format='' ` or `git show --format='' ` so you can -understand the actual code changes and not just the commit message (they may be misleading). -Do not use `git log` or author metadata when deciding attribution. - -Rules: - -- Write the final file with sections in this order: - `## Core`, `## TUI`, `## Desktop`, `## SDK`, `## Extensions` -- Only include sections that have at least one notable entry -- Keep one bullet per commit you keep -- Skip commits that are entirely internal, CI, tests, refactors, or otherwise not user-facing -- Start each bullet with a capital letter -- Prefer what changed for users over what code changed internally -- Do not copy raw commit prefixes like `fix:` or `feat:` or trailing PR numbers like `(#123)` -- Community attribution is deterministic: only preserve an existing `(@username)` suffix from the changelog input -- If an input bullet has no `(@username)` suffix, do not add one -- Never add a new `(@username)` suffix from `git show`, commit authors, names, or email addresses -- If no notable entries remain and there is no contributor block, write exactly `No notable changes.` -- If no notable entries remain but there is a contributor block, omit all release sections and return only the contributor block -- If the input contains `## Community Contributors Input`, append the block below that heading to the end of the final file verbatim -- Do not add, remove, rewrite, or reorder contributor names or commit titles in that block -- Do not derive the thank-you section from the main summary bullets -- Do not include the heading `## Community Contributors Input` in the final file -- Focus on writing the least words to get your point across - users will skim read the changelog, so we should be precise - -**Importantly, the changelog is for users (who are at least slightly technical), they may use the TUI, Desktop, SDK, Plugins and so forth. Be thorough in understanding flow on effects may not be immediately apparent. e.g. a package upgrade looks internal but may patch a bug. Or a refactor may also stabilise some race condition that fixes bugs for users. The PR title/body + commit message will give you the authors context, usually containing the outcome not just technical detail** - - - -!`bun script/raw-changelog.ts $ARGUMENTS` - - diff --git a/.opencode/command/commit.md b/.opencode/command/commit.md deleted file mode 100644 index e88932a24485..000000000000 --- a/.opencode/command/commit.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -description: git commit and push -model: opencode/kimi-k2.5 -subtask: true ---- - -commit and push - -make sure it includes a prefix like -docs: -tui: -core: -ci: -ignore: -wip: - -For anything in the packages/web use the docs: prefix. - -prefer to explain WHY something was done from an end user perspective instead of -WHAT was done. - -do not do generic messages like "improved agent experience" be very specific -about what user facing changes were made - -if there are conflicts DO NOT FIX THEM. notify me and I will fix them - -## GIT DIFF - -!`git diff` - -## GIT DIFF --cached - -!`git diff --cached` - -## GIT STATUS --short - -!`git status --short` diff --git a/.opencode/command/issues.md b/.opencode/command/issues.md deleted file mode 100644 index 75b59616743f..000000000000 --- a/.opencode/command/issues.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: "find issue(s) on github" -model: opencode/claude-haiku-4-5 ---- - -Search through existing issues in anomalyco/opencode using the gh cli to find issues matching this query: - -$ARGUMENTS - -Consider: - -1. Similar titles or descriptions -2. Same error messages or symptoms -3. Related functionality or components -4. Similar feature requests - -Please list any matching issues with: - -- Issue number and title -- Brief explanation of why it matches the query -- Link to the issue - -If no clear matches are found, say so. diff --git a/.opencode/command/learn.md b/.opencode/command/learn.md deleted file mode 100644 index fe4965a5887e..000000000000 --- a/.opencode/command/learn.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -description: Extract non-obvious learnings from session to AGENTS.md files to build codebase understanding ---- - -Analyze this session and extract non-obvious learnings to add to AGENTS.md files. - -AGENTS.md files can exist at any directory level, not just the project root. When an agent reads a file, any AGENTS.md in parent directories are automatically loaded into the context of the tool read. Place learnings as close to the relevant code as possible: - -- Project-wide learnings → root AGENTS.md -- Package/module-specific → packages/foo/AGENTS.md -- Feature-specific → src/auth/AGENTS.md - -What counts as a learning (non-obvious discoveries only): - -- Hidden relationships between files or modules -- Execution paths that differ from how code appears -- Non-obvious configuration, env vars, or flags -- Debugging breakthroughs when error messages were misleading -- API/tool quirks and workarounds -- Build/test commands not in README -- Architectural decisions and constraints -- Files that must change together - -What NOT to include: - -- Obvious facts from documentation -- Standard language/framework behavior -- Things already in an AGENTS.md -- Verbose explanations -- Session-specific details - -Process: - -1. Review session for discoveries, errors that took multiple attempts, unexpected connections -2. Determine scope - what directory does each learning apply to? -3. Read existing AGENTS.md files at relevant levels -4. Create or update AGENTS.md at the appropriate level -5. Keep entries to 1-3 lines per insight - -After updating, summarize which AGENTS.md files were created/updated and how many learnings per file. - -$ARGUMENTS diff --git a/.opencode/command/rmslop.md b/.opencode/command/rmslop.md deleted file mode 100644 index 02c9fc0844a7..000000000000 --- a/.opencode/command/rmslop.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -description: Remove AI code slop ---- - -Check the diff against dev, and remove all AI generated slop introduced in this branch. - -This includes: - -- Extra comments that a human wouldn't add or is inconsistent with the rest of the file -- Extra defensive checks or try/catch blocks that are abnormal for that area of the codebase (especially if called by trusted / validated codepaths) -- Casts to any to get around type issues -- Any other style that is inconsistent with the file -- Unnecessary emoji usage - -Report at the end with only a 1-3 sentence summary of what you changed diff --git a/.opencode/command/spellcheck.md b/.opencode/command/spellcheck.md deleted file mode 100644 index 0abf23c4fd09..000000000000 --- a/.opencode/command/spellcheck.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -description: spellcheck all markdown file changes ---- - -Look at all the unstaged changes to markdown (.md, .mdx) files, pull out the lines that have changed, and check for spelling and grammar errors. diff --git a/.opencode/env.d.ts b/.opencode/env.d.ts deleted file mode 100644 index f2b13a934c43..000000000000 --- a/.opencode/env.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module "*.txt" { - const content: string - export default content -} diff --git a/.opencode/glossary/README.md b/.opencode/glossary/README.md deleted file mode 100644 index 983900381ca9..000000000000 --- a/.opencode/glossary/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Locale Glossaries - -Use this folder for locale-specific translation guidance that supplements `.opencode/agent/translator.md`. - -The global glossary in `translator.md` remains the source of truth for shared do-not-translate terms (commands, code, paths, product names, etc.). These locale files capture community learnings about phrasing and terminology preferences. - -## File Naming - -- One file per locale -- Use lowercase locale slugs that match docs locales when possible (for example, `zh-cn.md`, `zh-tw.md`) -- If only language-level guidance exists, use the language code (for example, `fr.md`) -- Some repo locale slugs may be aliases/non-BCP47 for consistency (for example, `br` for Brazilian Portuguese / `pt-BR`) - -## What To Put In A Locale File - -- **Sources**: PRs/issues/discussions that motivated the guidance -- **Do Not Translate (Locale Additions)**: locale-specific terms or casing decisions -- **Preferred Terms**: recurring UI/docs words with preferred translations -- **Guidance**: tone, style, and consistency notes -- **Avoid** (optional): common literal translations or wording we should avoid -- If the repo uses a locale alias slug, document the alias in **Guidance** (for example, prose may mention `pt-BR` while config/examples use `br`) - -Prefer guidance that is: - -- Repeated across multiple docs/screens -- Easy to apply consistently -- Backed by a community contribution or review discussion - -## Template - -```md -# Glossary - -## Sources - -- PR #12345: https://github.com/anomalyco/opencode/pull/12345 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing) - -## Preferred Terms - -| English | Preferred | Notes | -| ------- | --------- | --------- | -| prompt | ... | preferred | -| session | ... | preferred | - -## Guidance - -- Prefer natural phrasing over literal translation - -## Avoid - -- Avoid ... when ... -``` - -## Contribution Notes - -- Mark entries as preferred when they may evolve -- Keep examples short -- Add or update the `Sources` section whenever you add a new rule -- Prefer PR-backed guidance over invented term mappings; start with general guidance if no term-level corrections exist yet diff --git a/.opencode/glossary/ar.md b/.opencode/glossary/ar.md deleted file mode 100644 index 37355522a0a5..000000000000 --- a/.opencode/glossary/ar.md +++ /dev/null @@ -1,28 +0,0 @@ -# ar Glossary - -## Sources - -- PR #9947: https://github.com/anomalyco/opencode/pull/9947 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural Arabic phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths -- For RTL text, treat code, commands, and paths as LTR artifacts and keep their character order unchanged - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple Arabic terms for the same recurring UI action once a preferred term is established diff --git a/.opencode/glossary/br.md b/.opencode/glossary/br.md deleted file mode 100644 index fd3e7251cd90..000000000000 --- a/.opencode/glossary/br.md +++ /dev/null @@ -1,34 +0,0 @@ -# br Glossary - -## Sources - -- PR #10086: https://github.com/anomalyco/opencode/pull/10086 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Locale code `br` in repo config, code, and paths (repo alias for Brazilian Portuguese) - -## Preferred Terms - -These are PR-backed locale naming preferences and may evolve. - -| English / Context | Preferred | Notes | -| ---------------------------------------- | ------------------------------ | ------------------------------------------------------------- | -| Brazilian Portuguese (prose locale name) | `pt-BR` | Use standard locale naming in prose when helpful | -| Repo locale slug (code/config) | `br` | PR #10086 uses `br` for consistency/simplicity | -| Browser locale detection | `pt`, `pt-br`, `pt-BR` -> `br` | Preserve this mapping in docs/examples about locale detection | - -## Guidance - -- This file covers Brazilian Portuguese (`pt-BR`), but the repo locale code is `br` -- Use natural Brazilian Portuguese phrasing over literal translation -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths -- Keep repo locale identifiers as implemented in code/config (`br`) even when prose mentions `pt-BR` - -## Avoid - -- Avoid changing repo locale code references from `br` to `pt-br` in code snippets, paths, or config examples -- Avoid mixing Portuguese variants when a Brazilian Portuguese form is established diff --git a/.opencode/glossary/bs.md b/.opencode/glossary/bs.md deleted file mode 100644 index aa3bd96f6f94..000000000000 --- a/.opencode/glossary/bs.md +++ /dev/null @@ -1,33 +0,0 @@ -# bs Glossary - -## Sources - -- PR #12283: https://github.com/anomalyco/opencode/pull/12283 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -These are PR-backed locale naming preferences and may evolve. - -| English / Context | Preferred | Notes | -| ---------------------------------- | ---------- | ------------------------------------------------- | -| Bosnian language label (UI) | `Bosanski` | PR #12283 tested switching language to `Bosanski` | -| Repo locale slug (code/config) | `bs` | Preserve in code, config, paths, and examples | -| Browser locale detection (Bosnian) | `bs` | PR #12283 added `bs` locale auto-detection | - -## Guidance - -- Use natural Bosnian phrasing over literal translation -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths -- Keep repo locale references as `bs` in code/config, and use `Bosanski` for the user-facing language name when applicable - -## Avoid - -- Avoid changing repo locale references from `bs` to another slug in code snippets or config examples -- Avoid translating product and protocol names that are fixed identifiers diff --git a/.opencode/glossary/da.md b/.opencode/glossary/da.md deleted file mode 100644 index e63222170109..000000000000 --- a/.opencode/glossary/da.md +++ /dev/null @@ -1,27 +0,0 @@ -# da Glossary - -## Sources - -- PR #9821: https://github.com/anomalyco/opencode/pull/9821 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural Danish phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple Danish terms for the same recurring UI action once a preferred term is established diff --git a/.opencode/glossary/de.md b/.opencode/glossary/de.md deleted file mode 100644 index 0d2c49faceae..000000000000 --- a/.opencode/glossary/de.md +++ /dev/null @@ -1,27 +0,0 @@ -# de Glossary - -## Sources - -- PR #9817: https://github.com/anomalyco/opencode/pull/9817 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural German phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple German terms for the same recurring UI action once a preferred term is established diff --git a/.opencode/glossary/es.md b/.opencode/glossary/es.md deleted file mode 100644 index dc9b977ecffa..000000000000 --- a/.opencode/glossary/es.md +++ /dev/null @@ -1,27 +0,0 @@ -# es Glossary - -## Sources - -- PR #9817: https://github.com/anomalyco/opencode/pull/9817 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural Spanish phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple Spanish terms for the same recurring UI action once a preferred term is established diff --git a/.opencode/glossary/fr.md b/.opencode/glossary/fr.md deleted file mode 100644 index 074c4de110a0..000000000000 --- a/.opencode/glossary/fr.md +++ /dev/null @@ -1,27 +0,0 @@ -# fr Glossary - -## Sources - -- PR #9821: https://github.com/anomalyco/opencode/pull/9821 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural French phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple French terms for the same recurring UI action once a preferred term is established diff --git a/.opencode/glossary/ja.md b/.opencode/glossary/ja.md deleted file mode 100644 index f0159ca96690..000000000000 --- a/.opencode/glossary/ja.md +++ /dev/null @@ -1,33 +0,0 @@ -# ja Glossary - -## Sources - -- PR #9821: https://github.com/anomalyco/opencode/pull/9821 -- PR #13160: https://github.com/anomalyco/opencode/pull/13160 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -These are PR-backed wording preferences and may evolve. - -| English / Context | Preferred | Notes | -| --------------------------- | ----------------------- | ------------------------------------- | -| WSL integration (UI label) | `WSL連携` | PR #13160 prefers this over `WSL統合` | -| WSL integration description | `WindowsのWSL環境で...` | PR #13160 improved phrasing naturally | - -## Guidance - -- Prefer natural Japanese phrasing over literal translation -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths -- In WSL integration text, follow PR #13160 wording direction for more natural Japanese phrasing - -## Avoid - -- Avoid `WSL統合` in the WSL integration UI context where `WSL連携` is the reviewed wording -- Avoid translating product and protocol names that are fixed identifiers diff --git a/.opencode/glossary/ko.md b/.opencode/glossary/ko.md deleted file mode 100644 index 71385c8a10ac..000000000000 --- a/.opencode/glossary/ko.md +++ /dev/null @@ -1,27 +0,0 @@ -# ko Glossary - -## Sources - -- PR #9817: https://github.com/anomalyco/opencode/pull/9817 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural Korean phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple Korean terms for the same recurring UI action once a preferred term is established diff --git a/.opencode/glossary/no.md b/.opencode/glossary/no.md deleted file mode 100644 index d7159dca4107..000000000000 --- a/.opencode/glossary/no.md +++ /dev/null @@ -1,38 +0,0 @@ -# no Glossary - -## Sources - -- PR #10018: https://github.com/anomalyco/opencode/pull/10018 -- PR #12935: https://github.com/anomalyco/opencode/pull/12935 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Sound names (PR #10018 notes these were intentionally left untranslated) - -## Preferred Terms - -These are PR-backed corrections and may evolve. - -| English / Context | Preferred | Notes | -| ----------------------------------- | ------------ | ----------------------------- | -| Save (data persistence action) | `Lagre` | Prefer over `Spare` | -| Disabled (feature/state) | `deaktivert` | Prefer over `funksjonshemmet` | -| API keys | `API Nøkler` | Prefer over `API Taster` | -| Cost (noun) | `Kostnad` | Prefer over verb form `Koste` | -| Show/View (imperative button label) | `Vis` | Prefer over `Utsikt` | - -## Guidance - -- Prefer natural Norwegian Bokmal (Bokmål) wording over literal translation -- Keep tone clear and practical in UI labels -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths -- Keep recurring UI terms consistent once a preferred term is chosen - -## Avoid - -- Avoid `Spare` for save actions in persistence contexts -- Avoid `funksjonshemmet` for disabled feature states -- Avoid `API Taster`, `Koste`, and `Utsikt` in the corrected contexts above diff --git a/.opencode/glossary/pl.md b/.opencode/glossary/pl.md deleted file mode 100644 index e9bad7a51567..000000000000 --- a/.opencode/glossary/pl.md +++ /dev/null @@ -1,27 +0,0 @@ -# pl Glossary - -## Sources - -- PR #9884: https://github.com/anomalyco/opencode/pull/9884 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural Polish phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple Polish terms for the same recurring UI action once a preferred term is established diff --git a/.opencode/glossary/ru.md b/.opencode/glossary/ru.md deleted file mode 100644 index 6fee0f94c06f..000000000000 --- a/.opencode/glossary/ru.md +++ /dev/null @@ -1,27 +0,0 @@ -# ru Glossary - -## Sources - -- PR #9882: https://github.com/anomalyco/opencode/pull/9882 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural Russian phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple Russian terms for the same recurring UI action once a preferred term is established diff --git a/.opencode/glossary/th.md b/.opencode/glossary/th.md deleted file mode 100644 index 7b5a31d16bfc..000000000000 --- a/.opencode/glossary/th.md +++ /dev/null @@ -1,34 +0,0 @@ -# th Glossary - -## Sources - -- PR #10809: https://github.com/anomalyco/opencode/pull/10809 -- PR #11496: https://github.com/anomalyco/opencode/pull/11496 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -These are PR-backed preferences and may evolve. - -| English / Context | Preferred | Notes | -| ------------------------------------- | --------------------- | -------------------------------------------------------------------------------- | -| Thai language label in language lists | `ไทย` | PR #10809 standardized this across locales | -| Language names in language pickers | Native names (static) | PR #11496: keep names like `English`, `Deutsch`, `ไทย` consistent across locales | - -## Guidance - -- Prefer natural Thai phrasing over literal translation -- Keep tone short and clear for buttons and labels -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths -- Keep language names static/native in language pickers instead of translating them per current locale (PR #11496) - -## Avoid - -- Avoid translating language names differently per current locale in language lists -- Avoid changing `ไทย` to another display form for the Thai language option unless the product standard changes diff --git a/.opencode/glossary/tr.md b/.opencode/glossary/tr.md deleted file mode 100644 index 72b1cdfb40b1..000000000000 --- a/.opencode/glossary/tr.md +++ /dev/null @@ -1,38 +0,0 @@ -# tr Glossary - -## Sources - -- PR #15835: https://github.com/anomalyco/opencode/pull/15835 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose, docs, and UI copy) -- Keep lowercase `opencode` in commands, package names, paths, URLs, and other exact identifiers -- `` stays the literal key token in code blocks; use `Tab` for the nearby explanatory label in prose -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -These are PR-backed wording preferences and may evolve. - -| English / Context | Preferred | Notes | -| ------------------------- | --------------------------------------- | ------------------------------------------------------------- | -| available in beta | `beta olarak mevcut` | Prefer this over `beta olarak kullanılabilir` | -| privacy-first | `Gizlilik öncelikli tasarlandı` | Prefer this over `Önce gizlilik için tasarlandı` | -| connect your local models | `yerel modellerinizi bağlayabilirsiniz` | Use the fuller, more direct action phrase | -| `` key label | `Tab` | Use `Tab` in prose; keep `` in literal UI or code blocks | -| cross-platform | `cross-platform (tüm platformlarda)` | Keep the English term, add a short clarification when helpful | - -## Guidance - -- Prefer natural Turkish phrasing over literal translation -- Merge broken sentence fragments into one clear sentence when the source is a single thought -- Keep product naming consistent: `OpenCode` in prose, `opencode` only for exact technical identifiers -- When an English technical term is intentionally kept, add a short Turkish clarification only if it improves readability - -## Avoid - -- Avoid `beta olarak kullanılabilir` when `beta olarak mevcut` fits -- Avoid `Önce gizlilik için tasarlandı`; use the more natural reviewed wording instead -- Avoid `Sekme` for the translated key label in prose when referring to `` -- Avoid changing `opencode` to `OpenCode` inside commands, URLs, package names, or code literals diff --git a/.opencode/glossary/zh-cn.md b/.opencode/glossary/zh-cn.md deleted file mode 100644 index 054e94b7e83a..000000000000 --- a/.opencode/glossary/zh-cn.md +++ /dev/null @@ -1,42 +0,0 @@ -# zh-cn Glossary - -## Sources - -- PR #13942: https://github.com/anomalyco/opencode/pull/13942 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only when it is part of commands, package names, paths, or code) -- `OpenCode Zen` -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- `Model Context Protocol` (prefer the English expansion when introducing `MCP`) - -## Preferred Terms - -These are preferred terms for docs/UI prose and may evolve. - -| English | Preferred | Notes | -| ----------------------- | --------- | ------------------------------------------- | -| prompt | 提示词 | Keep `--prompt` unchanged in flags/code | -| session | 会话 | | -| provider | 提供商 | | -| share link / shared URL | 分享链接 | Prefer `分享` for user-facing share actions | -| headless (server) | 无界面 | Docs wording | -| authentication | 认证 | Prefer in auth/OAuth contexts | -| cache | 缓存 | | -| keybind / shortcut | 快捷键 | User-facing docs wording | -| workflow | 工作流 | e.g. GitHub Actions workflow | - -## Guidance - -- Prefer natural, concise phrasing over literal translation -- Keep the tone direct and friendly (PR #13942 consistently moved wording in this direction) -- Preserve technical artifacts exactly: commands, flags, code, inline code, URLs, file paths, model IDs -- Keep enum-like values in English when they are literals (for example, `default`, `json`) -- Prefer consistent terminology across pages once a term is chosen (`会话`, `提供商`, `提示词`, etc.) - -## Avoid - -- Avoid `opencode` in prose when referring to the product name; use `OpenCode` -- Avoid mixing alternative terms for the same concept across docs when a preferred term is already established diff --git a/.opencode/glossary/zh-tw.md b/.opencode/glossary/zh-tw.md deleted file mode 100644 index 283660e12198..000000000000 --- a/.opencode/glossary/zh-tw.md +++ /dev/null @@ -1,42 +0,0 @@ -# zh-tw Glossary - -## Sources - -- PR #13942: https://github.com/anomalyco/opencode/pull/13942 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only when it is part of commands, package names, paths, or code) -- `OpenCode Zen` -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- `Model Context Protocol` (prefer the English expansion when introducing `MCP`) - -## Preferred Terms - -These are preferred terms for docs/UI prose and may evolve. - -| English | Preferred | Notes | -| ----------------------- | --------- | ------------------------------------------- | -| prompt | 提示詞 | Keep `--prompt` unchanged in flags/code | -| session | 工作階段 | | -| provider | 供應商 | | -| share link / shared URL | 分享連結 | Prefer `分享` for user-facing share actions | -| headless (server) | 無介面 | Docs wording | -| authentication | 認證 | Prefer in auth/OAuth contexts | -| cache | 快取 | | -| keybind / shortcut | 快捷鍵 | User-facing docs wording | -| workflow | 工作流程 | e.g. GitHub Actions workflow | - -## Guidance - -- Prefer natural, concise phrasing over literal translation -- Keep the tone direct and friendly (PR #13942 consistently moved wording in this direction) -- Preserve technical artifacts exactly: commands, flags, code, inline code, URLs, file paths, model IDs -- Keep enum-like values in English when they are literals (for example, `default`, `json`) -- Prefer consistent terminology across pages once a term is chosen (`工作階段`, `供應商`, `提示詞`, etc.) - -## Avoid - -- Avoid `opencode` in prose when referring to the product name; use `OpenCode` -- Avoid mixing alternative terms for the same concept across docs when a preferred term is already established diff --git a/.opencode/opencode.jsonc b/.opencode/opencode.jsonc deleted file mode 100644 index 8380f7f719ef..000000000000 --- a/.opencode/opencode.jsonc +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://opencode.ai/config.json", - "provider": { - "opencode": { - "options": {}, - }, - }, - "permission": { - "edit": { - "packages/opencode/migration/*": "deny", - }, - }, - "mcp": {}, - "tools": { - "github-triage": false, - "github-pr-search": false, - }, -} diff --git a/.opencode/plugins/smoke-theme.json b/.opencode/plugins/smoke-theme.json deleted file mode 100644 index 6e4595d44657..000000000000 --- a/.opencode/plugins/smoke-theme.json +++ /dev/null @@ -1,223 +0,0 @@ -{ - "$schema": "https://opencode.ai/theme.json", - "defs": { - "nord0": "#2E3440", - "nord1": "#3B4252", - "nord2": "#434C5E", - "nord3": "#4C566A", - "nord4": "#D8DEE9", - "nord5": "#E5E9F0", - "nord6": "#ECEFF4", - "nord7": "#8FBCBB", - "nord8": "#88C0D0", - "nord9": "#81A1C1", - "nord10": "#5E81AC", - "nord11": "#BF616A", - "nord12": "#D08770", - "nord13": "#EBCB8B", - "nord14": "#A3BE8C", - "nord15": "#B48EAD" - }, - "theme": { - "primary": { - "dark": "nord10", - "light": "nord9" - }, - "secondary": { - "dark": "nord9", - "light": "nord9" - }, - "accent": { - "dark": "nord7", - "light": "nord7" - }, - "error": { - "dark": "nord11", - "light": "nord11" - }, - "warning": { - "dark": "nord12", - "light": "nord12" - }, - "success": { - "dark": "nord14", - "light": "nord14" - }, - "info": { - "dark": "nord8", - "light": "nord10" - }, - "text": { - "dark": "nord6", - "light": "nord0" - }, - "textMuted": { - "dark": "#8B95A7", - "light": "nord1" - }, - "background": { - "dark": "nord0", - "light": "nord6" - }, - "backgroundPanel": { - "dark": "nord1", - "light": "nord5" - }, - "backgroundElement": { - "dark": "nord2", - "light": "nord4" - }, - "border": { - "dark": "nord2", - "light": "nord3" - }, - "borderActive": { - "dark": "nord3", - "light": "nord2" - }, - "borderSubtle": { - "dark": "nord2", - "light": "nord3" - }, - "diffAdded": { - "dark": "nord14", - "light": "nord14" - }, - "diffRemoved": { - "dark": "nord11", - "light": "nord11" - }, - "diffContext": { - "dark": "#8B95A7", - "light": "nord3" - }, - "diffHunkHeader": { - "dark": "#8B95A7", - "light": "nord3" - }, - "diffHighlightAdded": { - "dark": "nord14", - "light": "nord14" - }, - "diffHighlightRemoved": { - "dark": "nord11", - "light": "nord11" - }, - "diffAddedBg": { - "dark": "#36413C", - "light": "#E6EBE7" - }, - "diffRemovedBg": { - "dark": "#43393D", - "light": "#ECE6E8" - }, - "diffContextBg": { - "dark": "nord1", - "light": "nord5" - }, - "diffLineNumber": { - "dark": "nord2", - "light": "nord4" - }, - "diffAddedLineNumberBg": { - "dark": "#303A35", - "light": "#DDE4DF" - }, - "diffRemovedLineNumberBg": { - "dark": "#3C3336", - "light": "#E4DDE0" - }, - "markdownText": { - "dark": "nord4", - "light": "nord0" - }, - "markdownHeading": { - "dark": "nord8", - "light": "nord10" - }, - "markdownLink": { - "dark": "nord9", - "light": "nord9" - }, - "markdownLinkText": { - "dark": "nord7", - "light": "nord7" - }, - "markdownCode": { - "dark": "nord14", - "light": "nord14" - }, - "markdownBlockQuote": { - "dark": "#8B95A7", - "light": "nord3" - }, - "markdownEmph": { - "dark": "nord12", - "light": "nord12" - }, - "markdownStrong": { - "dark": "nord13", - "light": "nord13" - }, - "markdownHorizontalRule": { - "dark": "#8B95A7", - "light": "nord3" - }, - "markdownListItem": { - "dark": "nord8", - "light": "nord10" - }, - "markdownListEnumeration": { - "dark": "nord7", - "light": "nord7" - }, - "markdownImage": { - "dark": "nord9", - "light": "nord9" - }, - "markdownImageText": { - "dark": "nord7", - "light": "nord7" - }, - "markdownCodeBlock": { - "dark": "nord4", - "light": "nord0" - }, - "syntaxComment": { - "dark": "#8B95A7", - "light": "nord3" - }, - "syntaxKeyword": { - "dark": "nord9", - "light": "nord9" - }, - "syntaxFunction": { - "dark": "nord8", - "light": "nord8" - }, - "syntaxVariable": { - "dark": "nord7", - "light": "nord7" - }, - "syntaxString": { - "dark": "nord14", - "light": "nord14" - }, - "syntaxNumber": { - "dark": "nord15", - "light": "nord15" - }, - "syntaxType": { - "dark": "nord7", - "light": "nord7" - }, - "syntaxOperator": { - "dark": "nord9", - "light": "nord9" - }, - "syntaxPunctuation": { - "dark": "nord4", - "light": "nord0" - } - } -} diff --git a/.opencode/plugins/tui-smoke.tsx b/.opencode/plugins/tui-smoke.tsx deleted file mode 100644 index 63f9f331e04d..000000000000 --- a/.opencode/plugins/tui-smoke.tsx +++ /dev/null @@ -1,937 +0,0 @@ -/** @jsxImportSource @opentui/solid */ -import { useKeyboard, useTerminalDimensions, type JSX } from "@opentui/solid" -import { RGBA, VignetteEffect } from "@opentui/core" -import type { - TuiKeybindSet, - TuiPlugin, - TuiPluginApi, - TuiPluginMeta, - TuiPluginModule, - TuiSlotPlugin, -} from "@opencode-ai/plugin/tui" - -const tabs = ["overview", "counter", "help"] -const bind = { - modal: "ctrl+shift+m", - screen: "ctrl+shift+o", - home: "escape,ctrl+h", - left: "left,h", - right: "right,l", - up: "up,k", - down: "down,j", - alert: "a", - confirm: "c", - prompt: "p", - select: "s", - modal_accept: "enter,return", - modal_close: "escape", - dialog_close: "escape", - local: "x", - local_push: "enter,return", - local_close: "q,backspace", - host: "z", -} - -const pick = (value: unknown, fallback: string) => { - if (typeof value !== "string") return fallback - if (!value.trim()) return fallback - return value -} - -const num = (value: unknown, fallback: number) => { - if (typeof value !== "number") return fallback - return value -} - -const rec = (value: unknown) => { - if (!value || typeof value !== "object" || Array.isArray(value)) return - return Object.fromEntries(Object.entries(value)) -} - -type Cfg = { - label: string - route: string - vignette: number - keybinds: Record | undefined -} - -type Route = { - modal: string - screen: string -} - -type State = { - tab: number - count: number - source: string - note: string - selected: string - local: number -} - -const cfg = (options: Record | undefined) => { - return { - label: pick(options?.label, "smoke"), - route: pick(options?.route, "workspace-smoke"), - vignette: Math.max(0, num(options?.vignette, 0.35)), - keybinds: rec(options?.keybinds), - } -} - -const names = (input: Cfg) => { - return { - modal: `${input.route}.modal`, - screen: `${input.route}.screen`, - } -} - -type Keys = TuiKeybindSet -const ui = { - panel: "#1d1d1d", - border: "#4a4a4a", - text: "#f0f0f0", - muted: "#a5a5a5", - accent: "#5f87ff", -} - -type Color = RGBA | string - -const ink = (map: Record, name: string, fallback: string): Color => { - const value = map[name] - if (typeof value === "string") return value - if (value instanceof RGBA) return value - return fallback -} - -const look = (map: Record) => { - return { - panel: ink(map, "backgroundPanel", ui.panel), - border: ink(map, "border", ui.border), - text: ink(map, "text", ui.text), - muted: ink(map, "textMuted", ui.muted), - accent: ink(map, "primary", ui.accent), - selected: ink(map, "selectedListItemText", ui.text), - } -} - -const tone = (api: TuiPluginApi) => { - return look(api.theme.current) -} - -type Skin = { - panel: Color - border: Color - text: Color - muted: Color - accent: Color - selected: Color -} - -const Btn = (props: { txt: string; run: () => void; skin: Skin; on?: boolean }) => { - return ( - { - props.run() - }} - backgroundColor={props.on ? props.skin.accent : props.skin.border} - paddingLeft={1} - paddingRight={1} - > - {props.txt} - - ) -} - -const parse = (params: Record | undefined) => { - const tab = typeof params?.tab === "number" ? params.tab : 0 - const count = typeof params?.count === "number" ? params.count : 0 - const source = typeof params?.source === "string" ? params.source : "unknown" - const note = typeof params?.note === "string" ? params.note : "" - const selected = typeof params?.selected === "string" ? params.selected : "" - const local = typeof params?.local === "number" ? params.local : 0 - return { - tab: Math.max(0, Math.min(tab, tabs.length - 1)), - count, - source, - note, - selected, - local: Math.max(0, local), - } -} - -const current = (api: TuiPluginApi, route: Route) => { - const value = api.route.current - const ok = Object.values(route).includes(value.name) - if (!ok) return parse(undefined) - if (!("params" in value)) return parse(undefined) - return parse(value.params) -} - -const opts = [ - { - title: "Overview", - value: 0, - description: "Switch to overview tab", - }, - { - title: "Counter", - value: 1, - description: "Switch to counter tab", - }, - { - title: "Help", - value: 2, - description: "Switch to help tab", - }, -] - -const host = (api: TuiPluginApi, input: Cfg, skin: Skin) => { - api.ui.dialog.setSize("medium") - api.ui.dialog.replace(() => ( - - - {input.label} host overlay - - Using api.ui.dialog stack with built-in backdrop - esc closes · depth {api.ui.dialog.depth} - - api.ui.dialog.clear()} skin={skin} on /> - - - )) -} - -const warn = (api: TuiPluginApi, route: Route, value: State) => { - const DialogAlert = api.ui.DialogAlert - api.ui.dialog.setSize("medium") - api.ui.dialog.replace(() => ( - api.route.navigate(route.screen, { ...value, source: "alert" })} - /> - )) -} - -const check = (api: TuiPluginApi, route: Route, value: State) => { - const DialogConfirm = api.ui.DialogConfirm - api.ui.dialog.setSize("medium") - api.ui.dialog.replace(() => ( - api.route.navigate(route.screen, { ...value, count: value.count + 1, source: "confirm" })} - onCancel={() => api.route.navigate(route.screen, { ...value, source: "confirm-cancel" })} - /> - )) -} - -const entry = (api: TuiPluginApi, route: Route, value: State) => { - const DialogPrompt = api.ui.DialogPrompt - api.ui.dialog.setSize("medium") - api.ui.dialog.replace(() => ( - { - api.ui.dialog.clear() - api.route.navigate(route.screen, { ...value, note, source: "prompt" }) - }} - onCancel={() => { - api.ui.dialog.clear() - api.route.navigate(route.screen, value) - }} - /> - )) -} - -const picker = (api: TuiPluginApi, route: Route, value: State) => { - const DialogSelect = api.ui.DialogSelect - api.ui.dialog.setSize("medium") - api.ui.dialog.replace(() => ( - { - api.ui.dialog.clear() - api.route.navigate(route.screen, { - ...value, - tab: typeof item.value === "number" ? item.value : value.tab, - selected: item.title, - source: "select", - }) - }} - /> - )) -} - -const Screen = (props: { - api: TuiPluginApi - input: Cfg - route: Route - keys: Keys - meta: TuiPluginMeta - params?: Record -}) => { - const dim = useTerminalDimensions() - const value = parse(props.params) - const skin = tone(props.api) - const set = (local: number, base?: State) => { - const next = base ?? current(props.api, props.route) - props.api.route.navigate(props.route.screen, { ...next, local: Math.max(0, local), source: "local" }) - } - const push = (base?: State) => { - const next = base ?? current(props.api, props.route) - set(next.local + 1, next) - } - const open = () => { - const next = current(props.api, props.route) - if (next.local > 0) return - set(1, next) - } - const pop = (base?: State) => { - const next = base ?? current(props.api, props.route) - const local = Math.max(0, next.local - 1) - set(local, next) - } - const show = () => { - setTimeout(() => { - open() - }, 0) - } - useKeyboard((evt) => { - if (props.api.route.current.name !== props.route.screen) return - const next = current(props.api, props.route) - if (props.api.ui.dialog.open) { - if (props.keys.match("dialog_close", evt)) { - evt.preventDefault() - evt.stopPropagation() - props.api.ui.dialog.clear() - return - } - return - } - - if (next.local > 0) { - if (evt.name === "escape" || props.keys.match("local_close", evt)) { - evt.preventDefault() - evt.stopPropagation() - pop(next) - return - } - - if (props.keys.match("local_push", evt)) { - evt.preventDefault() - evt.stopPropagation() - push(next) - return - } - return - } - - if (props.keys.match("home", evt)) { - evt.preventDefault() - evt.stopPropagation() - props.api.route.navigate("home") - return - } - - if (props.keys.match("left", evt)) { - evt.preventDefault() - evt.stopPropagation() - props.api.route.navigate(props.route.screen, { ...next, tab: (next.tab - 1 + tabs.length) % tabs.length }) - return - } - - if (props.keys.match("right", evt)) { - evt.preventDefault() - evt.stopPropagation() - props.api.route.navigate(props.route.screen, { ...next, tab: (next.tab + 1) % tabs.length }) - return - } - - if (props.keys.match("up", evt)) { - evt.preventDefault() - evt.stopPropagation() - props.api.route.navigate(props.route.screen, { ...next, count: next.count + 1 }) - return - } - - if (props.keys.match("down", evt)) { - evt.preventDefault() - evt.stopPropagation() - props.api.route.navigate(props.route.screen, { ...next, count: next.count - 1 }) - return - } - - if (props.keys.match("modal", evt)) { - evt.preventDefault() - evt.stopPropagation() - props.api.route.navigate(props.route.modal, next) - return - } - - if (props.keys.match("local", evt)) { - evt.preventDefault() - evt.stopPropagation() - open() - return - } - - if (props.keys.match("host", evt)) { - evt.preventDefault() - evt.stopPropagation() - host(props.api, props.input, skin) - return - } - - if (props.keys.match("alert", evt)) { - evt.preventDefault() - evt.stopPropagation() - warn(props.api, props.route, next) - return - } - - if (props.keys.match("confirm", evt)) { - evt.preventDefault() - evt.stopPropagation() - check(props.api, props.route, next) - return - } - - if (props.keys.match("prompt", evt)) { - evt.preventDefault() - evt.stopPropagation() - entry(props.api, props.route, next) - return - } - - if (props.keys.match("select", evt)) { - evt.preventDefault() - evt.stopPropagation() - picker(props.api, props.route, next) - } - }) - - return ( - - - - - {props.input.label} screen - plugin route - - {props.keys.print("home")} home - - - - {tabs.map((item, i) => { - const on = value.tab === i - return ( - props.api.route.navigate(props.route.screen, { ...value, tab: i })} - skin={skin} - on={on} - /> - ) - })} - - - - {value.tab === 0 ? ( - - Route: {props.route.screen} - plugin state: {props.meta.state} - - first: {props.meta.state === "first" ? "yes" : "no"} · updated:{" "} - {props.meta.state === "updated" ? "yes" : "no"} · loads: {props.meta.load_count} - - plugin source: {props.meta.source} - source: {value.source} - note: {value.note || "(none)"} - selected: {value.selected || "(none)"} - local stack depth: {value.local} - host stack open: {props.api.ui.dialog.open ? "yes" : "no"} - - ) : null} - - {value.tab === 1 ? ( - - Counter: {value.count} - - {props.keys.print("up")} / {props.keys.print("down")} change value - - - ) : null} - - {value.tab === 2 ? ( - - - {props.keys.print("modal")} modal | {props.keys.print("alert")} alert | {props.keys.print("confirm")}{" "} - confirm | {props.keys.print("prompt")} prompt | {props.keys.print("select")} select - - - {props.keys.print("local")} local stack | {props.keys.print("host")} host stack - - - local open: {props.keys.print("local_push")} push nested · esc or {props.keys.print("local_close")}{" "} - close - - {props.keys.print("home")} returns home - - ) : null} - - - - props.api.route.navigate("home")} skin={skin} /> - props.api.route.navigate(props.route.modal, value)} skin={skin} on /> - - host(props.api, props.input, skin)} skin={skin} /> - warn(props.api, props.route, value)} skin={skin} /> - check(props.api, props.route, value)} skin={skin} /> - entry(props.api, props.route, value)} skin={skin} /> - picker(props.api, props.route, value)} skin={skin} /> - - - - 0} - width={dim().width} - height={dim().height} - alignItems="center" - position="absolute" - zIndex={3000} - paddingTop={dim().height / 4} - left={0} - top={0} - backgroundColor={RGBA.fromInts(0, 0, 0, 160)} - onMouseUp={() => { - pop() - }} - > - { - evt.stopPropagation() - }} - width={60} - maxWidth={dim().width - 2} - backgroundColor={skin.panel} - border - borderColor={skin.border} - paddingTop={1} - paddingBottom={1} - paddingLeft={2} - paddingRight={2} - gap={1} - flexDirection="column" - > - - {props.input.label} local overlay - - Plugin-owned stack depth: {value.local} - - {props.keys.print("local_push")} push nested · {props.keys.print("local_close")} pop/close - - - - - - - - - ) -} - -const Modal = (props: { - api: TuiPluginApi - input: Cfg - route: Route - keys: Keys - params?: Record -}) => { - const Dialog = props.api.ui.Dialog - const value = parse(props.params) - const skin = tone(props.api) - - useKeyboard((evt) => { - if (props.api.route.current.name !== props.route.modal) return - - if (props.keys.match("modal_accept", evt)) { - evt.preventDefault() - evt.stopPropagation() - props.api.route.navigate(props.route.screen, { ...value, source: "modal" }) - return - } - - if (props.keys.match("modal_close", evt)) { - evt.preventDefault() - evt.stopPropagation() - props.api.route.navigate("home") - } - }) - - return ( - -

props.api.route.navigate("home")}> - - - {props.input.label} modal - - {props.keys.print("modal")} modal command - {props.keys.print("screen")} screen command - - {props.keys.print("modal_accept")} opens screen · {props.keys.print("modal_close")} closes - - - props.api.route.navigate(props.route.screen, { ...value, source: "modal" })} - skin={skin} - on - /> - props.api.route.navigate("home")} skin={skin} /> - - - - - ) -} - -const home = (api: TuiPluginApi, input: Cfg) => ({ - slots: { - home_logo(ctx) { - const map = ctx.theme.current - const skin = look(map) - const art = [ - " $$\\", - " $$ |", - " $$$$$$$\\ $$$$$$\\$$$$\\ $$$$$$\\ $$ | $$\\ $$$$$$\\", - "$$ _____|$$ _$$ _$$\\ $$ __$$\\ $$ | $$ |$$ __$$\\", - "\\$$$$$$\\ $$ / $$ / $$ |$$ / $$ |$$$$$$ / $$$$$$$$ |", - " \\____$$\\ $$ | $$ | $$ |$$ | $$ |$$ _$$< $$ ____|", - "$$$$$$$ |$$ | $$ | $$ |\\$$$$$$ |$$ | \\$$\\ \\$$$$$$$\\", - "\\_______/ \\__| \\__| \\__| \\______/ \\__| \\__| \\_______|", - ] - const fill = [ - skin.accent, - skin.muted, - ink(map, "info", ui.accent), - skin.text, - ink(map, "success", ui.accent), - ink(map, "warning", ui.accent), - ink(map, "secondary", ui.accent), - ink(map, "error", ui.accent), - ] - - return ( - - {art.map((line, i) => ( - {line} - ))} - - ) - }, - home_prompt(ctx, value) { - const skin = look(ctx.theme.current) - type Prompt = (props: { - workspaceID?: string - visible?: boolean - disabled?: boolean - onSubmit?: () => void - hint?: JSX.Element - right?: JSX.Element - showPlaceholder?: boolean - placeholders?: { - normal?: string[] - shell?: string[] - } - }) => JSX.Element - type Slot = ( - props: { name: string; mode?: unknown; children?: JSX.Element } & Record, - ) => JSX.Element | null - const ui = api.ui as TuiPluginApi["ui"] & { Prompt: Prompt; Slot: Slot } - const Prompt = ui.Prompt - const Slot = ui.Slot - const normal = [ - `[SMOKE] route check for ${input.label}`, - "[SMOKE] confirm home_prompt slot override", - "[SMOKE] verify prompt-right slot passthrough", - ] - const shell = ["printf '[SMOKE] home prompt\n'", "git status --short", "bun --version"] - const hint = ( - - - smoke home prompt - - - ) - - return ( - - - - - } - placeholders={{ normal, shell }} - /> - ) - }, - home_prompt_right(ctx, value) { - const skin = look(ctx.theme.current) - const id = value.workspace_id?.slice(0, 8) ?? "none" - return ( - - {input.label} home:{id} - - ) - }, - session_prompt_right(ctx, value) { - const skin = look(ctx.theme.current) - return ( - - {input.label} session:{value.session_id.slice(0, 8)} - - ) - }, - smoke_prompt_right(ctx, value) { - const skin = look(ctx.theme.current) - const id = typeof value.workspace_id === "string" ? value.workspace_id.slice(0, 8) : "none" - const label = typeof value.label === "string" ? value.label : input.label - return ( - - {label} custom:{id} - - ) - }, - home_bottom(ctx) { - const skin = look(ctx.theme.current) - const text = "extra content in the unified home bottom slot" - - return ( - - - - {input.label} {text} - - - - ) - }, - }, -}) - -const block = (input: Cfg, order: number, title: string, text: string): TuiSlotPlugin => ({ - order, - slots: { - sidebar_content(ctx, value) { - const skin = look(ctx.theme.current) - - return ( - - - {title} - - {text} - - {input.label} order {order} · session {value.session_id.slice(0, 8)} - - - ) - }, - }, -}) - -const slot = (api: TuiPluginApi, input: Cfg): TuiSlotPlugin[] => [ - home(api, input), - block(input, 50, "Smoke above", "renders above internal sidebar blocks"), - block(input, 250, "Smoke between", "renders between internal sidebar blocks"), - block(input, 650, "Smoke below", "renders below internal sidebar blocks"), -] - -const reg = (api: TuiPluginApi, input: Cfg, keys: Keys) => { - const route = names(input) - api.command.register(() => [ - { - title: `${input.label} modal`, - value: "plugin.smoke.modal", - keybind: keys.get("modal"), - category: "Plugin", - slash: { - name: "smoke", - }, - onSelect: () => { - api.route.navigate(route.modal, { source: "command" }) - }, - }, - { - title: `${input.label} screen`, - value: "plugin.smoke.screen", - keybind: keys.get("screen"), - category: "Plugin", - slash: { - name: "smoke-screen", - }, - onSelect: () => { - api.route.navigate(route.screen, { source: "command", tab: 0, count: 0 }) - }, - }, - { - title: `${input.label} alert dialog`, - value: "plugin.smoke.alert", - category: "Plugin", - slash: { - name: "smoke-alert", - }, - onSelect: () => { - warn(api, route, current(api, route)) - }, - }, - { - title: `${input.label} confirm dialog`, - value: "plugin.smoke.confirm", - category: "Plugin", - slash: { - name: "smoke-confirm", - }, - onSelect: () => { - check(api, route, current(api, route)) - }, - }, - { - title: `${input.label} prompt dialog`, - value: "plugin.smoke.prompt", - category: "Plugin", - slash: { - name: "smoke-prompt", - }, - onSelect: () => { - entry(api, route, current(api, route)) - }, - }, - { - title: `${input.label} select dialog`, - value: "plugin.smoke.select", - category: "Plugin", - slash: { - name: "smoke-select", - }, - onSelect: () => { - picker(api, route, current(api, route)) - }, - }, - { - title: `${input.label} host overlay`, - value: "plugin.smoke.host", - category: "Plugin", - slash: { - name: "smoke-host", - }, - onSelect: () => { - host(api, input, tone(api)) - }, - }, - { - title: `${input.label} go home`, - value: "plugin.smoke.home", - category: "Plugin", - enabled: api.route.current.name !== "home", - onSelect: () => { - api.route.navigate("home") - }, - }, - { - title: `${input.label} toast`, - value: "plugin.smoke.toast", - category: "Plugin", - onSelect: () => { - api.ui.toast({ - variant: "info", - title: "Smoke", - message: "Plugin toast works", - duration: 2000, - }) - }, - }, - ]) -} - -const tui: TuiPlugin = async (api, options, meta) => { - if (options?.enabled === false) return - - await api.theme.install("./smoke-theme.json") - api.theme.set("smoke-theme") - - const value = cfg(options ?? undefined) - const route = names(value) - const keys = api.keybind.create(bind, value.keybinds) - const fx = new VignetteEffect(value.vignette) - const post = fx.apply.bind(fx) - api.renderer.addPostProcessFn(post) - api.lifecycle.onDispose(() => { - api.renderer.removePostProcessFn(post) - }) - - api.route.register([ - { - name: route.screen, - render: ({ params }) => , - }, - { - name: route.modal, - render: ({ params }) => , - }, - ]) - - reg(api, value, keys) - for (const item of slot(api, value)) { - api.slots.register(item) - } -} - -const plugin: TuiPluginModule & { id: string } = { - id: "tui-smoke", - tui, -} - -export default plugin diff --git a/.opencode/themes/.gitignore b/.opencode/themes/.gitignore deleted file mode 100644 index 5b41319c6aeb..000000000000 --- a/.opencode/themes/.gitignore +++ /dev/null @@ -1 +0,0 @@ -smoke-theme.json diff --git a/.opencode/tool/github-pr-search.ts b/.opencode/tool/github-pr-search.ts deleted file mode 100644 index 927e68fd73d9..000000000000 --- a/.opencode/tool/github-pr-search.ts +++ /dev/null @@ -1,64 +0,0 @@ -/// -import { tool } from "@opencode-ai/plugin" -async function githubFetch(endpoint: string, options: RequestInit = {}) { - const response = await fetch(`https://api.github.com${endpoint}`, { - ...options, - headers: { - Authorization: `Bearer ${process.env.GITHUB_TOKEN}`, - Accept: "application/vnd.github+json", - "Content-Type": "application/json", - ...options.headers, - }, - }) - if (!response.ok) { - throw new Error(`GitHub API error: ${response.status} ${response.statusText}`) - } - return response.json() -} - -interface PR { - title: string - html_url: string -} - -export default tool({ - description: `Use this tool to search GitHub pull requests by title and description. - -This tool searches PRs in the anomalyco/opencode repository and returns LLM-friendly results including: -- PR number and title -- Author -- State (open/closed/merged) -- Labels -- Description snippet - -Use the query parameter to search for keywords that might appear in PR titles or descriptions.`, - args: { - query: tool.schema.string().describe("Search query for PR titles and descriptions"), - limit: tool.schema.number().describe("Maximum number of results to return").default(10), - offset: tool.schema.number().describe("Number of results to skip for pagination").default(0), - }, - async execute(args) { - const owner = "anomalyco" - const repo = "opencode" - - const page = Math.floor(args.offset / args.limit) + 1 - const searchQuery = encodeURIComponent(`${args.query} repo:${owner}/${repo} type:pr state:open`) - const result = await githubFetch( - `/search/issues?q=${searchQuery}&per_page=${args.limit}&page=${page}&sort=updated&order=desc`, - ) - - if (result.total_count === 0) { - return `No PRs found matching "${args.query}"` - } - - const prs = result.items as PR[] - - if (prs.length === 0) { - return `No other PRs found matching "${args.query}"` - } - - const formatted = prs.map((pr) => `${pr.title}\n${pr.html_url}`).join("\n\n") - - return `Found ${result.total_count} PRs (showing ${prs.length}):\n\n${formatted}` - }, -}) diff --git a/.opencode/tool/github-triage.ts b/.opencode/tool/github-triage.ts deleted file mode 100644 index c06d2407fe32..000000000000 --- a/.opencode/tool/github-triage.ts +++ /dev/null @@ -1,116 +0,0 @@ -/// -import { tool } from "@opencode-ai/plugin" -const TEAM = { - desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"], - zen: ["fwang", "MrMushrooooom"], - tui: ["thdxr", "kommander", "rekram1-node"], - core: ["thdxr", "rekram1-node", "jlongster"], - docs: ["R44VC0RP"], - windows: ["Hona"], -} as const - -const ASSIGNEES = [...new Set(Object.values(TEAM).flat())] - -function pick(items: readonly T[]) { - return items[Math.floor(Math.random() * items.length)]! -} - -function getIssueNumber(): number { - const issue = parseInt(process.env.ISSUE_NUMBER ?? "", 10) - if (!issue) throw new Error("ISSUE_NUMBER env var not set") - return issue -} - -async function githubFetch(endpoint: string, options: RequestInit = {}) { - const response = await fetch(`https://api.github.com${endpoint}`, { - ...options, - headers: { - Authorization: `Bearer ${process.env.GITHUB_TOKEN}`, - Accept: "application/vnd.github+json", - "Content-Type": "application/json", - ...options.headers, - }, - }) - if (!response.ok) { - throw new Error(`GitHub API error: ${response.status} ${response.statusText}`) - } - return response.json() -} - -export default tool({ - description: `Use this tool to assign and/or label a GitHub issue. - -Choose labels and assignee using the current triage policy and ownership rules. -Pick the most fitting labels for the issue and assign one owner. - -If unsure, choose the team/section with the most overlap with the issue and assign a member from that team at random.`, - args: { - assignee: tool.schema - .enum(ASSIGNEES as [string, ...string[]]) - .describe("The username of the assignee") - .default("rekram1-node"), - labels: tool.schema - .array(tool.schema.enum(["nix", "opentui", "perf", "web", "desktop", "zen", "docs", "windows", "core"])) - .describe("The labels(s) to add to the issue") - .default([]), - }, - async execute(args) { - const issue = getIssueNumber() - const owner = "anomalyco" - const repo = "opencode" - - const results: string[] = [] - let labels = [...new Set(args.labels.map((x) => (x === "desktop" ? "web" : x)))] - const web = labels.includes("web") - const text = `${process.env.ISSUE_TITLE ?? ""}\n${process.env.ISSUE_BODY ?? ""}`.toLowerCase() - const zen = /\bzen\b/.test(text) || text.includes("opencode black") - const nix = /\bnix(os)?\b/.test(text) - - if (labels.includes("nix") && !nix) { - labels = labels.filter((x) => x !== "nix") - results.push("Dropped label: nix (issue does not mention nix)") - } - - const assignee = nix ? "rekram1-node" : web ? pick(TEAM.desktop) : args.assignee - - if (labels.includes("zen") && !zen) { - throw new Error("Only add the zen label when issue title/body contains 'zen'") - } - - if (web && !nix && !(TEAM.desktop as readonly string[]).includes(assignee)) { - throw new Error("Web issues must be assigned to adamdotdevin, iamdavidhill, Brendonovich, or nexxeln") - } - - if ((TEAM.zen as readonly string[]).includes(assignee) && !labels.includes("zen")) { - throw new Error("Only zen issues should be assigned to fwang or MrMushrooooom") - } - - if (assignee === "Hona" && !labels.includes("windows")) { - throw new Error("Only windows issues should be assigned to Hona") - } - - if (assignee === "R44VC0RP" && !labels.includes("docs")) { - throw new Error("Only docs issues should be assigned to R44VC0RP") - } - - if (assignee === "kommander" && !labels.includes("opentui")) { - throw new Error("Only opentui issues should be assigned to kommander") - } - - await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/assignees`, { - method: "POST", - body: JSON.stringify({ assignees: [assignee] }), - }) - results.push(`Assigned @${assignee} to issue #${issue}`) - - if (labels.length > 0) { - await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/labels`, { - method: "POST", - body: JSON.stringify({ labels }), - }) - results.push(`Added labels: ${labels.join(", ")}`) - } - - return results.join("\n") - }, -}) diff --git a/.opencode/tui.json b/.opencode/tui.json deleted file mode 100644 index 1eee01b30220..000000000000 --- a/.opencode/tui.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://opencode.ai/tui.json", - "plugin": [ - [ - "./plugins/tui-smoke.tsx", - { - "enabled": false, - "label": "workspace", - "keybinds": { - "modal": "ctrl+alt+m", - "screen": "ctrl+alt+o", - "home": "escape,ctrl+shift+h", - "dialog_close": "escape,q" - } - } - ] - ] -} diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index f9ca88341434..352d6b916e10 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -1012,6 +1012,19 @@ export namespace Config { }, ), instructions: z.array(z.string()).optional().describe("Additional instruction files or patterns to include"), + context: z + .object({ + exclude: z + .array(z.string()) + .optional() + .describe("Glob patterns for instruction files or directories to exclude from automatic loading"), + include: z + .array(z.string()) + .optional() + .describe("Glob patterns for additional instruction files to include"), + }) + .optional() + .describe("Control which instruction files are automatically loaded"), layout: Layout.optional().describe("@deprecated Always uses stretch layout."), permission: Permission.optional(), tools: z.record(z.string(), z.boolean()).optional(), diff --git a/packages/opencode/src/session/instruction.ts b/packages/opencode/src/session/instruction.ts index 04f2610dfe28..450689130ea3 100644 --- a/packages/opencode/src/session/instruction.ts +++ b/packages/opencode/src/session/instruction.ts @@ -10,6 +10,7 @@ import { withTransientReadRetry } from "@/util/effect-http-client" import { Global } from "../global" import { Instance } from "../project/instance" import { Log } from "../util/log" +import { ContextMap } from "../util/context-map" import type { MessageV2 } from "./message-v2" import type { MessageID } from "./schema" @@ -157,7 +158,7 @@ export namespace Instruction { } } - return paths + return yield* ContextMap.filterPaths(paths) }) const system = Effect.fn("Instruction.system")(function* () { @@ -205,6 +206,12 @@ export namespace Instruction { continue } + const ignored = yield* ContextMap.isIgnored(found) + if (ignored) { + current = path.dirname(current) + continue + } + let set = s.claims.get(messageID) if (!set) { set = new Set() diff --git a/packages/opencode/src/skill/index.ts b/packages/opencode/src/skill/index.ts index 6c4f290a08d8..e77ea3afd0bc 100644 --- a/packages/opencode/src/skill/index.ts +++ b/packages/opencode/src/skill/index.ts @@ -16,6 +16,7 @@ import { ConfigMarkdown } from "../config/markdown" import { Glob } from "../util/glob" import { Log } from "../util/log" import { Discovery } from "./discovery" +import { ContextMap } from "../util/context-map" export namespace Skill { const log = Log.create({ service: "skill" }) @@ -144,6 +145,7 @@ export namespace Skill { ) { if (!Flag.OPENCODE_DISABLE_EXTERNAL_SKILLS) { for (const dir of EXTERNAL_DIRS) { + if (yield* ContextMap.isExternalDirIgnored(dir)) continue const root = path.join(Global.Path.home, dir) if (!(yield* fsys.isDir(root))) continue yield* scan(state, bus, root, EXTERNAL_SKILL_PATTERN, { dot: true, scope: "global" }) @@ -154,6 +156,8 @@ export namespace Skill { .pipe(Effect.catch(() => Effect.succeed([] as string[]))) for (const root of upDirs) { + const dirName = path.basename(root) + if (yield* ContextMap.isExternalDirIgnored(dirName)) continue yield* scan(state, bus, root, EXTERNAL_SKILL_PATTERN, { dot: true, scope: "project" }) } } diff --git a/packages/opencode/src/util/context-map.ts b/packages/opencode/src/util/context-map.ts new file mode 100644 index 000000000000..09a8035f51f5 --- /dev/null +++ b/packages/opencode/src/util/context-map.ts @@ -0,0 +1,75 @@ +import path from "path" +import { Effect } from "effect" +import { minimatch } from "minimatch" +import { Config } from "../config/config" +import { Instance } from "../project/instance" + +export namespace ContextMap { + function excludePatterns(): Effect.Effect { + return Effect.promise(() => Config.get().then((config) => config.context?.exclude ?? [])).pipe( + Effect.catch(() => Effect.succeed([])), + ) + } + + function includePatterns(): Effect.Effect { + return Effect.promise(() => Config.get().then((config) => config.context?.include ?? [])).pipe( + Effect.catch(() => Effect.succeed([])), + ) + } + + function matches(filepath: string, patterns: string[]): boolean { + const basename = path.basename(filepath) + const relative = path.relative(Instance.directory, filepath) + + for (const pattern of patterns) { + if (minimatch(basename, pattern)) return true + if (minimatch(relative, pattern)) return true + if (minimatch(filepath, pattern)) return true + } + return false + } + + export function isIgnored(filepath: string): Effect.Effect { + return excludePatterns().pipe( + Effect.map((patterns) => { + if (patterns.length === 0) return false + return matches(filepath, patterns) + }), + ) + } + + export function filterPaths(paths: Set): Effect.Effect, never, never> { + return excludePatterns().pipe( + Effect.map((patterns) => { + if (patterns.length === 0) return paths + const result = new Set() + for (const p of paths) { + if (!matches(p, patterns)) { + result.add(p) + } + } + return result + }), + ) + } + + export function isExternalDirIgnored(dirName: string): Effect.Effect { + return excludePatterns().pipe( + Effect.map((patterns) => { + if (patterns.length === 0) return false + for (const pattern of patterns) { + if (minimatch(dirName, pattern)) return true + const segments = pattern.split("/") + for (const segment of segments) { + if (segment !== "*" && minimatch(dirName, segment)) return true + } + } + return false + }), + ) + } + + export function contextPatterns(): Effect.Effect { + return includePatterns() + } +}