Compare commits

...

1131 Commits

Author SHA1 Message Date
Leonidas 1ea4b1dc01 fix(github): add persist-credentials: false to workflow templates (#8202)
publish-github-action / publish (push) Waiting to run
Update Nix Hashes / update-linux (push) Waiting to run
Update Nix Hashes / update-macos (push) Blocked by required conditions
2026-01-13 09:53:30 -06:00
Brendan Allan 12384a9ad4 console: reduce desktop download cache ttl to 5 minutes 2026-01-13 23:51:16 +08:00
GitHub Action 14d7f50033 chore: generate 2026-01-13 15:42:58 +00:00
Daniel Polito 52778f6d5b feat(desktop): Adding Provider Icons (#8215) 2026-01-13 09:41:35 -06:00
usvimal 393d038e1a fix(desktop): correct health check endpoint URL to /global/health (#8231) 2026-01-13 09:39:43 -06:00
GitHub Action 735a90228e chore: generate 2026-01-13 14:58:51 +00:00
Dax Raad 2e64be52ab add fullscreen view to permission prompt 2026-01-13 09:58:08 -05:00
opencode 9f46edda9a release: v1.1.17
Update Nix Hashes / update-linux (push) Waiting to run
Update Nix Hashes / update-macos (push) Blocked by required conditions
2026-01-13 14:41:54 +00:00
Brendan Allan b28e3da935 desktop: macos killall opencode-cli on launch 2026-01-13 22:38:24 +08:00
GitHub Action 8c726008dd chore: generate 2026-01-13 14:17:35 +00:00
OpeOginni 8784c946ad fix(ui): track memo-based items in useFilteredList without affecting async function based lists (#8216)
Co-authored-by: neriousy <neriousy@users.noreply.github.com>
2026-01-13 08:16:57 -06:00
GitHub Action c931063459 chore: generate 2026-01-13 13:13:45 +00:00
Brendan Allan 618fdce640 fix(desktop): set serverPassword 2026-01-13 21:13:06 +08:00
Brendan Allan 77efb8fb3a fix(app): provide pty socket auth if available from desktop (#8210) 2026-01-13 07:08:19 -06:00
Adam c28455608b fix(app): file search 2026-01-13 06:55:21 -06:00
GitHub Action 3e1c5a2b65 ignore: update download stats 2026-01-13 2026-01-13 12:05:22 +00:00
Brendan Allan d715d473c7 feat(desktop): Tie desktop & CLI to the same Windows JobObject (#8153) 2026-01-13 15:32:54 +08:00
GitHub Action d7c4cca09d chore: generate 2026-01-13 07:08:25 +00:00
Aiden Cline 1b196b93f0 fix: remove the symlinkBinary function call that replaces the wrapper script (#8133)
Co-authored-by: Chuck Chen <459052+chuckchen@users.noreply.github.com>
2026-01-13 01:07:35 -06:00
zerone0x 270a055696 fix(tui): prevent question tool keybindings when dialog is open (#8147)
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-13 01:04:49 -06:00
GitHub Action f9d36f1e5a chore: generate 2026-01-13 07:01:50 +00:00
Aiden Cline e1a2f5ee2c fix: brew autoupgrade 2026-01-13 01:01:06 -06:00
opencode 521c7b30d8 release: v1.1.16
Update Nix Hashes / update-linux (push) Waiting to run
Update Nix Hashes / update-macos (push) Blocked by required conditions
2026-01-13 06:01:17 +00:00
Brendan Allan bd193dbd22 feat(desktop): spawn local server with password (#8139) 2026-01-13 13:58:00 +08:00
ShoeBoom ac680c7e38 fix(config): handle write errors when updating schema in opencode config (#8125) 2026-01-12 23:11:26 -06:00
Eric Guo a13243aea9 feat(cli): Support debug tool calling directly in CLI. (#6564) 2026-01-12 23:10:56 -06:00
Aiden Cline 7b6c17b21e tweak: for zai ensure clear_thinking is false 2026-01-12 23:04:50 -06:00
Leonidas 0e9773d8f2 fix(TUI): dont submit prompt when switching sessions (#8016) 2026-01-12 22:43:44 -05:00
Dax Raad a63bb0ee5f get rid of extra file 2026-01-12 22:33:57 -05:00
Aiden Cline 771ff9ed13 core: tweak edit and write tool outputs to prevent agent from thinking edit didn't apply 2026-01-12 20:40:10 -06:00
lemon 73e8c7e74f fix: prevent [object Object] error display in console output (#8116)
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-12 20:40:03 -06:00
Patrick Schiel cb74fad057 fix: add missing args to windows tauri cli spawn (#8084) 2026-01-13 10:38:01 +08:00
M. Adel Alhashemi 73cd146c85 fix(task): respect agent task permission for nested sub-agents (#8111) 2026-01-12 20:37:42 -06:00
Rohan Mukherjee ba7a6f82e4 fix: max completion tokens error for cloudflare (#7970) 2026-01-12 20:08:27 -06:00
Adam e6960a0cd7 fix(bedrock): use reasoningConfig for Anthropic models on Bedrock (#8092) 2026-01-12 18:42:19 -06:00
Darsh Patel ec32e70716 fix: await auth.set to prevent race condition when setting API key (#8075) 2026-01-12 18:20:50 -06:00
Jérôme Benoit 97e7ad80fb fix(nix): initialize hashes.json with per-system format (#8095) 2026-01-12 18:18:48 -06:00
Paolo Ricciuti de1352f07b fix: actually modify opencode config with mcp add (#7339) 2026-01-12 18:02:59 -06:00
GitHub Action 512025314a chore: generate 2026-01-12 23:30:47 +00:00
OpeOginni 6f63f030db feat(terminal): implement better dynamic terminal numbering (#8081) 2026-01-12 17:30:09 -06:00
Jason Kölker 8807032d68 fix(session): store api url in metadata (#8090) 2026-01-12 17:22:42 -06:00
opencode-agent[bot] a76dcafd65 Add nova-2 to region prefix models (#8085)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2026-01-12 16:55:12 -06:00
Aiden Cline 2839c21a7c tweak: truncation dir perm 2026-01-12 16:46:47 -06:00
Frank b7465bf5de wip: black 2026-01-12 17:10:50 -05:00
Adam cf0c9d613b fix(app): sanitize markdown -> html 2026-01-12 15:50:24 -06:00
Aiden Cline 2c56a3bb14 test: fix read test 2026-01-12 15:49:31 -06:00
Aiden Cline e406b5ac48 tweak: make the .env reads ask user for permission instead of blocking 2026-01-12 15:32:43 -06:00
GitHub Action 2c488754fb chore: generate 2026-01-12 21:27:36 +00:00
OpeOginni c437b22c5b feat(dialog-select-server): add icon button for server removal (#8053) 2026-01-12 15:26:58 -06:00
GitHub Action 9ae3726b2d chore: generate 2026-01-12 21:22:21 +00:00
Andrew Pashynnyk 8ce7e66b9e fix(formatter): use biome check instead of format to include import sorting (#8057)
Co-authored-by: kynnyhsap <kynnyhsap@users.noreply.github.com>
2026-01-12 15:21:38 -06:00
Aiden Cline 8f93012aeb fix: ensure plurals are properly handled (#8070) 2026-01-12 15:21:01 -06:00
opencode b14ba577f6 release: v1.1.15
Update Nix Hashes / update-linux (push) Waiting to run
Update Nix Hashes / update-macos (push) Blocked by required conditions
2026-01-12 21:03:52 +00:00
Dax Raad f30091eb84 core: rename OPENCODE_PASSWORD to OPENCODE_SERVER_PASSWORD for clearer authentication configuration 2026-01-12 15:59:17 -05:00
Ryan Vogel 6dfe5f398f fix(tui): prevent layout jump when toggling tips visibility (#8068) 2026-01-12 14:57:49 -06:00
Kenny ebaaad5f36 docs: fix permission rule ordering in examples (#7010) 2026-01-12 14:45:17 -06:00
Dax Raad 3930f7e57d core: fix HTTP exception handling order to prevent NamedError from masking HTTP exceptions
docs: add OPENCODE_PASSWORD environment variable documentation for basic auth
2026-01-12 15:43:48 -05:00
Dax Raad 0ddb992bd6 core: add password authentication and improve server security
- Add OPENCODE_PASSWORD flag for basic auth protection
- Show security warnings when password is not set
- Remove deprecated spawn command
- Improve error handling with HTTPException responses
2026-01-12 15:23:19 -05:00
Daniel Polito 7f9893fd14 fix(desktop): Improve User Message Badges for Big Titles and Small Screens (#8037) 2026-01-12 14:22:03 -06:00
Dax Raad bc7f604e98 security.md 2026-01-12 15:05:37 -05:00
Aiden Cline 3780cce550 core: improve error handling for built-in plugin installation failures 2026-01-12 13:47:25 -06:00
Aiden Cline ff2a18ffdb tui: add hint for variants toggle 2026-01-12 13:35:07 -06:00
Aiden Cline 320df86f37 ci: adjust triage prompt 2026-01-12 13:28:23 -06:00
Frank 9bde0ec09b admin unshare 2026-01-12 14:20:33 -05:00
Aiden Cline 498bfb907d fix: permissions wildcarding so that for ex: 'ls *' includes ls * AND 'ls' to prevent having to double state commands or use 'ls*' 2026-01-12 13:02:29 -06:00
Ravi Kumar 0104645be5 fix(tui): reopen autocomplete after backspace deletes space (#6031)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-01-12 12:52:36 -06:00
Jérôme Benoit 86985ff63b fix(nix): filter optional dependencies by target platform (#8033) 2026-01-12 12:49:06 -06:00
Aiden Cline 5ee70ce2e3 test: fix 2026-01-12 12:33:39 -06:00
Shubh Porwal 15c303a3a3 docs: fix typo (#8041) 2026-01-12 12:30:30 -06:00
Github Action 8e54c45438 Update Nix flake.lock and hashes 2026-01-12 18:26:56 +00:00
Aiden Cline 6bd7530bd6 chore: bump ai sdk packages 2026-01-12 12:25:19 -06:00
Dax Raad 115ada5e34 core: fix model selection in title generation to use user's model instead of assistant's 2026-01-12 12:45:24 -05:00
Dax Raad aec5ce053e tui: rename kvStore to store for consistency 2026-01-12 09:53:08 -05:00
Dax Raad 2d54930666 tui: redesign tips display on home screen 2026-01-12 09:42:28 -05:00
David Hill 4d66c84ca0 Redesign tips display on home screen (#6126)
Co-authored-by: Dax <mail@thdxr.com>
2026-01-12 09:31:36 -05:00
Dax Raad 9f6d4903d5 tui: remove update complete toast notification 2026-01-12 09:30:31 -05:00
Brendan Allan 68b4ab1b91 ci: don't continue-on-error in tauri action 2026-01-12 21:27:52 +08:00
GitHub Action 9e3093a6df ignore: update download stats 2026-01-12 2026-01-12 12:05:37 +00:00
Brendan Allan 837043db77 disable appimage 2026-01-12 19:28:55 +08:00
GitHub Action 756fcf9e5d chore: generate 2026-01-12 09:58:50 +00:00
Brendan Allan c928cfd849 fix(desktop): improve server detection & connection logic (#7962) 2026-01-12 17:58:13 +08:00
GitHub Action e3f8017e1a chore: generate 2026-01-12 05:22:12 +00:00
Brendan Allan 1d509db35a app: resolve defaultServerUrl inside AppInterface
The desktop app sometimes modified __OPENCODE__.serverUrl after the
window is created. Previously this was ignored since defaultServerUrl
was created at module scope, now it isn't created until desktop's
ensure_server_started query complete, after which point serverUrl is
guaranteed to be updated.
2026-01-12 13:21:21 +08:00
opencode 333a683726 release: v1.1.14
Update Nix Hashes / update (push) Waiting to run
2026-01-12 05:08:25 +00:00
Aiden Cline eedf839fef tweak: make the subagent header have clickable nav 2026-01-11 22:43:09 -06:00
Eyal Cherevatsky a358b0b284 docs: fix scroll_speed default value (#7867) 2026-01-11 21:40:23 -06:00
Andrey Taranov bc4e327b11 fix(mcp): support resource content type in MCP tool output (#7879) 2026-01-11 21:39:42 -06:00
Zeke Sikelianos 35416b00a9 docs: fix typos in settings doc (#7892) 2026-01-11 21:20:19 -06:00
Prince Carlo Juguilon 16f29a3df6 chore: update GitHub stars count to 60K (#7899) 2026-01-11 21:19:59 -06:00
indeep99 498d499338 feat(tui): add mouse hover and click support to questions tool (#7905) 2026-01-11 22:01:48 -05:00
Dax Raad 0520285f2f tui: fix prompt ref initialization to prevent undefined reference errors 2026-01-11 19:40:16 -05:00
Dax Raad 66acd4c859 tui: refactor event streaming to use SDK instead of manual RPC subscription 2026-01-11 18:53:39 -05:00
Octane d7908c4c0f Fix: TUI single-line paste cursor position (#7277) 2026-01-12 00:50:27 +01:00
Dillon Mulroy 6c3ef5c7c5 fix: check worktree for external_directory permission in subdirs (#7811) 2026-01-11 14:17:36 -06:00
Jacob Gillespie b1efa0262d chore(lander): fix spacing in desktop app banner (#7822) 2026-01-11 14:16:29 -06:00
Daniel Polito 93c185fd78 feat(desktop): User Message Badges on Hover (#7835) 2026-01-11 14:14:26 -06:00
Daniel Polito 581c6f75b7 feat(desktop): Image Preview support for Image Attachments (#7841) 2026-01-11 14:12:52 -06:00
Zeke Sikelianos 295750889f docs: fix typos on the providers page (#7829) 2026-01-11 13:04:04 -06:00
Aiden Cline 786d5467fc bump copilot plugin version 2026-01-11 12:58:14 -06:00
Kit Langton d2d0b21918 feat(tui): add mouse hover and click support to autocomplete (#7820) 2026-01-11 12:45:20 -05:00
King'ori Maina 8576f44097 fix(tui): copy oauth url when no device code (#7812) 2026-01-11 10:27:17 -06:00
GitHub Action ba49b69c70 chore: generate 2026-01-11 16:12:14 +00:00
Kit Langton 9819bd028b fix(tui): slash command autocomplete highlighted row jumping (#7815) 2026-01-11 11:11:40 -05:00
GitHub Action bfdb3c8f72 ignore: update download stats 2026-01-11 2026-01-11 12:04:38 +00:00
opencode d86a4783af release: v1.1.13
Update Nix Hashes / update (push) Waiting to run
2026-01-11 07:10:41 +00:00
Mani Sundararajan 8e808b44d7 fix(tui): move props.ref to onMount (#7702) 2026-01-11 00:20:04 -06:00
Aiden Cline 98e28e0e5c tweak: ensure codex built in plugin is always available 2026-01-10 22:59:39 -06:00
Aiden Cline e3855041ea fix: ensure /connect works for openai business plans too (was missing field) 2026-01-10 22:48:28 -06:00
wid4t 773e30aa26 feat(acp): track file modified (#7723)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-01-10 21:46:53 -06:00
Kit Langton 1cc2a6cc56 fix(opencode): command palette mouse hover highlights wrong item (#7721) 2026-01-10 19:52:17 -05:00
Spoon 656aef30cf feat: pass sessionID to chat.system.transform (#7718) 2026-01-10 18:21:51 -06:00
Dax Raad 48070e0f8d core: extract external directory validation to shared utility to reduce code duplication across tools 2026-01-10 18:49:42 -05:00
Daniel Polito e269d80290 feat(desktop): Fork Session (#7673) 2026-01-10 16:04:01 -06:00
Daniel Polito 96e8f1179c feat(desktop): Make mouse hover / click work on prompt input autocomplete (#7661) 2026-01-10 16:02:47 -06:00
Daniel Polito 67afa7d784 feat(deskop): Add Copy to Messages (#7658) 2026-01-10 16:01:23 -06:00
Jérôme Benoit 818bba3d5a fix(desktop): add missing StoreExt import and fix unused mut warning (#7707) 2026-01-10 15:26:24 -06:00
Lekë Dobruna 15aec100af fix(desktop): build failing - missing import (#7697) 2026-01-10 15:24:15 -06:00
Frank 77726886da wip: black 2026-01-10 13:57:49 -05:00
Alejandro Chinchilla 76d6a72d33 fix: exclude 'none' variant for gpt-5.2-codex (#7682) 2026-01-10 12:22:44 -06:00
Kenny b0c72aa6fd fix(grep): follow symlinks by default in ripgrep searches (#7501) 2026-01-10 10:57:18 -06:00
Andrew Thal 41e520c2d9 feat: support configuring default server URL for desktop (#7363) 2026-01-10 08:56:48 -06:00
GitHub Action 45a667c84d chore: generate 2026-01-10 14:55:55 +00:00
OpeOginni 1b112628b9 fix(desktop): remove split operation in serverDisplayName function (#7397) 2026-01-10 08:55:20 -06:00
zerone0x 8dc9845ff8 fix(ui): allow text editing shortcuts in search dialogs on macOS (#7419) 2026-01-10 08:53:52 -06:00
Daniel Polito fce168858b fix(desktop): Allow Selecting Card Errors (#7506) 2026-01-10 08:52:11 -06:00
GitHub Action fe4b95183a chore: generate 2026-01-10 14:44:47 +00:00
madflow aa3d29f1a3 fix: disable telemetry in the php lsp server (#7649) 2026-01-10 08:44:12 -06:00
opencode fd7b3295e9 release: v1.1.12
Update Nix Hashes / update (push) Waiting to run
2026-01-10 13:56:12 +00:00
GitHub Action c3c6700bf0 chore: generate 2026-01-10 13:50:58 +00:00
Frédéric DE MATOS 51bfc5f50f fix: add ChatGPT-Account-Id header for organization subscriptions (#7603) 2026-01-10 07:50:24 -06:00
Vlad Temian da60185ae3 docs(ecosystem): add micode and octto plugins (#7327) 2026-01-10 07:44:21 -06:00
GitHub Action 11fb4bf0cf ignore: update download stats 2026-01-10 2026-01-10 12:04:24 +00:00
Github Action 34cfec4a32 Update Nix flake.lock and hashes 2026-01-10 03:10:45 +00:00
Adam 651cf61874 chore: prep 2026-01-09 21:09:26 -06:00
Adam 86d6ac0280 chore: prep 2026-01-09 20:18:42 -06:00
GitHub Action 302bd4ad66 chore: generate 2026-01-10 02:07:24 +00:00
Adam e09a8ca6a3 chore: prep 2026-01-09 19:53:04 -06:00
Adam f874556456 fix(app): more defensive dom access 2026-01-09 19:53:03 -06:00
opencode 2420ed1e40 release: v1.1.11
Update Nix Hashes / update (push) Waiting to run
2026-01-10 01:25:30 +00:00
Aiden Cline 0a27964bc1 fix: ensure gpt-5.2-codex has variants 2026-01-09 19:15:57 -06:00
Spoon 4b81b13b89 docs: add url based instructions to web docs (#7216) 2026-01-09 19:01:13 -06:00
Aiden Cline 8a12495ad9 tweak: adjust codex styling and fix hint 2026-01-09 18:59:57 -06:00
Aiden Cline 67d354a27d update docs and auth methods for openai 2026-01-09 18:45:03 -06:00
Aiden Cline dfe9624e67 fix: instance dispose issue 2026-01-09 18:26:32 -06:00
Aiden Cline 2366205b84 tweak codex instructions prompt for codex plan 2026-01-09 18:07:24 -06:00
Frank 1a734375f0 wip: black 2026-01-09 19:00:51 -05:00
GitHub Action 268b72b749 chore: generate 2026-01-09 23:48:13 +00:00
Aiden Cline ded956635e feat: codex auth support (#7537) 2026-01-09 17:47:37 -06:00
opencode 424fea0bcf release: v1.1.10
Update Nix Hashes / update (push) Waiting to run
2026-01-09 21:38:04 +00:00
Dax 043e16e061 disable server unless explicitly opted in (#7529) 2026-01-09 16:33:46 -05:00
Ariane Emory b8bbb51a5c fix: report config errors instead of silent termination (#7522) 2026-01-09 14:21:01 -06:00
Github Action f97be89acc Update Nix flake.lock and hashes 2026-01-09 19:20:11 +00:00
Sebastian Herrlinger 20336f4419 upgrade opentui to v0.1.72 2026-01-09 20:18:34 +01:00
Aiden Cline e67a5db361 ignore: bump plugin version 2026-01-09 12:33:51 -06:00
adamelmore ccee35f53e fix(app): no inline js 2026-01-09 11:57:31 -06:00
Ravi Kumar 5258cc7161 fix(cli): enable API key prompt for Bedrock in auth login (#7332) 2026-01-09 11:56:19 -06:00
ryanwyler a108cf730c fix: resolve broken forked sessions with compactions due to missing parent-child message references (#6445) 2026-01-09 11:42:55 -06:00
Aiden Cline b74eb6d0f4 ci: fix failing pipeline 2026-01-09 11:38:01 -06:00
GitHub Action 2439052db7 chore: generate 2026-01-09 17:03:51 +00:00
adamelmore 7ef73ec038 fix(app): break words in user message 2026-01-09 11:03:11 -06:00
adamelmore 34f5a952ec fix(app): no custom url param 2026-01-09 11:03:11 -06:00
Frank 57d8d1c951 wip: zen 2026-01-09 11:52:31 -05:00
Aiden Cline c42bf8f13c docs: update brew formula references 2026-01-09 10:39:59 -06:00
Github Action 0828a74283 Update Nix flake.lock and hashes 2026-01-09 16:28:31 +00:00
Sebastian Herrlinger 8d70dbaaa8 upgrade opentui to v0.1.71, fixing capability detection and therefor kitty keyboard 2026-01-09 17:26:33 +01:00
AJ Ka 6ac484b042 fix: prevent model picker overflow with Zen in new sessions (#7495) 2026-01-09 09:53:18 -06:00
Jérôme Benoit 830fe537d4 fix(desktop): complete symlink support implementation and enable CI for PRs (#7113) 2026-01-09 22:43:49 +08:00
adamelmore 8de582a501 fix(app): store terminal and review pane visibility per session 2026-01-09 08:20:00 -06:00
GitHub Action a6fb92f3ec ignore: update download stats 2026-01-09 2026-01-09 12:04:56 +00:00
adamelmore 3887a1988b fix(app): more defensive todo access 2026-01-09 06:02:45 -06:00
adamelmore bfc72bcb87 docs: perf plans 2026-01-09 05:07:16 -06:00
Jeon Suyeol f140179802 feat(config): deduplicate plugins by name with priority-based resolution (#5957) 2026-01-09 02:11:24 -06:00
Aiden Cline 6759b1f6a3 ci: tweak pr standards workflow 2026-01-09 00:54:42 -06:00
Aiden Cline 879fa539aa bump copilot plugin version 2026-01-09 01:32:00 -05:00
Aiden Cline bb716cd484 fix: model dialog issue when searching for models in fav/recents list, also ensure that deprecated models dont appear in list (#7429) 2026-01-09 01:32:00 -05:00
GitHub Action 77e3925f18 chore: generate 2026-01-09 01:32:00 -05:00
Frank c7c05408ec wip: black 2026-01-09 01:32:00 -05:00
Frank d55b3ebbfa wip: black 2026-01-09 01:32:00 -05:00
Daniel Polito ae37115845 feat(desktop): Change Terminal's Cursor to Bar and Stop Blinking when not focused (#7402) 2026-01-09 01:32:00 -05:00
Frank 554b1da0d4 wip: black 2026-01-09 01:32:00 -05:00
GitHub Action 522e908ad4 chore: generate 2026-01-09 01:32:00 -05:00
Frank d3b86dc796 wip: black 2026-01-09 01:32:00 -05:00
Frank e5beb0f696 wip: zen 2026-01-09 01:32:00 -05:00
Frank 8f02b36fe0 wip: zen
wip: zen
2026-01-09 01:32:00 -05:00
GitHub Action f36ca50428 chore: generate 2026-01-09 00:13:26 +00:00
Danil 3d3d0719c8 docs: Add Scaleway to provider docs (#7389) 2026-01-08 18:12:22 -06:00
Luke Parker 6983102594 ignore: add PR template (#7391) 2026-01-08 18:11:59 -06:00
opencode ab7a485422 release: v1.1.8
Update Nix Hashes / update (push) Waiting to run
2026-01-08 23:58:57 +00:00
Adam 0e69185cd6 Revert "fix(desktop): open external links in default browser (#7221)"
This reverts commit 248acec04d.
2026-01-08 17:51:30 -06:00
Anthony Huang 6b356cdf40 feat(desktop): add Vesper theme (#7331)
Co-authored-by: Anthony Huang <ahuang@ramp.com>
2026-01-08 17:48:57 -06:00
Adam 6d0103ecf9 feat(app): cache session-scoped stores, optional context gating 2026-01-08 17:48:15 -06:00
Adam df8ef8024f feat(app): incrementally render turns, markdown cache, lazily render diffs 2026-01-08 17:48:15 -06:00
Adam 3086b6bb45 feat(app): gate hydration side-effects, handoff while stores load 2026-01-08 17:48:15 -06:00
Adam 3f61fd30ab feat(app): prefetch adjacent sessions 2026-01-08 17:48:15 -06:00
Adam c27857c8d4 feat(app): chunk message loading, lazy load diffs 2026-01-08 17:48:15 -06:00
AJ Ka 006e8ce391 Fix for #7229 Fix/project sidebar folder name (#7232) 2026-01-08 17:47:15 -06:00
Dax Raad ceb5a761cb ci 2026-01-08 18:26:51 -05:00
Dax Raad 3782ad9b9f add sponsored 2026-01-08 18:21:21 -05:00
Github Action 365acf5c72 Update Nix flake.lock and hashes 2026-01-08 22:56:55 +00:00
Sebastian Herrlinger b813e05f2e stop esc propagation from dialogs 2026-01-08 23:53:00 +01:00
Sebastian Herrlinger d4a4f0308b upgrade opentui to v0.1.70 2026-01-08 23:52:36 +01:00
Aleksandr Bagatka c52ae9c8df fix(ui): use full file path for fuzzy matching in autocomplete (#6705)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-01-08 16:43:05 -06:00
GitHub Action fa201bd3a3 chore: generate 2026-01-08 22:33:10 +00:00
Dax 185f2e7090 feat(question): support multi-select questions (#7386) 2026-01-08 17:32:21 -05:00
GitHub Action aeb229f5c1 chore: generate 2026-01-08 22:03:23 +00:00
Marc Espin 70e31e51c5 fix(docs): Document cargofmt (#7383) 2026-01-08 16:00:03 -06:00
opencode ff2fcceeb8 release: v1.1.7
Update Nix Hashes / update (push) Waiting to run
2026-01-08 20:37:24 +00:00
ryanwyler 1a0bd4e5de fix(tui): restore showDetails check removed in Permission rework (#7285) 2026-01-08 14:30:41 -06:00
Mani Sundararajan 17f309c702 fix: cleanly restore terminal state on fatal error exit (#7359) 2026-01-08 14:22:24 -06:00
Aiden Cline 432f8d115a ci: update fork_repo var 2026-01-08 14:19:44 -06:00
Aiden Cline 67cf23dd2f ci: tweak enforcement of titles 2026-01-08 14:12:38 -06:00
zerone0x 5fa33cee6b fix(app): show custom models without valid release_date in web UI mod… (#7349) 2026-01-08 14:06:18 -06:00
Dax Raad b502b666f6 ci: add retry logic to desktop build to handle intermittent failures 2026-01-08 14:57:55 -05:00
Noam Bressler 002d9824c8 fix: Add kind, title, rawInput to ACP tool_call_update events (#7368)
Co-authored-by: noam-v <noam@bespo.ai>
2026-01-08 13:56:16 -06:00
Dax e88ce8be59 tui: improve responsive layout by hiding header/footer when sidebar is visible (#7365) 2026-01-08 13:57:23 -05:00
Aiden Cline beafb99257 ci: adjust workflow 2026-01-08 12:50:17 -06:00
Aiden Cline e351cc3f99 docs: update pr workflows 2026-01-08 12:44:04 -06:00
Dax Raad fb27a0a0bb docs: simplify tools configuration by consolidating under permission field 2026-01-08 12:39:40 -05:00
Aiden Cline 8479b6970c docs: add PR title guidelines and workflow to enforce conventional commits 2026-01-08 11:23:52 -06:00
Aiden Cline fd2e06159f ignore: ensure new file truncation stuff still works even if external_directoy is set to deny 2026-01-08 11:07:33 -06:00
Max Stevens 258ea8c336 refactor: session settings simplification (#7342) 2026-01-08 11:06:06 -06:00
Sercan Sagman f82ae79652 fix(cli): add help text to debug command and subcommands (#7328)
Signed-off-by: assagman <ahmetsercansagman@gmail.com>
2026-01-08 09:42:12 -06:00
Aaron Iker 3b8c27a781 feat: Polish dialog, popover shadows & borders, add missing provider logos (#7326) 2026-01-08 09:02:04 -06:00
GitHub Action cf2acc6ed4 ignore: update download stats 2026-01-08 2026-01-08 12:05:02 +00:00
GitHub Action d31a353245 chore: generate 2026-01-08 06:11:00 +00:00
freespace8 b60ce3a977 feat(flags): allow disabling .claude prompt and skills loading (#7205) 2026-01-08 00:10:25 -06:00
Aiden Cline 31d9a72d45 test: add larger image test for read tool 2026-01-07 23:01:02 -06:00
Dax Raad f33aa8e78b tui: remove unused imports and variables from header component to clean up code 2026-01-07 23:54:54 -05:00
Dax 4d475bfaf0 tui: add overlay sidebar for narrow screens to improve mobile experience (#7288) 2026-01-07 23:51:49 -05:00
Dax Raad 76ce399c27 tui: add expandable bash output for long commands to improve readability 2026-01-07 23:11:46 -05:00
Dax Raad 80edfd8a8e tui: remove username visibility toggle and simplify user message metadata display 2026-01-07 23:02:19 -05:00
Dax Raad 378cff7871 tui: improve other answer option layout with better indentation and clearer label 2026-01-07 22:34:31 -05:00
GitHub Action 0c4c2d77e5 chore: generate 2026-01-08 03:30:18 +00:00
Dax 544f398374 core: add interactive question tool for gathering user preferences and clarifying instructions (#7268) 2026-01-07 22:29:42 -05:00
Aiden Cline 19fc221ca8 fix: issue w/ normal transform options conflicting w/ small model options when gen-ing title 2026-01-07 17:32:38 -06:00
Aiden Cline 1bccb5e488 feat: write truncated tool outputs to files (#7239) 2026-01-07 17:25:00 -06:00
Ariane Emory c49dd1b0a1 fix(tui): ensure forked message text is inserted in prompt (resolves #7257) (#7259) 2026-01-07 16:12:03 -06:00
Andrew Thal 248acec04d fix(desktop): open external links in default browser (#7221) 2026-01-07 16:04:27 -06:00
Adam 0cc9509462 fix(app): user message text wrap 2026-01-07 15:36:08 -06:00
Frank ced9e12cda wip: zen 2026-01-07 15:49:52 -05:00
Ariane Emory 7c40a0e41c fix: use selectedForeground's computer colour (or theme's selectedForeground value) for the colour of text in permission selection (resolves #7246) (#7251) 2026-01-07 14:45:30 -06:00
M. Adel Alhashemi 4d0b807ce7 refactor: simplify task tool subagent filtering (#7165)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-01-07 13:28:13 -06:00
Aiden Cline d0d2250b5e tweak: title gen prompt & temp to avoid repetative 'Analyzing ...' titles 2026-01-07 12:56:41 -06:00
Ravi Kumar e2bf825197 fix(tui): constrain autocomplete height to available screen space (#7181) 2026-01-07 19:24:44 +01:00
Github Action dd3dd936f5 Update Nix flake.lock and hashes 2026-01-07 18:07:10 +00:00
GitHub Action cfb483d771 chore: generate 2026-01-07 18:06:30 +00:00
Rodolfo Carvalho 265ddb968a fix(mcp): use correct authorization server URL for OAuth discovery (#7234) 2026-01-07 12:05:54 -06:00
Thanh Nguyen 0bdb39e9f2 fix(theme): add selectedListItemText to orng themes for button text visibility (#7169) 2026-01-07 12:05:46 -06:00
Matt Silverlock f76e9b2e30 fix: config precedence now correctly allows local config to override remote (#7141) 2026-01-07 12:07:21 -05:00
Aiden Cline 34a6cd449c docs: update brew formula stuff to recommend our tap 2026-01-07 11:06:54 -06:00
GitHub Action 78d81cd9f3 chore: generate 2026-01-07 17:03:43 +00:00
Aiden Cline 6cbbc49b0f tweak: update formula for anomalyco tap 2026-01-07 11:02:37 -06:00
opencode f961fc503f release: v1.1.6
Update Nix Hashes / update (push) Waiting to run
2026-01-07 16:38:32 +00:00
Dax Raad 3d628f51dc oops 2026-01-07 11:35:11 -05:00
Daniel Polito 0c88d9661e Desktop: Fix Paste image on empty input (#7130) 2026-01-07 09:40:21 -06:00
Andrew Thal c99b210279 fix(ui): prevent iOS Safari auto-zoom on input focus (#7214) 2026-01-07 09:22:48 -06:00
Brendan Allan 02aa1151a7 desktop: bundleMediaFramework 2026-01-07 22:55:29 +08:00
GitHub Action d767e841de chore: generate 2026-01-07 14:50:57 +00:00
Dax Raad 66e6170c3f theme 2026-01-07 09:50:04 -05:00
opencode 52aab3cdef release: v1.1.5
Update Nix Hashes / update (push) Waiting to run
2026-01-07 14:37:04 +00:00
Dax Raad 751e842c03 theme reload 2026-01-07 09:34:11 -05:00
Dax Raad ce1aa7b2e0 clear palette cache 2026-01-07 08:49:35 -05:00
Adam 6554b2d218 fix(app): select first item when filtering 2026-01-07 07:15:06 -06:00
jwaterwater 1a39d39c15 fix: encode non-ASCII directory paths in HTTP headers (#7145)
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-07 06:46:17 -06:00
shuv eefe155631 feat: add Night Owl theme (desktop) (#7151)
Co-authored-by: shuv <shuv@shuv.dev>
2026-01-07 06:44:27 -06:00
Brendan Allan 788689e78d desktop: go back to regular tauri cli 2026-01-07 20:42:10 +08:00
GitHub Action 0724d2338e ignore: update download stats 2026-01-07 2026-01-07 12:04:44 +00:00
Brendan Allan 39bf8bf1ef fix entitlements 2026-01-07 20:03:38 +08:00
Saeed Vaziry 99e0eb773e Add audio input and camera permissions to entitlements (#7117)
Co-authored-by: Brendan Allan <brendonovich@outlook.com>
2026-01-07 17:56:46 +08:00
Brendan Allan f638f818c3 desktop: appimage bundleMediaFramework 2026-01-07 17:39:35 +08:00
GitHub Action dd14735473 chore: generate 2026-01-07 08:48:34 +00:00
Brendan Allan a7878a9ba2 desktop: use Show instead of Suspense 2026-01-07 16:47:45 +08:00
Aiden Cline 5961821ef8 add truncation for all tools 2026-01-07 02:01:32 -06:00
GitHub Action aaff5cdd88 chore: generate 2026-01-07 07:46:40 +00:00
Frank 5a37cc3a11 wip: black 2026-01-07 02:45:57 -05:00
Frank 431e212134 wip: black 2026-01-07 02:45:57 -05:00
GitHub Action a0696998ab chore: generate 2026-01-07 07:43:43 +00:00
Aiden Cline 1e703dbe8d ignore: add truncation funcs (#7178) 2026-01-07 01:43:06 -06:00
Github Action 9228729d20 Update Nix flake.lock and hashes 2026-01-07 07:36:49 +00:00
Brendan Allan d9b9f831aa add ui package to desktop 2026-01-07 15:35:32 +08:00
Daniel Polito e1b5789ee5 Make General SubAgent not hidden (#6752) 2026-01-07 01:31:47 -06:00
Brendan Allan 91a8224dfd try using forked tauri-cli again 2026-01-07 15:29:14 +08:00
usvimal 3a529d8955 perf: show window immediately during desktop startup (#6734)
Co-authored-by: Brendan Allan <git@brendonovich.dev>
2026-01-07 15:19:01 +08:00
Brendan Allan c37c9c15a0 desktop: use correct tauri-cli branch 2026-01-07 14:36:43 +08:00
Aiden Cline c7146e3b2b tweak: unserializable error handling 2026-01-07 00:29:41 -06:00
Brendan Allan f4b7a4fee2 desktop: use regular tauri cli again 2026-01-07 14:25:25 +08:00
Spoon b6e55461d7 add agent color to inline task tool ascii char and highlight agent name (#7142) 2026-01-07 00:10:00 -06:00
Brendan Allan cc7d4bd01e use custom tauri-cli on linux 2026-01-07 13:34:18 +08:00
Frank cd691237f0 wip: black 2026-01-07 00:08:49 -05:00
GitHub Action b4d936bebb chore: generate 2026-01-07 04:35:15 +00:00
Frank beadd0d14b wip: black 2026-01-06 23:34:12 -05:00
Frank 27651acda3 wip: black 2026-01-06 23:34:12 -05:00
GitHub Action 20401fb31f chore: generate 2026-01-07 04:29:50 +00:00
Sewer. 967b6779f6 Added: Ability to hide subagents from primary agents system prompt. (#4773)
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-01-06 22:29:17 -06:00
GitHub Action 934bf35ee8 chore: generate 2026-01-07 03:42:44 +00:00
Adam 622461d3bc chore: fix types 2026-01-06 21:42:04 -06:00
Adam 233813225e chore(app): rework storage approach 2026-01-06 21:42:03 -06:00
NSPC911 a95ccc6c0e fix: escape backticks when passing to powershell (#7157) 2026-01-06 21:41:33 -06:00
Github Action f7f746b812 Update Nix flake.lock and hashes 2026-01-07 03:20:32 +00:00
GitHub Action 74ba9c8341 chore: generate 2026-01-07 03:19:47 +00:00
Adam 3556bbefc4 fix(app): hide line numbers in diffs on mobile 2026-01-06 21:18:50 -06:00
Adam d472bc0fc4 fix(app): session navigation with shortcuts 2026-01-06 21:18:41 -06:00
Adam ec31f698ca fix(app): permission auto-accept should hide when not needed 2026-01-06 21:18:29 -06:00
Adam c8e6f79257 fix(app): terminal selection color contrast 2026-01-06 21:18:17 -06:00
Aiden Cline da659962b3 fix: add --use-system-ca flag in execArgv 2026-01-06 21:01:03 -06:00
Rafi Khardalian f6c43ad51e fix(tui): restore attach session lookup behavior (#7150) 2026-01-06 20:59:12 -06:00
kamkm 335d46b91d chore: Refactor style guide to use consistent language (#7139) 2026-01-06 17:54:16 -06:00
Sebastian Herrlinger b819f6e93f remove hallucinated keybinding 2026-01-07 00:19:56 +01:00
GitHub Action f8345dfb3c chore: generate 2026-01-06 23:06:08 +00:00
Aiden Cline c47bece223 fix: ensure commands listing subagent work 2026-01-06 17:05:20 -06:00
opencode c2799487b4 release: v1.1.4
Update Nix Hashes / update (push) Waiting to run
2026-01-06 22:06:38 +00:00
Daniel Polito beb1b645d0 Desktop: Fix Big Messages (#7133) 2026-01-06 16:00:37 -06:00
Jay 1c00a20f89 docs: readme
Removed section about unrelated repository.
2026-01-06 16:31:39 -05:00
GitHub Action addef87122 chore: generate 2026-01-06 21:17:29 +00:00
Frank 124a962faf zen: fix rate limit 2026-01-06 16:16:35 -05:00
Spoon e3a59ca52e adding timeout (#7128) 2026-01-06 15:00:34 -06:00
Mateusz Tymek 39fa427896 docs: add OpenCode-Obsidian plugin (#7129) 2026-01-06 15:00:22 -06:00
Justas Raudonius b7073658e2 fix(app): open links in new tab or browser (#7127) 2026-01-06 14:58:36 -06:00
Spoon 180567eafa feat: url based instructions (#7125) 2026-01-06 14:48:22 -06:00
Maik 8038e20075 chore: update stars count (#7120) 2026-01-06 14:28:47 -06:00
galkatz373 d831d14636 feat(cli): frecency file autocomplete (#6603) 2026-01-06 14:05:57 -06:00
Thomas Gormley d1445f9ffd load OPENCODE_CONFIG_DIR AGENTS.md into the system prompt (#7115) 2026-01-06 13:43:55 -06:00
Adam 746ee8fe57 fix(app): copy and paste in terminal was broken 2026-01-06 13:38:15 -06:00
Aiden Cline 521717353e core: fix title generation for subtask-only messages to extract actual user prompts instead of generic tool execution descriptions 2026-01-06 13:30:39 -06:00
Frank f197e3074e OpenCode Black 2026-01-06 14:22:54 -05:00
Damian Barabonkov 0a744313a7 docs: Expand keybinds documentation (#7108) 2026-01-06 13:10:35 -06:00
Aiden Cline caa0a73dd8 docs: fix desktop stuff 2026-01-06 13:07:20 -06:00
Aiden Cline 9cae8cdd37 Revert "fix(desktop): use current_binary() to support symlinked executables (#7102)"
This reverts commit 9b95e42ed2.
2026-01-06 12:59:05 -06:00
Aiden Cline cd5749d510 test: fix test 2026-01-06 12:31:41 -06:00
Aiden Cline 3851889d70 fix: ensure 'name' isnt being sent in request body for custom agent 2026-01-06 12:21:30 -06:00
Jérôme Benoit 9b95e42ed2 fix(desktop): use current_binary() to support symlinked executables (#7102) 2026-01-06 12:07:18 -06:00
Adam 4827392a0e fix(app): more contrast in terminal text 2026-01-06 12:03:24 -06:00
M. Adel Alhashemi f63a3cd998 fix: restore skill filtering by agent permissions (#7042) 2026-01-06 12:01:37 -06:00
Matthijs Wolting 0660c5152b fix: add missing await for available skills in skill tool (#7072) 2026-01-06 11:56:49 -06:00
Akinfolami Akin-Alamu 14cc72e493 feat(tui): add 'c' shortcut to copy device code in OAuth flow (#7020) 2026-01-06 11:54:49 -06:00
GitHub Action fa80980bc9 chore: generate 2026-01-06 17:29:44 +00:00
Guofang.Tang 617c68d6cf fix: prevent jdtls path checks from throwing (#7052) 2026-01-06 11:29:06 -06:00
ikeda-tomoya-swx f389d564c8 fix(provider): add jp. prefix auto-assignment for Tokyo region (ap-northeast-1) (#7053) 2026-01-06 11:18:34 -06:00
Guofang.Tang 01c678035b docs: add Simplified Chinese README (#7055) 2026-01-06 11:18:13 -06:00
ryanwyler 65d029d494 fix: sidebar title padding to prevent scrollbar edge case (#7089) 2026-01-06 11:15:39 -06:00
Andrew Thal fdccd56417 docs(ecosystem): add opencode-devcontainers plugin (#7100) 2026-01-06 10:58:55 -06:00
Justas Raudonius d2626a109c feat(app): add view button to open files from review sidebar (#7095) 2026-01-06 10:28:03 -06:00
Justas Raudonius eb04464515 fix(app): open review sidebar when selecting file from picker (#7096) 2026-01-06 10:27:22 -06:00
Justas Raudonius 6fc13333f9 feat(app): add middle-click to close tabs in review sidebar (#7094) 2026-01-06 10:15:17 -06:00
Adam c9dabf9d75 fix(app): code splitting for web load perf gains 2026-01-06 08:19:17 -06:00
Adam 29a5078576 fix(app): scroll store performance 2026-01-06 08:18:49 -06:00
GitHub Action 9ec33a3964 ignore: update download stats 2026-01-06 2026-01-06 12:04:50 +00:00
Eric Guo 76d1aa0768 Fix(app): @pierre/diffs will crash when a diff has undefined text (#7059) 2026-01-06 05:22:47 -06:00
Brendan Allan c22d252888 update Cargo.lock 2026-01-06 17:08:34 +08:00
Junseo5 820c5c77e0 fix(desktop): add single-instance plugin to prevent multiple windows (#6966) 2026-01-06 14:19:29 +08:00
Beryl b93ae7dbf2 fix: use actual version in install script (#7044) 2026-01-05 23:26:44 -06:00
Aiden Cline f426c1b7b2 ci: tweak prompt 2026-01-05 23:24:58 -06:00
Shane Bishop 052af48588 docs: fix order of permissions in agents docs (permissions subsection) (#7041) 2026-01-05 23:10:50 -06:00
Guofang.Tang ddcc2866c4 docs: polish markdown wording and capitalization (#7019)
Co-authored-by: Tang Guofang <tangguofang@mychery.com>
2026-01-05 22:45:40 -06:00
Frank 83b299e3f0 wip: zen 2026-01-05 22:48:07 -05:00
GitHub Action f98ff759fe chore: generate 2026-01-05 23:10:23 +00:00
Frank 328a116d86 wip: zen 2026-01-05 18:09:39 -05:00
Frank 36b5279c2d wip: zen 2026-01-05 18:09:38 -05:00
Adam 77ee19c111 fix(core): filter dead worktrees 2026-01-05 16:37:18 -06:00
Adam 6da1429063 fix(app): more defensive, handle no git 2026-01-05 15:12:02 -06:00
Github Action fc0c2bc8b8 Update Nix flake.lock and hashes 2026-01-05 20:19:14 +00:00
Sebastian Herrlinger e7e46691e9 upgrade opentui to v0.1.69, with some text rendering performance improvements 2026-01-05 21:17:45 +01:00
GitHub Action 86ba5c3c4b chore: generate 2026-01-05 19:33:32 +00:00
OpeOginni c5289bf631 Feat/clickable subtask (#6846) 2026-01-05 13:32:16 -06:00
opencode a30d5159ed release: v1.1.3
Update Nix Hashes / update (push) Waiting to run
2026-01-05 19:26:40 +00:00
Adam bd1fd88018 fix(app): store image attachments 2026-01-05 13:21:33 -06:00
Adam 7cce7d1dc5 fix(app): bottom padding 2026-01-05 13:21:32 -06:00
Adam f2b27f1911 fix(app): terminal long-running processes 2026-01-05 13:21:32 -06:00
Adam 582786e76e fix(app): terminal flakiness 2026-01-05 13:21:31 -06:00
Adam a0c6b5bb79 fix(app): performance improvements through event batching 2026-01-05 13:21:30 -06:00
Adam 99a664ef43 fix(app): prompt input shouldn't cover content 2026-01-05 13:21:30 -06:00
Adam 6cc0b3df0c fix(app): worktree selection should navigate to worktree 2026-01-05 13:21:29 -06:00
GitHub Action a8bae55480 chore: generate 2026-01-05 19:10:42 +00:00
Dax Raad 999897665c core: preserve permission config key order to maintain user-defined permission precedence 2026-01-05 14:09:37 -05:00
Guorui Yu b1a54c5631 tui: autocomplete: expand directory on Tab, select on Enter (#6975)
Signed-off-by: yuguorui <yuguorui@pku.edu.cn>
2026-01-05 13:08:17 -06:00
GitHub Action 9540fed467 chore: generate 2026-01-05 19:07:34 +00:00
Melih Mucuk 557db75d35 fix: pass image parts to custom commands (#6525)
Co-authored-by: Melih Mucuk <melih@monkeysteam.com>
2026-01-05 13:06:57 -06:00
ikeda-tomoya-swx 20a6cbad6e fix(provider): add support for jp. prefix in Amazon Bedrock cross-region inference profiles (#6916) 2026-01-05 13:03:09 -06:00
Josh Thomas fb1589dbf8 docs: update GHA examples to use actions/checkout@v6 (#6969)
publish-github-action / publish (push) Waiting to run
Update Nix Hashes / update (push) Waiting to run
2026-01-05 13:00:22 -06:00
Ravi Kumar ba4e0dc6ad ci: prevent duplicate PR check from flagging current PR as duplicate (#6924)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-01-05 12:59:42 -06:00
Rohan Mukherjee f4d6b18bed fix: lucent-orng bg transparency for slash commands (#6938) 2026-01-05 12:56:49 -06:00
Daniel Vélez c34af0443b docs: add description for MCP command (#6944) 2026-01-05 12:56:00 -06:00
Grégoire Morpain a3b25cf6f0 feat(bedrock): config options and authentication precedence (#6377) 2026-01-05 12:51:43 -06:00
MogamiTsuchikawa 7213a2fb3d fix(app): avoid unintended submits during IME composition (#6952) 2026-01-05 12:38:38 -06:00
Albin Groen ab7f13f1cc feat(app): highlight collapsed active project in sidebar (#6958) 2026-01-05 12:37:46 -06:00
Albin Groen 2d5549eee6 fix(app): fix flicker and navigation when collapsing/expanding projects (#6658) 2026-01-05 11:24:49 -06:00
Daniel Polito 2648ca065c Desktop: Install CLI (#6526)
Co-authored-by: Brendan Allan <git@brendonovich.dev>
2026-01-06 01:07:46 +08:00
Frank 7cfa30a770 sync 2026-01-05 11:59:17 -05:00
Frank ef2f449736 sync 2026-01-05 11:13:59 -05:00
Dax Raad 26bba7e5b7 tui: add system theme resolution and event handling 2026-01-05 10:38:35 -05:00
Frank 26768268a1 wip: zen 2026-01-05 10:16:47 -05:00
David Hill fe1455be1d fix: cleaner interrupted experience (#6785)
Co-authored-by: Dax <mail@thdxr.com>
2026-01-05 09:53:19 -05:00
Frank 50e6b89900 wip: zen 2026-01-05 06:16:04 -05:00
Frank 17555ff99f zen: optimize query 2026-01-05 05:58:39 -05:00
Albin Groen 4191382e77 fix(app): fix image dragging in project edit dialog (#6700) 2026-01-05 04:54:11 -06:00
GitHub Action 3f982830a7 chore: generate 2026-01-05 07:41:02 +00:00
Aiden Cline 009613423c tweak: transform 2026-01-05 01:40:15 -06:00
Aiden Cline 650843eb1d fix: for anthropic compat ensure empty msgs and empty reasoning is filtered out 2026-01-05 01:40:15 -06:00
opencode b3b77f6050 release: v1.1.2
Update Nix Hashes / update (push) Waiting to run
2026-01-05 07:16:26 +00:00
Aiden Cline 2d012c6ea8 ci: fix OPENCODE_PERMISSION env vars 2026-01-05 00:40:34 -06:00
Dax Raad 050713be41 core: fix permission rule matching to use permission field instead of pattern field 2026-01-05 01:21:49 -05:00
Aiden Cline 9ed318089d ignore: add codeowners file for adam 2026-01-05 00:17:32 -06:00
Dax Raad 8c3a75ea38 core: fix permission evaluation to use rule-based matching instead of wildcard patterns 2026-01-05 01:07:03 -05:00
Rafi Khardalian 4f8e88c243 fix(tui): allow exit when viewing child session (#6898)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-01-04 23:14:43 -06:00
GitHub Action d71a45c7d9 chore: generate 2026-01-05 03:47:12 +00:00
Frank 5e25daa66b zen: add index 2026-01-04 22:46:21 -05:00
Aiden Cline c9b4b4ef92 fix: handle skill scan failures for .claude gracefully 2026-01-04 21:39:45 -06:00
Aiden Cline 9134b08396 acp: handle case where big-pickle is unavailable as a fallback 2026-01-04 21:10:30 -06:00
Aiden Cline 683f6c9841 ci: make sure opencode is installed 2026-01-04 20:35:41 -06:00
opencode-agent[bot] 693c1f64ad Fix symmetric padding in TUI input field (#6894)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2026-01-04 20:34:14 -06:00
GitHub Action 4f08fff65f chore: generate 2026-01-05 02:18:20 +00:00
OpeOginni f10cae907c feat(desktop): implement auto-scroll for active command in slash popover (#6797) 2026-01-04 20:17:47 -06:00
Daniel Polito b675e30aa7 Desktop: Fix Responsive Menu (#6789) 2026-01-04 18:47:14 -06:00
Shkumbin Hasani 59c7003883 perf: optimize model dialog visibility lookups (#6791) 2026-01-04 18:46:23 -06:00
Daniel Polito 2fe4d2bfad Desktop: Adding Home Icon on Responsive Menu (#6794) 2026-01-04 18:44:28 -06:00
Daniel Polito 8a2d00528a Desktop: Improve Big Session Navigation - Scrollable (#6837) 2026-01-04 18:40:58 -06:00
GitHub Action 28d7346c06 chore: generate 2026-01-05 00:39:48 +00:00
shuv 6f76f8defd feat(app): add SVG preview support in session viewer (#6868) 2026-01-04 18:39:15 -06:00
NN708 d68e537784 feat(desktop): add AppStream MetaInfo file (#6030) 2026-01-04 18:36:07 -06:00
Dax Raad 6ad5746cad docs: restructure permissions documentation to clarify v1.1.1 changes and action-based model 2026-01-04 19:35:04 -05:00
Carter McBride 4ca71ced98 Fix a few mobile screen size issues (#6808) 2026-01-04 18:32:48 -06:00
Ravi Kumar 3a578efc42 fix(app): fix custom slash commands not showing on initial / (#6829) 2026-01-04 18:30:34 -06:00
Albin Groen 352598c719 fix(ui): fix select chevron alignment (#6690) 2026-01-04 18:29:19 -06:00
Aiden Cline 6e4de7e7d8 ci: nix desktop 2026-01-04 13:52:32 -06:00
Aiden Cline 2cff460d13 ci: fix dup pr action 2026-01-04 13:30:58 -06:00
Rohan Godha f1ebd698fa fix: prevent session list rows from wrapping to 2 lines (#6812) 2026-01-04 13:29:44 -06:00
Melih Mucuk b7e19741bf fix: prevent main model thinking variant from applying to small model (#6839)
Co-authored-by: Melih Mucuk <melih@monkeysteam.com>
2026-01-04 13:28:22 -06:00
Aiden Cline dcb6fcd6bf tweak: bump default to 30 seconds (lots of people complained about 5...) 2026-01-04 13:26:43 -06:00
Aiden Cline 24bbd0657d docs: add instructions for running web and desktop apps during development 2026-01-04 13:12:43 -06:00
Dax Raad 92688c0303 tui: fix session configuration merge conflict resolution 2026-01-04 13:43:33 -05:00
Dax Raad 917c60c8e0 tui: fix optional session access to prevent runtime errors 2026-01-04 13:43:33 -05:00
Github Action 3a19b16b01 Update Nix flake.lock and hashes 2026-01-04 18:39:44 +00:00
GitHub Action 7cb6c53dcd chore: generate 2026-01-04 18:39:08 +00:00
Dax Raad 82c2b82a26 tui: add session search functionality with debounced input and server-side filtering 2026-01-04 13:38:30 -05:00
Dax Raad 20cc23edd6 core: improve Rust formatter detection and add cargo fmt support 2026-01-04 13:04:28 -05:00
GitHub Action 00431c5646 chore: generate 2026-01-04 17:09:30 +00:00
Adam cb03e214ef fix(app): prompt input improvements 2026-01-04 11:08:47 -06:00
Adam c3d6086de8 fix(app): prompt input improvements 2026-01-04 10:37:56 -06:00
opencode 2222fc8517 release: v1.1.1
Update Nix Hashes / update (push) Waiting to run
2026-01-04 15:52:55 +00:00
Github Action fd943459a1 Update Nix flake.lock and hashes 2026-01-04 15:39:15 +00:00
Dax Raad 0b6a1a3c16 tui: add development scripts for better debugging workflow 2026-01-04 10:38:02 -05:00
Dax Raad 2115d33db3 ensure @opencode-ai/plugin exists only on first run 2026-01-04 10:23:42 -05:00
GitHub Action 3af14ae371 chore: generate 2026-01-04 15:13:52 +00:00
Adam Spiers 3c572587e0 fix(tui): add missing theme_list keybind (#6779)
Co-authored-by: Adam Spiers <opencode@adamspiers.org>
2026-01-04 09:13:19 -06:00
Paolo Ricciuti 1e49c94c76 feat: mcp resources (#6542) 2026-01-04 09:12:54 -06:00
Jérôme Benoit b95683374e feat(nix): preliminary desktop app flake integration (#6135)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-01-04 09:11:05 -06:00
Dax Raad a0b0d10434 initialize config in worktree 2026-01-04 10:10:25 -05:00
Felipe Orlando a691865085 Remove opencode-skills entry from ecosystem.mdx (#6817) 2026-01-04 08:43:36 -06:00
John Connor e175380d6c docs: typo in subtask documentation (#6821) 2026-01-04 08:43:03 -06:00
Aiden Cline 648a547777 ci: update duplicate pr action 2026-01-04 08:36:21 -06:00
GitHub Action 5434440af5 chore: generate 2026-01-04 14:22:48 +00:00
Matt Silverlock e200caf0fd docs: add logging best practices for plugin authors (#6833) 2026-01-04 08:22:14 -06:00
Aiden Cline 2af6a8aabd tweak: prioritize free gpt-5-mini for small model in github copilot 2026-01-04 08:21:09 -06:00
Adam bf23d9486c fix(app): auto-scroll 2026-01-04 06:14:24 -06:00
Adam c5c921477b fix(app): scroll position restoration 2026-01-04 04:53:55 -06:00
Adam a535b0aa9c fix(app): don't override ctrl+a on windows 2026-01-04 04:35:26 -06:00
Adam c1fc19609c fix(app): auto-scroll behaviors 2026-01-04 04:24:37 -06:00
Matt Silverlock 4f3d0bf663 github: handle duplicate PR creation when agent creates PR (#6777) 2026-01-04 02:05:08 -06:00
Aiden Cline d074b749f8 tui: fix system theme diff highlighting
- Generate distinct red/green backgrounds for added/removed lines in system theme
- Use bright ANSI colors for diff highlights to improve visibility
- Fix ANSI palette indexing to handle null entries safely
- Add color tinting to create proper diff backgrounds while respecting terminal colors

Resolves issue where system theme showed no red/green diff highlighting
2026-01-04 02:01:02 -06:00
Aiden Cline 513dce744f core: remove hardcoded .env read block and use new permissions model instead 2026-01-04 01:49:49 -06:00
Shpetim e0eff41db5 fix(plugin): prevent duplicate plugin function initialization (#6787)
Co-authored-by: Shpetim <shpetim.alimi@ndbit.net>
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2026-01-04 01:39:54 -06:00
ben 872cb0b3f7 docs: Add opencode-scheduler plugin to ecosystem (#6804)
Co-authored-by: Benjamin Shafii <benjaminshafii@home-server.local>
2026-01-04 01:08:14 -06:00
Aiden Cline 1b7fc643dc core: update github copilot model model priority list 2026-01-04 00:57:35 -06:00
Aiden Cline f5fe5a530f ignore: update sst -> anomalyco 2026-01-04 00:30:03 -06:00
Aiden Cline e25116edd6 core: add variant to chat.message input 2026-01-04 00:28:52 -06:00
Aiden Cline 2335233a85 ci: add duplicate PR detection bot 2026-01-04 00:15:59 -06:00
shuv 72a186c6b7 feat: add --variant flag to run command (#6805)
Co-authored-by: shuv <shuv@shuv.dev>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-01-04 00:08:34 -06:00
Frank cda4302a28 zen: update models 2026-01-04 00:58:06 -05:00
jerilynzheng ca701923da docs: Add Vercel AI Gateway to provider docs (#6790) 2026-01-03 20:57:26 -06:00
GitHub Action 8d8f2e4a78 chore: generate 2026-01-04 02:57:20 +00:00
Saatvik Arya 6c71b65c16 docs: update AGENTS.md (#6800) 2026-01-03 20:56:47 -06:00
Adam 293fa7b984 fix(app): diff rendering performance 2026-01-03 19:57:59 -06:00
Dax Raad 7954138b6d tui: remove openrouter provider from priority list 2026-01-03 20:45:15 -05:00
Dax Raad a2e8b63dff core: fix dependency installation and git worktree branch creation 2026-01-03 20:22:19 -05:00
Dax Raad 7a2e94b7e2 oops 2026-01-03 19:25:59 -05:00
GitHub Action 1c28383a6f chore: generate 2026-01-03 23:53:29 +00:00
Dax Raad 1193d7c380 core: add sandbox support for git worktrees to allow working in multiple directories per project 2026-01-03 18:52:53 -05:00
Mani Sundararajan 0555675a87 fix(tui): make lsp status icon muted when no lsps are active (#6773) 2026-01-03 14:56:29 -06:00
Rhys Sullivan 8d1ca26da3 fix(mcp): add timeout to client.connect() calls (#6760) 2026-01-03 11:54:24 -06:00
Osinachi Okpara 8f1e51d9ef docs: enhance MCP servers documentation with a tip (#6713) 2026-01-03 11:02:05 -06:00
Daniel Polito 28a7bc9c59 Desktop: Edit Project Fix (#6757) 2026-01-03 10:26:30 -06:00
Mani Sundararajan 10aa36e554 fix(tui): make mcp status icon muted when no mcp servers are enabled (#6745) 2026-01-03 10:23:09 -06:00
OpeOginni 3b784f30c1 fix(server): update server URL normalization to retain path (#6647) 2026-01-03 09:50:15 -06:00
shuv d3a1292f64 feat(app): add image preview support in session viewer (#6678) 2026-01-03 05:46:42 -06:00
GitHub Action 8603bb8c0e chore: generate 2026-01-03 07:16:11 +00:00
Jake Nelson 4f6adca35e feat: add per-project MCP config overrides (#5406)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-01-03 01:15:37 -06:00
Gabriel Patzleiner 22be26d1cb fix(kotlin-ls): improve root detection for Gradle multi-project builds (#6717) 2026-01-03 01:14:01 -06:00
Aiden Cline 3046ace550 core: improve plugin loading to handle builtin plugin failures gracefully (#6739) 2026-01-03 00:54:35 -06:00
GitHub Action 03f8e3045d chore: generate 2026-01-03 06:35:01 +00:00
Dax Raad d738332fc7 tui: add reject message support to permission dialogs for better user feedback 2026-01-03 01:34:23 -05:00
Aiden Cline 94cd6a624b ignore: update bug report template 2026-01-03 00:20:43 -06:00
Aiden Cline 75aa4fa4bd core: ephemerally wrap queued user messages with reminder to stay on track (#6725) 2026-01-02 22:42:56 -06:00
shuv 841fa12720 fix(tui): pass attach directory to sdk client (#6715)
Co-authored-by: shuv <shuv@shuv.dev>
2026-01-02 21:54:11 -06:00
opencode-agent[bot] 5e507c5a78 docs: rm incorrect -p alias from docs (#6721)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2026-01-02 21:11:42 -06:00
GitHub Action 414d2ebd34 chore: generate 2026-01-03 02:17:34 +00:00
Adam 30800b0b05 feat: add managed git worktrees (#6674) 2026-01-02 20:17:02 -06:00
Github Action 313f5c9343 Update Nix flake.lock and hashes 2026-01-03 00:10:15 +00:00
Sebastian Herrlinger 3385d85f4b upgrade opentui to v0.1.68, using gpa 2026-01-03 01:08:58 +01:00
Dax Raad f6d6d27259 ci 2026-01-02 18:58:56 -05:00
Dax Raad dccca5e9f9 ci 2026-01-02 18:56:41 -05:00
Dax Raad 530b1ac163 tui: add escape key handling to permission dialogs for better keyboard navigation 2026-01-02 18:48:26 -05:00
GitHub Action 4c5a056ca8 chore: generate 2026-01-02 22:58:02 +00:00
YeonGyu-Kim ca71d4c289 feat(plugin): add tui.session.select API endpoint for TUI navigation (#6565)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-01-02 16:57:21 -06:00
GitHub Action 2a8d13bb62 chore: generate 2026-01-02 22:46:22 +00:00
Troy Gaines 9663c11e91 feat: Add kotlin lsp integration (#6601) 2026-01-02 16:45:44 -06:00
theavgjojo dba8f06845 chore: add license field to package.json (#6693)
publish-github-action / publish (push) Waiting to run
Update Nix Hashes / update (push) Waiting to run
Co-authored-by: theavgjojo <jojo@noreply>
2026-01-02 16:29:09 -06:00
Spoon 5662d74092 docs: add subtask2 to ecosystem page (#6704) 2026-01-02 16:26:06 -06:00
Dax Raad 585a2c507a anomalyco/opencode
publish-github-action / publish (push) Waiting to run
Update Nix Hashes / update (push) Waiting to run
2026-01-02 16:09:06 -05:00
Dax a1a32f22c9 chore: rename repo references from sst/opencode to anomalyco/opencode (#6687)
publish-github-action / publish (push) Waiting to run
Update Nix Hashes / update (push) Waiting to run
Co-authored-by: Github Action <action@github.com>
2026-01-02 16:02:52 -05:00
Matt Silverlock 613ddb3be4 fix: merge instructions arrays across config files (#6663) 2026-01-02 13:55:40 -06:00
Matt Silverlock e44e5289b1 fix: handle actions/checkout v6 credential storage change (#6667) 2026-01-02 13:54:43 -06:00
Adam a4a6fd2809 feat: add /compact session command 2026-01-02 12:40:37 -06:00
Adam 6eadff2642 fix(desktop): tweak window drag region layout 2026-01-02 12:40:36 -06:00
Adam 6cfd567da5 fix: relax request timeouts 2026-01-02 12:40:36 -06:00
Aiden Cline 2c5703aa19 test: fix transform test 2026-01-02 12:38:44 -06:00
Aiden Cline 55fb243f82 test: add message-v2 test 2026-01-02 12:28:40 -06:00
Aiden Cline 76a1ff730c fix: variant for minimal 2026-01-02 12:28:40 -06:00
Aiden Cline 0f22b6e67a chore: ensure empty message isnt sent 2026-01-02 12:28:40 -06:00
Mike English 9afc2905c5 fix: cloudflare-ai-gateway sdk.chat undefined error (#6407) 2026-01-02 11:24:13 -06:00
rari404 b5eabf7e4e refactor: use Bun.sleep instead of Promise setTimeout (#6620)
publish-github-action / publish (push) Waiting to run
Update Nix Hashes / update (push) Waiting to run
2026-01-02 11:12:02 -06:00
steez 9cfef101cb feat(theme): add Osaka Jade theme (#6609) 2026-01-02 10:26:01 -06:00
Albin Groen b4e0c3fdd2 fix(ui): fix slight vertical overflow in project selector (#6589) 2026-01-02 06:24:20 -06:00
OpeOginni 5b162229e1 fix(desktop): Properly decode session id for permission context (#6580) 2026-01-02 06:22:50 -06:00
GitHub Action 181e18f539 ignore: update download stats 2026-01-02 2026-01-02 12:04:42 +00:00
Luke Parker 0276cc82ad fix(share): handle NotFoundError for non-shared sessions in sync (#6634) 2026-01-02 04:16:12 -06:00
Dax Raad d756749f15 core: improve plugin loading to handle builtin plugin failures gracefully 2026-01-01 23:15:04 -05:00
Dax Raad a6f1a08e15 ci 2026-01-01 22:44:22 -05:00
GitHub Action a61c70de34 chore: generate 2026-01-02 03:12:21 +00:00
Dax Raad c0c2d7fea3 tui: add reload functionality and improve lazy utility with reset capability 2026-01-01 22:11:43 -05:00
opencode 3a5480b2b0 release: v1.0.224
Update Nix Hashes / update (push) Waiting to run
2026-01-02 03:05:57 +00:00
Adam f90ac0b99c wip(app): progress 2026-01-01 21:03:08 -06:00
Adam e7dd6886de fix(app): startup time 2026-01-01 21:03:08 -06:00
Adam dfdb03cb86 chore: cleanup 2026-01-01 21:03:07 -06:00
Adam c58a4d14ab chore: cleanup 2026-01-01 21:03:07 -06:00
Adam e29284f8c8 wip(app): progress 2026-01-01 21:03:06 -06:00
Adam 781b0c1122 wip(app): progress 2026-01-01 21:03:06 -06:00
Adam bf7feecd56 wip(app): progress 2026-01-01 21:03:06 -06:00
Adam ac52a9261c wip(app): progress 2026-01-01 21:03:05 -06:00
Adam e04c380c8e wip(desktop): progress 2026-01-01 21:03:05 -06:00
Adam 678d448675 wip(desktop): progress 2026-01-01 21:03:04 -06:00
Adam 4e728c22cb wip(desktop): progress 2026-01-01 21:03:04 -06:00
Adam 0b9650cc1d wip(desktop): progress 2026-01-01 21:03:03 -06:00
Adam 604267c134 wip(app): file context 2026-01-01 21:03:03 -06:00
Dax Raad cc43462bcb tui: remove memory leak fixes documentation after implementation 2026-01-01 21:40:28 -05:00
GitHub Action 33d69b5175 chore: generate 2026-01-02 02:28:48 +00:00
Dax Raad 563aa49f43 tui: fix memory leaks in session management and improve permission error handling 2026-01-01 21:28:11 -05:00
Dax Raad 8290bca068 tui: add heap snapshot option to system menu for debugging memory usage 2026-01-01 21:18:28 -05:00
Dax Raad 9d8325b7ec tui: fix permission tests for new evaluate function signature 2026-01-01 21:01:34 -05:00
Dax Raad 18dfcd89c1 tui: improve permission error handling and evaluation logic 2026-01-01 21:01:00 -05:00
Dax Raad b248c73361 ignore 2026-01-01 20:42:06 -05:00
Dillon Mulroy b535c3a5f8 feat: add assistant metadata to session export (#6611) 2026-01-01 18:56:23 -06:00
Mani Sundararajan adf81f4c58 fix: windows fallback for "less" cmd in session list (#6515) 2026-01-01 18:53:29 -06:00
Daniel Polito b67356b7a4 Desktop: Improve Resize Handle (#6608) 2026-01-01 18:26:34 -06:00
Aiden Cline 9c749cbd09 test: fix bash test 2026-01-01 17:53:20 -06:00
Aiden Cline fb2dbbab9b fix: ensure new permissions changes work for special case bash commands like rm, cd, etc 2026-01-01 17:27:23 -06:00
Aiden Cline 9835b5b323 ignore: comment out repo default for ask 2026-01-01 17:13:31 -06:00
GitHub Action ea13b61089 chore: generate 2026-01-01 22:54:43 +00:00
Dax e839c1522d Permission rework (#6319)
Co-authored-by: Github Action <action@github.com>
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
2026-01-01 17:54:11 -05:00
Aiden Cline 8f7047e990 core: fix import command regex 2026-01-01 15:18:32 -06:00
Frank 9d496982b6 docs: update zen processing fee 2026-01-01 16:10:13 -05:00
alcpereira 787d27331d fix: remove outdated Haiku filter for GitHub Copilot (#6593) 2026-01-01 14:04:31 -06:00
Aiden Cline 3271b9a047 core: fix stats command day calculation and time filtering 2026-01-01 13:25:26 -06:00
Aiden Cline 18daa712e3 ignore: keep the process exit logic 2026-01-01 13:05:08 -06:00
Aiden Cline 19a1c0e8f4 core: make installdeps non blocking 2026-01-01 13:00:39 -06:00
Aiden Cline 2fab629722 core: use --no-cache when behind proxy to avoid hangs 2026-01-01 12:46:25 -06:00
Saatvik Arya a910143d53 fix(session): check for context overflow mid-turn in finish-step (#6480) 2026-01-01 12:03:18 -06:00
Lekë Dobruna acb5d524ed fix: display error if invalid agent is used in a command (#6578) 2026-01-01 11:39:21 -06:00
GitHub Action 6181e21c70 chore: generate 2026-01-01 17:35:37 +00:00
Tom 8a9a82890a fix(server): add Content-Type headers for proxied static assets (#6587) 2026-01-01 11:35:04 -06:00
GitHub Action be3b79a82e ignore: update download stats 2026-01-01 2026-01-01 12:04:57 +00:00
opencode 8d36be0beb release: v1.0.223
Update Nix Hashes / update (push) Waiting to run
2026-01-01 11:25:32 +00:00
Adam 3ce61074a5 feat(app): context window window 2026-01-01 05:23:07 -06:00
Adam 5858bbc094 fix(util): checksum defensiveness 2026-01-01 05:23:07 -06:00
Adam 734a8e0524 fix(util): checksum defensiveness 2026-01-01 05:23:06 -06:00
Adam 0675fadfcb fix(app): update primitive colors 2026-01-01 05:23:06 -06:00
Adam d5d722ed88 fix(app): update primitive colors 2026-01-01 05:23:05 -06:00
GitHub Action 9af83ec0bb chore: generate 2026-01-01 04:59:38 +00:00
Aiden Cline 98d3881f49 Reapply "fix(tui): don't show 'Agent not found' toast for subagents (#6528)"
This reverts commit f1567bc070.
2025-12-31 22:58:06 -06:00
Github Action 90c2c25cd5 Update Nix flake.lock and hashes 2026-01-01 01:21:05 +00:00
opencode 1c55bfc7a2 release: v1.0.222
Update Nix Hashes / update (push) Waiting to run
2026-01-01 01:21:04 +00:00
Luke Parker 39cedd20ea fix(desktop): Windows support for PTY and cross-platform build scripts (#6508) 2025-12-31 19:18:07 -06:00
Adam e4e7f20972 feat(desktop): in-app update toasts 2025-12-31 18:19:53 -06:00
Adam 7852fe8f8f feat(desktop): lose the summaries 2025-12-31 18:16:52 -06:00
Daniel Polito 7e0918bc5d Desktop: Add Subagents Mention Support (#6540) 2025-12-31 18:07:45 -06:00
GitHub Action 78702692fb chore: generate 2025-12-31 22:16:32 +00:00
Aiden Cline 804c2a3c22 core: prevent TimeoutOverflowWarning by capping setTimeout delay to max 32-bit signed integer 2025-12-31 16:15:49 -06:00
opencode 83d8e564d9 release: v1.0.221
Update Nix Hashes / update (push) Waiting to run
2025-12-31 21:10:33 +00:00
Adam b137e4531b chore: cleanup 2025-12-31 15:08:00 -06:00
Adam 5e30c113ff feat(app): better model selector 2025-12-31 15:07:59 -06:00
Aiden Cline 164026b530 tweak: default to ai-sdk/opeai-compatible if no npm package provided 2025-12-31 14:54:21 -06:00
Aiden Cline eec8e4c1b0 Reapply "tweak: adjust keys for uniqueness calculations to use provider/model"
This reverts commit 9d32a0354f1db3ea4893f4ad00900c6420ab78c6.
2025-12-31 14:54:21 -06:00
Aiden Cline c9e55e2b00 Revert "tweak: adjust keys for uniqueness calculations to use provider/model"
This reverts commit 3f8c159a07.
2025-12-31 14:54:21 -06:00
Adam 2f47ef3dd2 fix(app): auto-accept colors 2025-12-31 14:35:41 -06:00
Adam f6d3b15a55 fix(desktop): tooltip colors 2025-12-31 14:20:21 -06:00
Adam 1944078d07 feat(desktop): better affordance for auto-accept 2025-12-31 14:04:44 -06:00
Adam 3fbe33527a fix(app): text selection 2025-12-31 13:32:44 -06:00
Adam 465c0bb7a0 wip: input changes 2025-12-31 13:31:46 -06:00
Adam 9149274721 chore: cleanup 2025-12-31 13:13:50 -06:00
Adam a005f4de90 fix(desktop): don't show notifs if auto-accepting 2025-12-31 13:12:31 -06:00
Adam fd104c46a9 fix(desktop): more defensive access 2025-12-31 13:12:30 -06:00
Adam 61bf30b3f2 wip(app): better variant toggle 2025-12-31 13:12:30 -06:00
Adam b019d1e6d8 chore(app): tool args cleanup 2025-12-31 13:12:29 -06:00
Adam cad544fc12 chore(app): keybind tooltip component 2025-12-31 13:12:29 -06:00
Adam aed8f1390a fix(desktop): prompt input cleanup 2025-12-31 13:12:28 -06:00
Aiden Cline f1567bc070 Revert "fix(tui): don't show 'Agent not found' toast for subagents (#6528)"
This reverts commit 25949ab02d.
2025-12-31 13:11:12 -06:00
Vlad Temian 25949ab02d fix(tui): don't show 'Agent not found' toast for subagents (#6528) 2025-12-31 13:04:23 -06:00
Aiden Cline 5759d70aa8 chore: rm unused import 2025-12-31 12:11:42 -06:00
Aiden Cline dc87399c8a ci: tweak changelog ensure all cmd/ things fall under tui section 2025-12-31 12:05:35 -06:00
GitHub Action eb76614f41 chore: generate 2025-12-31 17:35:47 +00:00
Marcel de Vries 1ca5bd9a35 Add support for LSP workspace/didChangeWatchedFiles (#6524)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-31 11:35:09 -06:00
opencode b33c3e6815 release: v1.0.220
Update Nix Hashes / update (push) Waiting to run
2025-12-31 17:30:57 +00:00
Aiden Cline 50280cd65f fix: plugin & mode globs 2025-12-31 11:28:06 -06:00
GitHub Action 793376276b chore: generate 2025-12-31 17:19:02 +00:00
Michael Ramos 290aec3f85 docs: Add plannotator plugin (#6510) 2025-12-31 11:18:28 -06:00
Eric Guo f9ea9949df feat(cli): New debug agent <name> subcommand (#6529) 2025-12-31 11:16:03 -06:00
Anzul Aqeel 3e590dfffd fix: Clarify agent-name placeholder in tips (#6520) 2025-12-31 11:15:07 -06:00
opencode-agent[bot] 37f698e5c5 docs: new configurable CORS option (#6522)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
2025-12-31 11:11:20 -06:00
Frank ed22a01de2 zen: fix fee instruction 2025-12-31 11:54:01 -05:00
Adam 7519e635ae fix(desktop): remove status bar, new elements in header 2025-12-31 10:22:17 -06:00
Adam 2fb2195189 fix(desktop): cleanup user message style 2025-12-31 10:22:16 -06:00
Adam 2c7a9147ba fix(desktop): better new session button 2025-12-31 10:22:16 -06:00
Paolo Ricciuti fde0908405 fix: display MCP tag for prompts in autocomplete but not in prompt (#6531) 2025-12-31 09:34:36 -06:00
Adam 022c933076 fix(ui): radio group styles 2025-12-31 09:26:14 -06:00
GitHub Action 482c4c72d6 chore: generate 2025-12-31 15:24:05 +00:00
Adam 6f35b35459 feat(desktop): unified diff toggle 2025-12-31 09:23:24 -06:00
Adam dfc87995d2 fix(desktop): not rendering large sessions 2025-12-31 08:29:36 -06:00
opencode 524592d69c release: v1.0.219
Update Nix Hashes / update (push) Waiting to run
2025-12-31 14:01:59 +00:00
Adam 3fd628121d fix(core): mdns fails if service already registered 2025-12-31 07:33:49 -06:00
Adam 89260bfd50 fix(desktop): don't expand tools by default 2025-12-31 07:10:47 -06:00
GitHub Action 483dbe59f5 ignore: update download stats 2025-12-31 2025-12-31 12:04:56 +00:00
Adam 3d36f9d2a1 chore: cleanup 2025-12-31 04:47:24 -06:00
Adam f6cbf4e3f4 chore: daytona skip preview warning 2025-12-31 04:22:53 -06:00
GitHub Action 0a9cd25e28 chore: generate 2025-12-31 09:51:12 +00:00
Adam a62738b1e1 feat(core): configurable cors hosts 2025-12-31 03:50:29 -06:00
Aiden Cline d2ce6f8b8b docs: add variants docs (#6516)
Co-authored-by: David Hill <iamdavidhill@gmail.com>
2025-12-31 01:17:50 -06:00
GitHub Action aad5b9f7c1 chore: generate 2025-12-31 06:51:59 +00:00
Paolo Ricciuti 99e073ca16 feat: mcp prompts as slash commands (alternative) (#5767)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-31 00:51:25 -06:00
Github Action 07875f5465 Update Nix flake.lock and hashes 2025-12-31 06:11:51 +00:00
Qio 56ac68ae31 feat: add gemini-3-flash to fast models list (#6497)
Co-authored-by: qio <handsomehust@gmail.com>
2025-12-31 00:11:47 -06:00
OpeOginni 095e56e5e4 feat: bundle in @ai-sdk/vercel version 1.0.31 for aisdk v5 support (#6512) 2025-12-31 00:10:42 -06:00
David Hill 0d42e7b660 fix: cleaner view subagents hint text (#6437)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-30 22:47:16 -06:00
Brian Clinkenbeard b5cd029f1e Update mise install command (#6504) 2025-12-30 22:43:15 -06:00
GitHub Action 64839d27fd chore: generate 2025-12-31 04:06:43 +00:00
Dax Raad 41cfa88307 core: add configurable timeout for MCP tool calls to prevent hanging requests 2025-12-30 23:06:07 -05:00
Adam 0ae162b9e2 fix(app): don't open native folder select with remote server 2025-12-30 20:15:57 -06:00
Adam 049dc21c34 feat(app): hide reasoning once agent is done 2025-12-30 20:09:32 -06:00
Adam d7582924a4 feat(app): model variants 2025-12-30 20:06:03 -06:00
Adam ec9e14d319 fix(desktop): better notification icon 2025-12-30 19:40:14 -06:00
Aiden Cline 0b6aa7bfb9 tweak: read plurals too and stop erroring on them 2025-12-30 18:58:31 -06:00
Aiden Cline 3f8c159a07 tweak: adjust keys for uniqueness calculations to use provider/model 2025-12-30 18:41:28 -06:00
opencode-agent[bot] 1dd5ad7676 docs: for stats --models flag (#6492)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-30 18:31:48 -06:00
Aiden Cline 6aac942ad0 fix: openai variants for codex models 2025-12-30 18:18:16 -06:00
processtrader 223ed5cc73 feat: add model usage statistics with input/output token breakdown to stats command (#6296) 2025-12-30 17:52:41 -06:00
Farhad Omid 4ae525eb93 feat(format): add rustfmt formatter for Rust files (#6482) 2025-12-30 17:06:55 -06:00
GitHub Action 870a402062 chore: generate 2025-12-30 22:38:06 +00:00
Aiden Cline f5e84c3225 fix: ensure variants also work for completely custom models (#6481)
Co-authored-by: Daniel Smolsky <dannysmo@gmail.com>
2025-12-30 16:37:32 -06:00
GitHub Action e0e25e8c72 chore: generate 2025-12-30 20:32:02 +00:00
Adam 513f8e700b feat(desktop): upgrade to latest version on error page 2025-12-30 14:31:16 -06:00
opencode 61c6936791 release: v1.0.218
Update Nix Hashes / update (push) Waiting to run
2025-12-30 20:17:26 +00:00
Adam 836a328ddd fix(core): cors exception for tauri 2025-12-30 14:14:44 -06:00
Jay V 6b5ad77808 docs: add auto-reload and monthly limits documentation to Zen guide 2025-12-30 12:57:58 -07:00
GitHub Action 29ff7bb09b chore: generate 2025-12-30 19:14:09 +00:00
ravshansbox 5db6150aa1 Add prisma language server (#6462) 2025-12-30 13:13:36 -06:00
Ytzhak 6a5403dfb5 feat: add extract reasoning middleware (#6463) 2025-12-30 13:13:18 -06:00
opencode 44c8e044f1 release: v1.0.217
Update Nix Hashes / update (push) Waiting to run
2025-12-30 19:01:36 +00:00
Dax Raad 49a4881468 core: allow CORS requests from tauri://localhost 2025-12-30 13:58:41 -05:00
GitHub Action bc26bf5d38 chore: generate 2025-12-30 18:34:33 +00:00
Aiden Cline f41ff554e8 fix: move variant toggle to command bar 2025-12-30 12:33:50 -06:00
opencode 47585fb969 release: v1.0.216
Update Nix Hashes / update (push) Waiting to run
2025-12-30 18:07:37 +00:00
Dax Raad e520862069 core: allow CORS requests from *.opencode.ai subdomains 2025-12-30 13:04:18 -05:00
GitHub Action 2edebd188d chore: generate 2025-12-30 17:59:01 +00:00
ja e8bdae3392 style(nix): use idiomatic inherit syntax (#6457) 2025-12-30 11:58:28 -06:00
opencode c6490e9473 release: v1.0.215
Update Nix Hashes / update (push) Waiting to run
2025-12-30 17:09:08 +00:00
Adam 8e59a6babe fix(desktop): more defensive agent access 2025-12-30 11:03:34 -06:00
GitHub Action 9d6351fb4a chore: generate 2025-12-30 16:52:42 +00:00
Fayçal Mitidji 31ef07ae40 Fix: High CPU / memory leak when filtering model list window to empty results (#6435) 2025-12-30 10:52:06 -06:00
opencode 9eb306c15e release: v1.0.214
Update Nix Hashes / update (push) Waiting to run
2025-12-30 16:37:22 +00:00
Adam 5fab699034 fix(desktop): sort servers by health 2025-12-30 10:34:55 -06:00
Adam d91c5ac8ce fix(desktop): don't persist fallback server urls 2025-12-30 10:31:48 -06:00
Adam 1544f30531 fix(share): broken share pages 2025-12-30 10:27:06 -06:00
Adam 9aab1ea1fa fix(app): better text selection 2025-12-30 10:21:37 -06:00
Rohan Mukherjee c60991a530 fix: cloudflare provider information (#6426) 2025-12-30 09:45:09 -06:00
opencode-agent[bot] 666bc46e3f docs: projects, find.files, notifications (#6438)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
2025-12-30 09:44:09 -06:00
Didier Durand 83cd25b5b0 chore: fix various typos (#6429) 2025-12-30 09:42:04 -06:00
GitHub Action 94fad1300c chore: generate 2025-12-30 15:28:59 +00:00
Adam b8434da097 fix(desktop): share projects across all local servers 2025-12-30 09:28:19 -06:00
opencode f26d75c6c4 release: v1.0.213
Update Nix Hashes / update (push) Waiting to run
2025-12-30 15:26:35 +00:00
Adam d97571c501 fix(desktop): don't hang on to dead server 2025-12-30 09:24:03 -06:00
Github Action 7f091530eb Update Nix flake.lock and hashes 2025-12-30 14:32:38 +00:00
Sebastian Herrlinger 984e9bf4a2 upgrade opentui to v0.1.67, fixing split diff alignment and markdown jitter 2025-12-30 15:31:02 +01:00
GitHub Action cbc3bcd26e chore: generate 2025-12-30 13:37:48 +00:00
opencode 225ee84100 release: v1.0.212
Update Nix Hashes / update (push) Waiting to run
2025-12-30 13:37:47 +00:00
Adam 3a94092e58 chore: cleanup 2025-12-30 07:35:20 -06:00
Adam 9ec4d4acfa feat(app): change server 2025-12-30 07:24:40 -06:00
opencode cf6bc60103 release: v1.0.211
Update Nix Hashes / update (push) Waiting to run
2025-12-30 13:04:07 +00:00
Github Action ee63074835 Update Nix flake.lock and hashes 2025-12-30 12:58:37 +00:00
ryanwyler d34adbd5ab fix: revert opentui to 0.1.63 to fix streaming jitter regression (#6439) 2025-12-30 13:57:22 +01:00
GitHub Action d3419229df chore: generate 2025-12-30 12:40:52 +00:00
Connor Adams 6016068f1b docs: global claude skills (#6436) 2025-12-30 06:40:19 -06:00
GitHub Action 51a4d5b8f4 ignore: update download stats 2025-12-30 2025-12-30 12:04:47 +00:00
Github Action b31f5a3434 Update Nix flake.lock and hashes 2025-12-30 11:00:29 +00:00
GitHub Action ebfca02ef4 chore: generate 2025-12-30 11:00:29 +00:00
opencode d2a4a70530 release: v1.0.210
Update Nix Hashes / update (push) Waiting to run
2025-12-30 11:00:28 +00:00
Adam 931f23c917 feat(web): open projects 2025-12-30 04:57:37 -06:00
Adam bebf6f95b9 feat(desktop): readline shortcuts 2025-12-30 04:57:36 -06:00
Adam 0c61e81f29 chore(desktop): disable sourcemap 2025-12-30 04:57:36 -06:00
Adam 95ef0c9187 feat(desktop): more mono (nerd) fonts 2025-12-30 04:57:35 -06:00
Adam 557e518f98 feat(desktop): system notifications 2025-12-30 04:57:35 -06:00
Aiden Cline 1b0fe3af28 ignore: fix bug from variants pr, prevent createEffect issue 2025-12-30 00:14:10 -06:00
Aiden Cline 8ed7323176 tweak: read global claude skills too (#6420) 2025-12-29 23:48:58 -06:00
Joachim Isaksson 2164464699 fix: prevent model list corruption from SolidJS reactivity (#6359)
Co-authored-by: Joachim Isaksson <joachim.isaksson@centiro.com>
2025-12-29 23:45:15 -06:00
Jkker d426c61a45 fix(mdns): use named import for bonjour-service (resolves #6422) (#6423) 2025-12-29 23:29:34 -06:00
GitHub Action 1dc2e74e6e chore: generate 2025-12-30 03:44:29 +00:00
Aiden Cline 66bf9402ce feat: add variants toggle (#6325)
Co-authored-by: Github Action <action@github.com>
2025-12-29 21:43:50 -06:00
Aiden Cline 030eedc2b5 ci: improve changelog script 2025-12-29 21:15:36 -06:00
Eduardo Santos de Brito c216f49971 feat(plugin): expose server URL to plugins (#6373) 2025-12-29 21:05:08 -06:00
opencode-agent[bot] 27c68e203e docs: nix formatter (#6414)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
2025-12-29 20:54:25 -06:00
Aiden Cline 8fb8f594b8 ci: update token var 2025-12-29 18:16:17 -06:00
Github Action de54fcbc88 Update Nix flake.lock and hashes 2025-12-30 00:15:46 +00:00
Sebastian Herrlinger 80003ee44a upgrade opentui to v0.1.66, fixing split diff alignment 2025-12-30 01:14:18 +01:00
Silvio Ney 8c2bcc3b42 fix: Correct theme command in tui.mdx (#6410) 2025-12-29 18:11:07 -06:00
Aiden Cline d2e56f1064 ci: fix env 2025-12-29 17:52:36 -06:00
GitHub Action 22ee8848da chore: generate 2025-12-29 23:47:03 +00:00
Aiden Cline c8d72ef5cc ci: improve err msg 2025-12-29 17:46:29 -06:00
opencode 07d635a8de release: v1.0.209
Update Nix Hashes / update (push) Waiting to run
2025-12-29 23:37:04 +00:00
Adam 1c075f7dfb fix(desktop): error page formatting 2025-12-29 17:33:59 -06:00
Aiden Cline 082b3dbb76 ci: use .env 2025-12-29 17:31:41 -06:00
Adam 5964ae37c3 fix(desktop): better error messages 2025-12-29 17:29:56 -06:00
Aiden Cline bae4c110f2 Revert "feat: read global ~/.claude/skills"
This reverts commit b0d0e55056.
2025-12-29 17:20:04 -06:00
opencode-agent[bot] 5c94839658 docs: add run from source guide (#6405)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-29 17:12:42 -06:00
Aiden Cline b0d0e55056 feat: read global ~/.claude/skills 2025-12-29 17:11:28 -06:00
Aiden Cline b26f6e398b fix: add timeout to filewatcher subscriptions 2025-12-29 16:16:38 -06:00
Aiden Cline b547c721ba tweak: adjust git watcher to ignore files other than HEAD 2025-12-29 16:16:38 -06:00
Ivan Pantic bf05064d4c docs: opencode notificator plugin (fixed link) (#6341)
Co-authored-by: Ivan Pantic <panta@talentkit.io>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-29 16:03:35 -06:00
Dominik Engelhardt e6e3d1ff75 Set smallOptions for google models on openrouter (#6362) 2025-12-29 16:01:31 -06:00
ja be2bdf7a39 feat(highlight): add nix syntax highlighting (#6386) 2025-12-29 15:53:41 -06:00
samcornor eff885f147 fix(webfetch): make format parameter optional with markdown default (#6345)
Co-authored-by: Somair Ansar <somairansar@Somairs-MacBook-Air.local>
2025-12-29 15:53:12 -06:00
ja 188867c21e ci: use env vars for DRY workflow config (#6395) 2025-12-29 15:47:47 -06:00
Shoubhit Dash 4bcba5df0f add supermemory plugin to ecosystem (#6399) 2025-12-29 15:46:22 -06:00
Frank 8a5bad29c2 zen: add gpt 5.1 codex mini 2025-12-29 16:44:11 -05:00
GitHub Action 363f8466ef chore: generate 2025-12-29 21:16:16 +00:00
Graham Bennett e2dba9fbef Support different Nix store path prefixes (#6367) 2025-12-29 15:15:42 -06:00
Cole Leavitt df04c0cda2 fix: filter messages with only step-start parts in toModelMessage (#6383) 2025-12-29 14:58:11 -06:00
ja f544c2f151 feat(format): add nixfmt formatter for Nix files (#6380) 2025-12-29 14:57:52 -06:00
GitHub Action af8096f553 chore: generate 2025-12-29 20:50:47 +00:00
Adam 2486ba1f63 ignore: hide provider connect button until providers loaded 2025-12-29 14:50:06 -06:00
opencode 00bf7ffbe4 release: v1.0.208
Update Nix Hashes / update (push) Waiting to run
2025-12-29 20:44:22 +00:00
Adam 6e71f98169 fix(desktop): don't flash permissions with auto-accept 2025-12-29 14:40:53 -06:00
Adam 3bbbf456fc fix(desktop): scroll sync 2025-12-29 14:36:27 -06:00
Adam 4a2814f868 fix(desktop): more performance/scrolling fixes 2025-12-29 14:23:41 -06:00
Adam b1c7bde448 feat(desktop): auto-accept edits toggle 2025-12-29 14:23:41 -06:00
Adam 1eefc53648 fix(desktop): toast position 2025-12-29 14:23:41 -06:00
Github Action 4cf8b717ed Update Nix flake.lock and hashes 2025-12-29 20:06:20 +00:00
CasualDeveloper db26eb72d6 fix: prevent truncated Claude streams (#6388) 2025-12-29 14:04:53 -06:00
Frank f5a2263048 zen: do not switch provider for models require stick provider 2025-12-29 14:27:51 -05:00
Aiden Cline 901b8c90b0 chore: fix type error 2025-12-29 12:38:35 -06:00
Daniel Polito ba2c0b16d7 Desktop: Image Preview and Dedupe File Upload (#6372) 2025-12-29 11:22:48 -06:00
Brett Heap a46529ebba fix(tui): make auth URLs clickable regardless of line wrapping (#6317)
Co-authored-by: brettheap <brett.heap@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2025-12-29 11:21:09 -06:00
Adam ebc865ac8d fix(desktop): throttle markdown renders 2025-12-29 11:19:40 -06:00
Adam bae4320a7a chore: cleanup 2025-12-29 11:19:40 -06:00
Aiden Cline 8669dba4c9 ci: changelog script update (#6371) 2025-12-29 11:12:25 -06:00
Frainer Encarnación 1d80f9345c fix(lsp): ESLint LSP server fails to auto-install on Windows (#6366) 2025-12-29 11:02:46 -06:00
Adam 8cb939503f fix(desktop): don't show summary when already complete 2025-12-29 10:58:53 -06:00
Adam becab6b053 fix(desktop): markdown lists 2025-12-29 10:47:05 -06:00
Adam 6a5357cb7e fix(desktop): performance/jankiness 2025-12-29 10:42:48 -06:00
Matt Silverlock 10ce23e35b feat: install local plugin dependencies from package.json (#6302)
Co-authored-by: OpenCode <opencode@example.com>
2025-12-29 10:37:41 -06:00
Adam 3fb8ad389a Revert "fix(desktop): jankiness"
This reverts commit d77cc81359.
2025-12-29 09:56:33 -06:00
Github Action 6dc578e772 Update Nix flake.lock and hashes 2025-12-29 15:55:48 +00:00
Adam faf600ef90 feat(desktop): latex support 2025-12-29 09:54:22 -06:00
Adam d77cc81359 fix(desktop): jankiness 2025-12-29 09:47:57 -06:00
Adam b382217c4d fix(desktop): markdown styles 2025-12-29 09:47:57 -06:00
Zeno Jiricek e89317c70f docs: add opencode-skillful plugin to ecosystem page (#6333) 2025-12-29 09:32:44 -06:00
Frank c39e9f8acb zen: minimax m2.1 2025-12-29 10:16:32 -05:00
Adam 4fe7e26728 fix(desktop): one permission at a time 2025-12-29 09:07:36 -06:00
Daniel Polito 40d892390f Desktop: Edit Project (#6360) 2025-12-29 08:54:49 -06:00
Adam 3250ef29bf fix(desktop): more fine-grained state updates for permissions 2025-12-29 08:47:38 -06:00
Frank c1f8edc27c docs: update MiniMax console link in integration instructions 2025-12-29 09:29:01 -05:00
Adam eef5d3fbb9 fix(desktop): more fine-grained state updates 2025-12-29 08:21:32 -06:00
Adam 61538abbb3 fix(core): preserve imperative statements in summary 2025-12-29 07:25:55 -06:00
Adam e7a93e7776 fix(desktop): max height on edit tool calls 2025-12-29 07:03:44 -06:00
Frank db24240e90 docs: add MiniMax integration instructions to providers documentation 2025-12-29 07:45:54 -05:00
GitHub Action 217ccdac97 ignore: update download stats 2025-12-29 2025-12-29 12:05:06 +00:00
Adam c4420604fc fix(desktop): sync last agent and model when changing session 2025-12-29 02:57:28 -06:00
Adam 21b12047aa fix(desktop): context usage alignment 2025-12-29 02:47:51 -06:00
Matt Silverlock 6ed8a14c9b meta: use colors for agents (#5845) 2025-12-28 23:05:17 -06:00
Matt Silverlock d5d54145ce docs: add compaction, watcher, experimental and provider options (#6304)
Co-authored-by: OpenCode <opencode@example.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-28 23:01:43 -06:00
GitHub Action a23036bd79 chore: generate 2025-12-29 04:56:54 +00:00
Alice Alexandra Moore 9ce79f2e34 docs: clarify that MCP tools require glob patterns to disable (#6306)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-28 22:56:22 -06:00
GitHub Action b30d114214 chore: generate 2025-12-29 04:44:48 +00:00
Grégoire Morpain 4178c882ac fix(bedrock): support region and bearer token configuration (#6332)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-28 22:44:15 -06:00
GitHub Action 370b640cb2 chore: generate 2025-12-29 04:39:43 +00:00
Connor Adams a680e39f35 feat: add support for .claude/skills directory (#6252) 2025-12-28 22:39:10 -06:00
opencode 0c9251724a release: v1.0.207
Update Nix Hashes / update (push) Waiting to run
2025-12-29 01:57:53 +00:00
Adam 6163ccc749 chore: cleanup 2025-12-28 19:54:22 -06:00
Adam 431f7627e7 fix(desktop): edit diffs 2025-12-28 19:49:39 -06:00
Adam ff9debd7f1 chore: cleanup theme stuff 2025-12-28 19:27:36 -06:00
Frank 22c825da86 wip: bench 2025-12-28 20:00:49 -05:00
Daniel Polito c184718be5 Desktop: Sync LSP updates (#6305) 2025-12-28 16:07:36 -06:00
Adam 66fb40375a feat(desktop): theme preview 2025-12-28 15:47:05 -06:00
Frank 869c59fcd3 sync 2025-12-28 15:33:18 -05:00
Frank 96aea36950 wip: benchmark 2025-12-28 14:55:05 -05:00
GitHub Action 247f7fcdd9 chore: generate 2025-12-28 19:13:43 +00:00
Matt Silverlock 5a2e8ca0bf docs: add plugin configuration documentation (#6301)
Co-authored-by: OpenCode <opencode@example.com>
2025-12-28 13:13:11 -06:00
Mohak S 5ed462bef3 docs: add opencode-notifier plugin to ecosystem (#6283) 2025-12-28 13:09:38 -06:00
Daniel Polito 9c178cf8ae Include current working directory in local MCP transport (#6303) 2025-12-28 13:09:24 -06:00
GitHub Action 7a1240d23d chore: generate 2025-12-28 19:06:59 +00:00
Didier Durand 6dea0a1002 doc: fix typos in various files (#6294) 2025-12-28 13:06:25 -06:00
Didier Durand 5e2cf0c8e8 chore: activate code coverage in bun test config (#6297) 2025-12-28 19:05:55 +00:00
opencode 42a43dccd6 release: v1.0.206
Update Nix Hashes / update (push) Waiting to run
2025-12-28 19:05:54 +00:00
GitHub Action 4f88dbc865 chore: generate
publish-vscode / publish (push) Waiting to run
Update Nix Hashes / update (push) Waiting to run
2025-12-28 18:54:55 +00:00
Frank a71a056d59 wip: benchmark 2025-12-28 13:54:11 -05:00
opencode ab74630f4c release: v1.0.205
Update Nix Hashes / update (push) Waiting to run
2025-12-28 18:37:58 +00:00
Adam 1d7356d40c chore: cleanup theme stuff 2025-12-28 10:21:32 -06:00
Adam d9312302c0 chore: cleanup theme stuff 2025-12-28 10:14:30 -06:00
Adam bdfb6921aa chore: cleanup 2025-12-28 06:41:59 -06:00
GitHub Action ff57aad4dd ignore: update download stats 2025-12-28 2025-12-28 12:04:31 +00:00
Adam bd53b5e3f0 chore: cleanup 2025-12-28 05:47:22 -06:00
Adam 197e772674 chore: cleanup 2025-12-28 05:47:21 -06:00
Github Action c630a95361 Update Nix flake.lock and hashes 2025-12-28 11:13:54 +00:00
Adam 2435f24db7 feat(desktop): themes 2025-12-28 05:12:36 -06:00
Matt Silverlock 80e87c86ec fix: handle non-text response parts in GitHub action (#6173) 2025-12-27 21:24:10 -06:00
Aiden Cline deaf735a32 tweak: inform agent if no skills are available 2025-12-27 21:20:00 -06:00
Adam 8ea9fda10d chore: permissions ux 2025-12-27 20:40:25 -06:00
Adam 37c4dbfc01 fix(desktop): copy/paste in terminal 2025-12-27 20:18:59 -06:00
Adam 6455ecf739 chore(desktop): cleanup 2025-12-27 19:59:16 -06:00
Adam a0b6d7bd5b fix(desktop): drag file over entire body to attach 2025-12-27 19:49:35 -06:00
Adam c2bcb54ed6 chore(desktop): vertical tabs 2025-12-27 19:43:52 -06:00
rektide a8fda84552 Thinking & tool call visibility settings for /copy and /export (#6243)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-27 19:32:33 -06:00
scarf 90fab5f907 feat: add bash shell completions (#6239) 2025-12-27 19:14:56 -06:00
GitHub Action a126bd3b85 chore: generate 2025-12-28 01:12:02 +00:00
Nindaleth c731f43136 docs: document attach command (#6254)
Co-authored-by: Black_Fox <radekliska@gmail.com>
2025-12-27 19:11:30 -06:00
Connor Adams 054ef6b518 chore: update INVALID_DIRS to include plural 'skills' directory (#6255) 2025-12-27 19:10:51 -06:00
processtrader c63fd2c692 fix: stats command to correctly handle --days 0 for current day statistics (#6259)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2025-12-27 19:10:23 -06:00
Aiden Cline ab185201e1 fix: search all recent models instead of only top 5 in TUI /models command 2025-12-27 19:07:38 -06:00
Ivan Pantic 466d2d748e docs: add opencode-notificator to ecosystem plugins list (#6269)
Co-authored-by: Ivan Pantic <panta@talentkit.io>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-27 18:54:27 -06:00
GitHub Action f54004311f chore: generate 2025-12-28 00:53:48 +00:00
ewired 338a98d33b Allow line numbers and ranges in autocomplete (#4238) 2025-12-27 18:53:17 -06:00
opencode 1dfbb1c92f release: v1.0.204
Update Nix Hashes / update (push) Waiting to run
2025-12-27 20:51:09 +00:00
Adam 48b9951946 feat(desktop): permissions 2025-12-27 14:43:42 -06:00
Adam 104e010ff3 wip(desktop): handle more errors 2025-12-27 14:33:22 -06:00
GitHub Action d37db8903c ignore: update download stats 2025-12-27 2025-12-27 12:04:27 +00:00
Aiden Cline 6f7daa5f0c ci: stale issues 2025-12-27 00:51:05 -06:00
Didier Durand 96f33a0b7a doc: fix typos in various files (#6238) 2025-12-27 00:46:06 -06:00
rari404 899f17f4b6 feat: add path traversal protection to File.read and File.list (#5985) 2025-12-26 23:20:07 -06:00
Christopher Ochsenreither ae86f868ed fix: compact command after revert now properly cleans up revert state (#6235) 2025-12-26 22:57:59 -06:00
ja 1369de23f0 feat: add ability to disable spinner animation (#6084) 2025-12-26 22:12:35 -06:00
Adam c2b0ba42a9 fix(desktop): prompt input fixes, directory and branch in status bar 2025-12-26 20:47:13 -06:00
Dax Raad ba33e03e7b core: fix compaction config checks to properly respect user settings 2025-12-26 19:48:56 -05:00
Dax Raad b2af82a338 core: add compaction config tests to verify auto and prune settings work correctly 2025-12-26 19:44:32 -05:00
GitHub Action d470caf3a9 chore: generate 2025-12-27 00:32:34 +00:00
Dax Raad 431106e54f core: add configurable compaction settings to allow users to disable auto-compaction and pruning via config instead of flags 2025-12-26 19:31:48 -05:00
Frank 334eae7028 zen: add context for login errors 2025-12-26 17:32:39 -05:00
Github Action 51f4ee059d Update Nix flake.lock and hashes 2025-12-26 20:49:06 +00:00
GitHub Action 496911dc05 chore: generate 2025-12-26 20:48:30 +00:00
Adam 003ba776d1 fix(core): mdns global config 2025-12-26 14:47:53 -06:00
Adam c03ddd3046 Reapply "feat(core): optional mdns service (#6192)"
This reverts commit 40ba4cf21e.
2025-12-26 14:47:53 -06:00
Aiden Cline 9dd309a27b chore: kill some unused tools 2025-12-26 14:31:22 -06:00
Aiden Cline 50acb2a11a test: add more tests to make sure that cwd is locked for read tool 2025-12-26 14:30:05 -06:00
Aiden Cline a787f50d1a tweak: bash tool description to avoid unnecessary 'cd &&' usage 2025-12-26 13:44:52 -06:00
Matt Silverlock 2f9558102f github: support issues and workflow_dispatch events (#6157) 2025-12-26 13:34:03 -06:00
Aiden Cline f8b64d45e6 ci: re-enable sync zed 2025-12-26 12:24:14 -06:00
Aiden Cline 79290c70b6 ci: fix token for gh 2025-12-26 12:22:56 -06:00
Aiden Cline 07e8417b30 ci: fix var 2025-12-26 12:08:16 -06:00
Aiden Cline d7bdb55d01 ci: update zed extension sync 2025-12-26 12:06:36 -06:00
Github Action b19df79455 Update Nix flake.lock and hashes 2025-12-26 17:45:58 +00:00
GitHub Action 8498aa9bb2 chore: generate 2025-12-26 17:45:03 +00:00
Aiden Cline 40ba4cf21e Revert "feat(core): optional mdns service (#6192)"
This reverts commit 6122c0b8a9.
2025-12-26 11:43:52 -06:00
Aiden Cline e115bcd5e0 chore: rm comments 2025-12-26 11:43:13 -06:00
Aiden Cline 3b07898a1c fix: adjust upgrade command to use gh releases page if not npm/bun/pnpm install method 2025-12-26 11:43:12 -06:00
Rohan Godha 725cf7745c fix: opencode web baseURL error (#6181) 2025-12-26 11:36:31 -06:00
Ariane Emory 429644ffdb chore: kill the dead Polaris Alpha code (#6193) 2025-12-26 11:32:31 -06:00
ja 1b2e793ada feat: add shfmt formatter for shell scripts (#6204) 2025-12-26 11:31:51 -06:00
Aiden Cline bf68afa2bc tweak: make install script handle 404s better 2025-12-26 11:28:18 -06:00
Github Action ef56bcf4a5 Update Nix flake.lock and hashes 2025-12-26 17:09:54 +00:00
GitHub Action cc800f2790 chore: generate 2025-12-26 17:09:31 +00:00
Ayush Walekar 5706a76e6b chore: createOpencodeServer expose logLevel (#6202) 2025-12-26 11:09:06 -06:00
Roberto Carvajal 2f97aa93d7 fix(dep): Update package.json - fix perplexity provider version (#6199)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-26 11:08:45 -06:00
Daniel Polito 4cc60831f6 Desktop: MCP UI (#6162)
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
2025-12-26 10:49:05 -06:00
GitHub Action 4f8e1a94ce chore: generate 2025-12-26 16:30:20 +00:00
Adam 6122c0b8a9 feat(core): optional mdns service (#6192)
Co-authored-by: Github Action <action@github.com>
2025-12-26 10:29:48 -06:00
GitHub Action 54aa239efd chore: generate 2025-12-26 16:22:05 +00:00
Didier Durand 3897f7e7a7 doc: fix typos in various files (#6196) 2025-12-26 10:21:33 -06:00
GitHub Action 5cdf052330 ignore: update download stats 2025-12-26 2025-12-26 12:10:30 +00:00
Aiden Cline 1520298db1 tweak: update transform for gemini models so that topP and topK match gemini-cli values 2025-12-25 22:46:12 -06:00
GitHub Action 1ae077e89f chore: generate 2025-12-26 04:25:19 +00:00
JackNorris 4f84bd2c07 fix: only show diagnostics block when errors exist (#6175) 2025-12-25 22:24:48 -06:00
opencode b06c2b146c release: v1.0.203
Update Nix Hashes / update (push) Waiting to run
2025-12-26 04:10:11 +00:00
Dax Raad 59cf9dacdc prompt update to prevent searching via bash tool 2025-12-25 23:07:39 -05:00
Donghyun Shin 50dd82ff78 fix(lsp): make JDTLS use the correct config directory on Windows (#6121) 2025-12-25 21:17:54 -06:00
GitHub Action 404b3889ee chore: generate 2025-12-26 03:05:15 +00:00
Marco 744ea49659 feat: haskell lsp support (#6141) 2025-12-25 21:04:43 -06:00
opencode 9fcd67d03b release: v1.0.202
Update Nix Hashes / update (push) Waiting to run
2025-12-26 02:11:47 +00:00
Adam 1e8df0df53 chore: brain icon 2025-12-25 20:06:41 -06:00
Adam 66d328c838 fix(desktop): user message display 2025-12-25 19:45:20 -06:00
Adam 86884a0952 fix(desktop): padding 2025-12-25 19:22:16 -06:00
Adam 3093fea653 fix(desktop): move session context to top-right 2025-12-25 19:21:04 -06:00
Adam fe6602c8a3 fix(desktop): missing keybinds in tooltips 2025-12-25 19:07:42 -06:00
Adam 49db5cb1f4 fix(desktop): markdown rendering perf 2025-12-25 19:07:42 -06:00
GitHub Action ef362f853a chore: generate 2025-12-26 01:03:22 +00:00
Adam 3dec4db825 fix(desktop): can't collapse project with active session 2025-12-25 19:02:43 -06:00
Adam ec96541f49 chore(ui): radio group primitive 2025-12-25 18:46:57 -06:00
Adam 5381071f5f feat(desktop): better indicator that session is busy 2025-12-25 14:31:10 -06:00
opencode e8d23093d5 release: v1.0.201
Update Nix Hashes / update (push) Waiting to run
2025-12-25 14:07:19 +00:00
Adam dd280dc21f fix(desktop): so many prompt input fixes, merry christmas 2025-12-25 08:04:42 -06:00
GitHub Action 9ef8b1f916 ignore: update download stats 2025-12-25 2025-12-25 12:04:54 +00:00
GitHub Action 125ffdb6e9 chore: generate 2025-12-25 05:59:27 +00:00
Dax Raad 08e6874ed9 remove list tool 2025-12-25 00:58:47 -05:00
opencode 80e0727e86 release: v1.0.200
Update Nix Hashes / update (push) Waiting to run
2025-12-25 04:39:32 +00:00
Adam d835fb67ec fix(desktop): scroll jank in session turn and review 2025-12-24 22:36:45 -06:00
GitHub Action d28efe1db1 chore: generate 2025-12-25 04:17:39 +00:00
Adam 0b27ff4165 fix(desktop): override agent model 2025-12-24 22:16:58 -06:00
Adam cf79dca276 fix(desktop): reconcile session diff updates 2025-12-24 22:16:44 -06:00
opencode 87f934ef06 release: v1.0.199
Update Nix Hashes / update (push) Waiting to run
2025-12-25 02:40:19 +00:00
Adam fdcaa0c351 chore: toast on file load error 2025-12-24 20:37:12 -06:00
Adam a2004ae2a8 chore: cleanup dead code 2025-12-24 20:30:52 -06:00
Adam d903069f9a chore: show version on error page 2025-12-24 20:25:36 -06:00
Adam 1516267709 fix(desktop): show server connection failure 2025-12-24 20:11:37 -06:00
Ahmed Mansour 11808a3cb6 fix: correct Content-Type headers for static assets on app.opencode.ai (#6113) 2025-12-24 20:00:22 -06:00
Connor Adams 169d9ca7d5 docs: update skills to use canonical ~/.config/opencode location (#6132) 2025-12-24 18:18:33 -06:00
Robb Tolliver f2d5f7c71f docs: Corrected the number of built-in subagents in documentation (#6133) 2025-12-24 18:02:35 -06:00
GitHub Action 3149c74f4b chore: generate 2025-12-24 19:21:10 +00:00
Dax Raad c35cfeace0 tui: disable tips display in home route 2025-12-24 14:20:29 -05:00
Dax Raad aa6c65db0c CI 2025-12-24 18:16:46 +00:00
opencode b69d2934f7 release: v1.0.198
Update Nix Hashes / update (push) Waiting to run
2025-12-24 18:16:46 +00:00
Dax Raad 247890df1b ci 2025-12-24 13:12:29 -05:00
GitHub Action b37283d531 chore: generate 2025-12-24 18:08:42 +00:00
Dax Raad 1793cd568a ci 2025-12-24 13:07:56 -05:00
opencode 7f489c3600 release: v1.0.197
Update Nix Hashes / update (push) Waiting to run
2025-12-24 17:47:07 +00:00
Dax Raad 8b2a7753d8 Revert "feat: better styling for small screens (short and/or not wide) (#5968)"
This reverts commit 50fab0b838.
2025-12-24 12:38:10 -05:00
GitHub Action 7494bcfab8 chore: generate 2025-12-24 17:07:43 +00:00
Patrick Schiel 57e842f669 docs: add infos about server debugging (#6085) 2025-12-24 11:07:12 -06:00
opencode 2e1d8ebfbd release: v1.0.196
Update Nix Hashes / update (push) Waiting to run
2025-12-24 16:40:16 +00:00
Adam 2832871c97 fix(desktop): last text part streaming 2025-12-24 10:35:52 -06:00
Adam bcd6f51467 fix(desktop): render perf 2025-12-24 10:26:49 -06:00
Jay V 19a85df8ee docs: edits 2025-12-24 11:23:51 -05:00
Jay V 4a8b052153 docs: add comprehensive CLI command documentation for agent, mcp, session, stats, and web commands 2025-12-24 11:12:09 -05:00
Jay V 9ee1b3fd41 docs: make MCP server documentation more scannable and add Sentry example 2025-12-24 10:49:48 -05:00
Adam d6d0fd4838 chore: cleanup 2025-12-24 09:07:31 -06:00
Adam 77347fe27d fix(desktop): summary flicker 2025-12-24 09:07:31 -06:00
Adam 5c76443d45 feat(desktop): show read tool args 2025-12-24 09:07:31 -06:00
GitHub Action e6980f8f02 chore: generate 2025-12-24 14:56:15 +00:00
Adam 650c116ac2 fix(desktop): better session navigation, hide child sessions 2025-12-24 08:55:32 -06:00
opencode 8c48a674b2 release: v1.0.195
Update Nix Hashes / update (push) Waiting to run
2025-12-24 14:50:40 +00:00
Adam e6ac65f165 chore: cleanup 2025-12-24 08:46:11 -06:00
GitHub Action fc47d38be0 chore: generate 2025-12-24 14:38:25 +00:00
Adam 1ba2230e65 refactor(ui): rewrite createAutoScroll with robust event tracking to fix sticky behavior 2025-12-24 08:37:49 -06:00
opencode b5629759a3 release: v1.0.194
Update Nix Hashes / update (push) Waiting to run
2025-12-24 12:20:02 +00:00
Adam b87e68159d chore: cleanup 2025-12-24 06:16:17 -06:00
Adam de85ffd99d fix(desktop): session sort when multiple active 2025-12-24 06:16:17 -06:00
Adam 4f2b06a7a9 fix(share): page title should be session title 2025-12-24 06:16:17 -06:00
GitHub Action 2ab7283007 ignore: update download stats 2025-12-24 2025-12-24 12:04:46 +00:00
Adam 6299d5884f fix(desktop): exclude deprecated models 2025-12-24 06:01:27 -06:00
Adam 3f2972e330 fix(desktop): auto-scroll 2025-12-24 05:57:48 -06:00
Adam 40c7bc7406 fix: don't disable text selection 2025-12-24 05:57:48 -06:00
Github Action a902586613 Update Nix flake.lock and hashes 2025-12-24 11:56:43 +00:00
Adam 20a0ec40c0 deps: update marked and marked-shiki 2025-12-24 05:55:28 -06:00
Adam 896892a222 fix(desktop): hang on backtracing-prone regex 2025-12-24 05:49:35 -06:00
Adam a5b74fbd9b fix(desktop): conditionally show review pane toggle 2025-12-24 05:22:25 -06:00
Ryan Vogel 4af7f34d24 fix: remove SVG favicon to improve SEO (#5755) 2025-12-24 05:17:13 -06:00
Aiden Cline 8be600c0f6 docs: tweak lsp.mdx 2025-12-23 22:38:17 -06:00
opencode-agent[bot] 928ca98e87 docs: experimental LSP tool (#5943)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-23 22:37:49 -06:00
opencode-agent[bot] 4d0e560e48 docs: skill tool/perm + parent keybind (#6001)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-23 22:25:55 -06:00
xiantang a54cfd9428 docs: add Neovim to the list of editors (#6081) 2025-12-23 22:17:34 -06:00
Aiden Cline 6d33a1e788 tweak: retry err 2025-12-23 22:10:28 -06:00
GitHub Action 43a9905f37 chore: generate 2025-12-24 01:37:35 +00:00
Frank 35b4304006 zen: sync 2025-12-23 20:36:55 -05:00
Abdelkader Boudih e38d0ad4ec feat(mcp): handle tools/list_changed notifications (#5913)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-23 19:36:37 -06:00
Aiden Cline ad8091b327 test: add test for retry 2025-12-23 19:34:40 -06:00
Aiden Cline 3d1f43dccb chore: regen sdk 2025-12-23 19:34:38 -06:00
GitHub Action f499a76eb4 chore: generate 2025-12-24 01:21:10 +00:00
Aiden Cline 177e36af60 make 'The socket connection was closed unexpectedly' errors retryable 2025-12-23 19:20:31 -06:00
Rohan Mukherjee 833ce3e48c chore: update AGENTS.md to ~150 lines (#5955) 2025-12-23 19:04:44 -06:00
David Hill bedd66b9b2 style: update current todo style (#6077) 2025-12-23 18:57:02 -06:00
rari404 81eabb5099 feat(tui): console copy-to-clipboard via opentui (#5658)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-23 18:46:01 -06:00
OpeOginni 65d1aedcd7 fix: resize textarea when pasting prompt less than 150 chars (#6070) 2025-12-23 18:11:48 -06:00
Matt Silverlock 2837698940 providers: add Cloudflare AI Gateway (#5174) 2025-12-23 17:31:58 -06:00
Frank 3a879c97a2 ci: adam is not a full stack engineer 2025-12-23 17:03:00 -05:00
Adam 150d0fbcb1 fix: remove desktop dup 2025-12-23 15:31:18 -06:00
Viktor Nagy 5eed2687b6 Update gitlab.mdx to use the 2.x component version (#6062) 2025-12-23 14:56:46 -06:00
Github Action 04c918f7a3 Update Nix flake.lock and hashes 2025-12-23 20:42:17 +00:00
Aiden Cline 2279689a0f Revert "Add animated braille spinner to terminal title when agent is running (#5984)"
This reverts commit 0983e242e5.
2025-12-23 14:40:55 -06:00
Aiden Cline 40685baf7f tweak: update import & pr commands to use new share link ur 2025-12-23 14:04:33 -06:00
GitHub Action aed3c3f1d5 chore: generate 2025-12-23 19:48:40 +00:00
Jon Redeker 4e4cd8d537 Add opencode-shell-strategy plugin to ecosystem (#5995) 2025-12-23 13:48:08 -06:00
ja c43ee72fc4 feat(install): add standard CLI flags (--help, --version, --no-modify-path) (#5885) 2025-12-23 13:47:32 -06:00
David Hill 0983e242e5 Add animated braille spinner to terminal title when agent is running (#5984)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
Co-authored-by: Github Action <action@github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-23 13:38:10 -06:00
GitHub Action 0551130201 chore: generate 2025-12-23 19:29:05 +00:00
David Hill 964f513e69 "Did you know?" start screen tips (#5982)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-23 13:28:33 -06:00
Daniel Gray 8638125b82 fix: favorites and recents stay visible when filtering models (#6053) 2025-12-23 12:55:47 -06:00
Matt Silverlock e69a899b4f docs: add MCP OAuth debugging section (#6047) 2025-12-23 12:46:59 -06:00
Aiden Cline 37133f3afc ci: update zed sync script 2025-12-23 12:45:34 -06:00
ja 21693e4909 fix(tui): prevent keybinds from executing when dialog is open (#6017) 2025-12-23 12:37:28 -06:00
Frank 00566f4c6c ci: fix 2025-12-23 12:58:28 -05:00
GitHub Action 9fcdd5533d chore: generate 2025-12-23 15:58:33 +00:00
Rhys Sullivan 850eee623c [feat]: prompt stashing (#6021) 2025-12-23 09:58:00 -06:00
GitHub Action 309dd08504 chore: generate 2025-12-23 15:39:47 +00:00
Daniel Polito 7d908ae55e Fix Github Pull Request Event (#6037) 2025-12-23 09:39:14 -06:00
opencode cb59f7c782 release: v1.0.193
Update Nix Hashes / update (push) Waiting to run
2025-12-23 15:16:20 +00:00
GitHub Action 86e9ba5a23 chore: generate 2025-12-23 14:45:27 +00:00
Sebastian Herrlinger beba422835 indent wrapped todo items properly 2025-12-23 15:44:45 +01:00
opencode dc406cc3da release: v1.0.192
Update Nix Hashes / update (push) Waiting to run
2025-12-23 14:43:07 +00:00
Frank e6ad123cf5 zen: glm 4.7 2025-12-23 07:56:37 -05:00
Frank e0b85656ab ci: fix 2025-12-23 07:53:33 -05:00
Frank f1c9a2a4ca ci: fix 2025-12-23 07:38:42 -05:00
GitHub Action 464112baf6 ignore: update download stats 2025-12-23 2025-12-23 12:04:42 +00:00
Sebastian Herrlinger 3af7592bfe no intermediate autocomplete result to avoid flickering 2025-12-23 12:22:34 +01:00
GitHub Action 6732c86534 chore: generate 2025-12-23 10:15:49 +00:00
Brendan Allan 5f243a52e1 desktop: kill_sidecar before update install on windows 2025-12-23 18:14:44 +08:00
Github Action 59c16a52c0 Update Nix flake.lock and hashes 2025-12-23 10:12:37 +00:00
GitHub Action 99b20f123e chore: generate 2025-12-23 10:11:53 +00:00
Adam ed5fd65bfd deps: diffs, shiki updates 2025-12-23 04:08:42 -06:00
opencode 81785cc9fe release: v1.0.191
Update Nix Hashes / update (push) Waiting to run
2025-12-23 05:57:23 +00:00
GitHub Action 15588dfe82 chore: generate 2025-12-23 05:53:02 +00:00
Brendan Allan 77d3bfe172 console: add AppImage download link 2025-12-23 13:52:23 +08:00
GitHub Action 3b207ad9cf chore: generate 2025-12-23 04:28:11 +00:00
Matt Silverlock 7c75e29858 improve mcp CLI + ability to debug MCP oauth (#5980) 2025-12-22 22:27:38 -06:00
Aiden Cline 8799efa2ad ci: docs sync 2025-12-22 22:27:21 -06:00
opencode-agent[bot] 746a09170e docs: new /global/health API (#6006)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
2025-12-22 22:26:47 -06:00
lif a07dfc8d2a fix: handle Windows CRLF line endings in grep tool (#5948)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-22 22:26:15 -06:00
Joel Hooks afd3927fa2 feat(plugin): allow compaction hook to replace prompt entirely (#5907) 2025-12-22 22:19:14 -06:00
Brendan Allan 781d2d4805 ci: rename tauri -> desktop 2025-12-23 11:15:19 +08:00
Github Action 8a1d6edc6a Update Nix flake.lock and hashes 2025-12-23 02:03:30 +00:00
Adam 108dc97654 chore: rename packages/tauri -> packages/desktop 2025-12-22 20:01:25 -06:00
Github Action 68c106a69a Update Nix flake.lock and hashes 2025-12-23 01:40:34 +00:00
Adam 4e46d511d3 chore: rename packages/desktop -> packages/app 2025-12-22 19:39:00 -06:00
GitHub Action e40433957b chore: generate 2025-12-23 01:17:33 +00:00
Adam d23078d173 fix(desktop): better error messages on connection failure 2025-12-22 19:16:54 -06:00
Mohammad Alhashemi 823f122214 feat(skill): add per-agent filtering to skill tool description (#6000) 2025-12-22 20:14:33 -05:00
Frank b31f178b7c zen: glm 4.7 2025-12-22 19:36:07 -05:00
Aiden Cline 50fab0b838 feat: better styling for small screens (short and/or not wide) (#5968) 2025-12-22 18:00:26 -06:00
GitHub Action a3847e5aed chore: generate 2025-12-22 23:40:52 +00:00
Dax Raad d441c263f4 tui: change task tool container to block layout for better subagent session display 2025-12-22 18:40:15 -05:00
opencode 89bf6efbb7 release: v1.0.190
Update Nix Hashes / update (push) Waiting to run
2025-12-22 23:31:20 +00:00
GitHub Action 4e1a2356ec chore: generate 2025-12-22 23:24:39 +00:00
Mohammad Alhashemi 43b1501f5a feat: add native skill tool with permission system (#5930)
Co-authored-by: Dax Raad <d@ironbay.co>
2025-12-22 18:24:06 -05:00
opencode 8fc4921f4c release: v1.0.189
Update Nix Hashes / update (push) Waiting to run
2025-12-22 23:15:23 +00:00
GitHub Action 2bb0a58fad chore: generate 2025-12-22 22:47:22 +00:00
Jay V 2727ad9e4e docs: edit gitlab 2025-12-22 22:47:22 +00:00
opencode ccf0e65cb0 release: v1.0.188
Update Nix Hashes / update (push) Waiting to run
2025-12-22 22:47:21 +00:00
Github Action 911ff4131f Update Nix flake.lock and hashes 2025-12-22 22:37:48 +00:00
Frank e79f9571eb remove sharp 2025-12-22 17:36:23 -05:00
Josh Thomas 135c7944f4 fix(tui): resize textarea if text inserted via appendPrompt TUI API (#5983) 2025-12-22 16:29:18 -06:00
Frank 8ea689dade zen: add glm 4.7 2025-12-22 17:23:33 -05:00
Aiden Cline aae44afe04 ignore: agents.md 2025-12-22 16:21:56 -06:00
Adam ab2242565e feat(desktop): review pane toggle 2025-12-22 16:20:17 -06:00
Viktor Nagy 9e51d9fa64 Add gitlab-opencode to GitLab docs
The current GitLab page describes OpenCode integration through GitLab Duo.

GitLab Duo is a paying functionality and is limited to workflows supported by GitLab.

GitLab-OpenCode is a community project that offers more flexiblity, better customization and easier setup to use OpenCode in GitLab. On the downside, it does not have the level of integration into GitLab as Duo does.
2025-12-22 17:14:59 -05:00
Frank 8499af06df support glm 4.7 2025-12-22 17:11:02 -05:00
GitHub Action 5f562df0b0 chore: generate 2025-12-22 22:01:27 +00:00
Jay V c7742c2f77 ignore: update GitHub stars to 41K and project stats to reflect current growth 2025-12-22 17:00:27 -05:00
opencode 4ccaa05659 release: v1.0.187
Update Nix Hashes / update (push) Waiting to run
2025-12-22 21:58:41 +00:00
Aiden Cline 827779fd25 ci: add failure case for changelog 2025-12-22 15:53:41 -06:00
Aiden Cline a45deddba4 test: rm claude skills test 2025-12-22 15:45:31 -06:00
Aiden Cline 143ff2b43a fix: disable claude skill loading for now 2025-12-22 15:40:08 -06:00
Shpetim 289e341aa5 fix: stop auto execute on sendText vscode extension (#5994)
Co-authored-by: Shpetim <shpetim.alimi@ndbit.net>
2025-12-22 15:38:54 -06:00
Jon Redeker f088140fb6 docs: add opencode-morph-fast-apply plugin to ecosystem (#5992) 2025-12-22 15:21:14 -06:00
Blake North 9b533cae87 fix(providers.opencode): check config for api key in addition to auth (#5906) 2025-12-22 15:20:40 -06:00
Github Action 665d5ecf3b Update Nix flake.lock and hashes 2025-12-22 21:13:50 +00:00
Aiden Cline 6373595540 fix: bundle more providers to fix breaking ai sdk issue 2025-12-22 15:12:18 -06:00
Rohan Godha cec7c8ba80 feat(tui): go to parent keybind for subagents (#5762) 2025-12-22 14:50:45 -06:00
GitHub Action 3c4cb11a8d chore: generate 2025-12-22 20:34:21 +00:00
wienans bec1b3cdfe Add OpenChamber to ecosystem documentation (#5978) 2025-12-22 14:33:45 -06:00
ja 76c7779b96 feat(lsp): add Tinymist LSP support for Typst (#5933) 2025-12-22 14:31:47 -06:00
Github Action 0d761abd16 Update Nix flake.lock and hashes 2025-12-22 20:27:29 +00:00
Sebastian Herrlinger 1cebb1273d upgrade opentui to v0.1.63, enabling kitty alternate keys by default 2025-12-22 21:26:03 +01:00
Tim Kleinschmidt bace3b9e6d support clojure projects with built-in lsp (#5975) 2025-12-22 14:20:15 -06:00
Shpetim 74aa3116c9 [FEATURE]: Show context usage in OpenCode Desktop Context usage (#5979) 2025-12-22 13:56:36 -06:00
Adam ce02e5df04 fix(prompt): better summary prompt 2025-12-22 13:09:12 -06:00
Daniel Polito f2f8433bfe docs: Github Auto Pull Request Docs (#5974) 2025-12-22 11:53:58 -06:00
Adam 018065e576 chore(desktop): auto scroll utility 2025-12-22 11:27:27 -06:00
GitHub Action e16abc700a chore: generate 2025-12-22 17:06:35 +00:00
Will Marella b525d6fb2c Add keybindable commands to navigate between user messages (#5078)
Co-authored-by: Will@Cambridge <willcambridge@MacBook-Pro-59.local>
Co-authored-by: Will@Cambridge <willcambridge@macbookpro.mynetworksettings.com>
2025-12-22 11:06:00 -06:00
Aiden Cline 14c7bc2f17 ci: tweak docs prompt 2025-12-22 11:00:32 -06:00
Daniel Polito 2e80e755b6 GitHub pull request event (#5335) 2025-12-22 10:59:02 -06:00
Adam a3a839d9a5 fix(share): expanded state and responsiveness 2025-12-22 10:13:57 -06:00
Aiden Cline 0e2e2f8b24 ci: limit to opencode repo 2025-12-22 09:56:51 -06:00
Lekë Dobruna b7f524718f fix: duplicate words in dialog options (#5944) 2025-12-22 09:56:32 -06:00
Adam fc3f902a04 fix(desktop): diff readability (colors) 2025-12-22 09:51:21 -06:00
Dax Raad ac05a7d50c fix url for web 2025-12-22 10:45:51 -05:00
GitHub Action 99fb473c50 chore: generate 2025-12-22 14:38:20 +00:00
Buck Evan 3d9414838e fix(read): narrow .env file blocking to not block .envrc (#5654)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-22 08:37:43 -06:00
opencode 5d7b015661 release: v1.0.186
Update Nix Hashes / update (push) Waiting to run
2025-12-22 12:14:06 +00:00
Adam e5d154fc70 Revert "server: ensure frontend has correct port for PTY websocket connections (#5898)"
This reverts commit aa8cae4d59.
2025-12-22 06:05:46 -06:00
Adam c9f05cfd3a Revert "fix: server"
This reverts commit 2cc86bbe51.
2025-12-22 06:05:46 -06:00
GitHub Action 98d98c6a52 ignore: update download stats 2025-12-22 2025-12-22 12:05:15 +00:00
Adam 2cc86bbe51 fix: server 2025-12-22 06:02:16 -06:00
Ashutosh Kumar aa8cae4d59 server: ensure frontend has correct port for PTY websocket connections (#5898) 2025-12-22 05:56:23 -06:00
Adam 777dca775d Revert "fix: use current page port instead of hardcoded 4096 (#5949)"
This reverts commit 54997572da.
2025-12-22 05:55:15 -06:00
Adam 1bb63f6dd0 fix(desktop): cleanup auto scroll 2025-12-22 05:46:07 -06:00
Adam b0685f34cc feat(desktop): mobile responsiveness 2025-12-22 05:46:07 -06:00
Adam d4e121913e fix(desktop): filter child sessions from header 2025-12-22 05:46:07 -06:00
Adam e4bd10af4f feat(desktop): better task tool rendering 2025-12-22 05:46:06 -06:00
lif 54997572da fix: use current page port instead of hardcoded 4096 (#5949)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-22 05:45:08 -06:00
Aaron Iker 4285590b7e feat: polish dialog & list styles for the desktop app, add fixed logos from models.dev (#5925) 2025-12-22 05:41:38 -06:00
Brendan Allan ff86ebe2d5 ci: fix tauri build args 2025-12-22 18:55:01 +08:00
Brendan Allan da1ac70eda ci: verbose build and re-enable appimage 2025-12-22 18:48:58 +08:00
GitHub Action 35e46ecce2 chore: generate 2025-12-22 10:06:29 +00:00
Brendan Allan b226ab6a69 ci: run prepare step for tauri build 2025-12-22 18:05:52 +08:00
Brendan Allan 4e30508ef0 ci: try downloading artifact in desktop prepare 2025-12-22 17:45:57 +08:00
NN708 5120584a1c feat(desktop): arm64 build for linux (#5935) 2025-12-22 03:45:30 -06:00
Brendan Allan 98954f65b0 ci: replace with just upload-artifact whole dir 2025-12-22 17:17:17 +08:00
Brendan Allan dcb96deef1 ci: import bun shell 2025-12-22 17:10:43 +08:00
Brendan Allan eaeefa4c48 try uploading artifacts in workflow 2025-12-22 17:08:44 +08:00
Brendan Allan 477df5f85d remove actions artifact uploading 2025-12-22 17:01:52 +08:00
Github Action a34c081d64 Update Nix flake.lock and hashes 2025-12-22 08:58:57 +00:00
GitHub Action a1c1b4ab4a chore: generate 2025-12-22 08:58:16 +00:00
Brendan Allan 15cf85f47a ci: try to upload cli artifacts 2025-12-22 16:57:28 +08:00
Aiden Cline 5eadeedc7b feat: add experimental lsp tool (#5886) 2025-12-22 00:34:21 -06:00
Luo Chen ad4f43b17e feat: add nixd as lsp for nix language (#5929) 2025-12-22 00:30:08 -06:00
opencode-agent[bot] 6b2eee03d1 docs: Agent Skills (#5931)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-21 23:49:28 -06:00
Valerio Di Maggio da468f33e2 fix: ensure installation commands are using .quiet (#5758)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-21 23:44:25 -06:00
Ben Vargas 21057913f4 docs: add ai-sdk-provider-opencode-sdk to ecosystem (#5772) 2025-12-21 22:59:36 -06:00
Aiden Cline 3e49fe218d ci: update docs prompt 2025-12-21 22:32:04 -06:00
Aiden Cline ee468db795 ci: add automatic doc update workflow 2025-12-21 21:52:44 -06:00
Neil Daquioag b7a01f8d00 fix: support clipboard image paste (Ctrl+V) on Windows (#5919) 2025-12-21 21:18:47 -06:00
Aiden Cline b266a1bd89 tweak: adjust minimax m2 topK and add minimax m2.1 topP 2025-12-21 21:17:58 -06:00
Noam Bressler 961a2ee729 fix: Perform snapshot in cases finish-step is not reached (#5912)
Co-authored-by: noamzbr <noamzbr@users.noreply.github.com>
2025-12-21 21:13:11 -06:00
Matt Silverlock dd14cf0c12 prompts: improve built-in /review prompt (#5918) 2025-12-21 21:11:48 -06:00
YeonGyu-Kim 64c59387cc feat(server): expose auto param in session.summarize for plugins (#5924) 2025-12-21 21:05:30 -06:00
GitHub Action 4397a52ac3 chore: generate 2025-12-22 00:45:30 +00:00
Dax fbd1576c46 feat: add Agent Skills support (#5921) 2025-12-21 19:44:56 -05:00
opencode 2073b65193 release: v1.0.185
Update Nix Hashes / update (push) Waiting to run
2025-12-21 23:38:40 +00:00
Dax Raad b7d48cec64 core: fix LSP server binary installation and shell command execution
- Ensure proper file permissions are set for installed LSP binaries on non-Windows platforms
- Add error handling for shell command execution in prompt system to prevent crashes
2025-12-21 18:33:37 -05:00
Aiden Cline e200e788d1 tweak: update kimi-k2 and kimi-k2-thinking to use recommended temperature values 2025-12-21 15:54:57 -06:00
Dax Raad 724ad46bc7 tui: fix SDK context usage and server port fallback
- Update SDK context to return client instead of event for proper usage
- Add server port fallback to 4096 when port 0 is specified but unavailable
- Fix SDK event listener usage in TUI app
2025-12-21 14:57:55 -05:00
Github Action 54408065e5 Update Nix flake.lock and hashes 2025-12-21 17:46:30 +00:00
GitHub Action 59372786a9 chore: generate 2025-12-21 17:45:51 +00:00
Sherlock Holmes 6cdcad713f fix(deps): add missing @opencode-ai/plugin to dependencies (#5797) 2025-12-21 11:45:20 -06:00
Nalin Singh 5c53b0c772 feat: add syntax highlighting for .ets files (#5889) 2025-12-21 11:42:47 -06:00
Adam Hosker 372441028f fix: prevent stats workflow from running on forks (#5897) 2025-12-21 11:32:00 -06:00
Abdelkader Boudih 6815930eef fix: use official MCP SDK for better tool schema handling (#5463) 2025-12-21 11:31:07 -06:00
GitHub Action db293f9d96 chore: generate 2025-12-21 14:47:27 +00:00
858 changed files with 98845 additions and 15271 deletions
+4
View File
@@ -0,0 +1,4 @@
# web + desktop packages
packages/app/ @adamdotdevin
packages/tauri/ @adamdotdevin
packages/desktop/ @adamdotdevin
+8
View File
@@ -11,6 +11,14 @@ body:
validations:
required: true
- type: input
id: plugins
attributes:
label: Plugins
description: What plugins are you using?
validations:
required: false
- type: input
id: opencode-version
attributes:
+3
View File
@@ -0,0 +1,3 @@
### What does this PR do?
### How did you verify your code works?
+72
View File
@@ -0,0 +1,72 @@
name: Docs Update
on:
schedule:
- cron: "0 */12 * * *"
workflow_dispatch:
env:
LOOKBACK_HOURS: 4
jobs:
update-docs:
if: github.repository == 'sst/opencode'
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
id-token: write
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch full history to access commits
- name: Setup Bun
uses: ./.github/actions/setup-bun
- name: Get recent commits
id: commits
run: |
COMMITS=$(git log --since="${{ env.LOOKBACK_HOURS }} hours ago" --pretty=format:"- %h %s" 2>/dev/null || echo "")
if [ -z "$COMMITS" ]; then
echo "No commits in the last ${{ env.LOOKBACK_HOURS }} hours"
echo "has_commits=false" >> $GITHUB_OUTPUT
else
echo "has_commits=true" >> $GITHUB_OUTPUT
{
echo "list<<EOF"
echo "$COMMITS"
echo "EOF"
} >> $GITHUB_OUTPUT
fi
- name: Run opencode
if: steps.commits.outputs.has_commits == 'true'
uses: sst/opencode/github@latest
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
with:
model: opencode/gpt-5.2
agent: docs
prompt: |
Review the following commits from the last ${{ env.LOOKBACK_HOURS }} hours and identify any new features that may need documentation.
<recent_commits>
${{ steps.commits.outputs.list }}
</recent_commits>
Steps:
1. For each commit that looks like a new feature or significant change:
- Read the changed files to understand what was added
- Check if the feature is already documented in packages/web/src/content/docs/*
2. If you find undocumented features:
- Update the relevant documentation files in packages/web/src/content/docs/*
- Follow the existing documentation style and structure
- Make sure to document the feature clearly with examples where appropriate
3. If all new features are already documented, report that no updates are needed
4. If you are creating a new documentation file be sure to update packages/web/astro.config.mjs too.
Focus on user-facing features and API changes. Skip internal refactors, bug fixes, and test updates unless they affect user-facing behavior.
Don't feel the need to document every little thing. It is perfectly okay to make 0 changes at all.
Try to keep documentation only for large features or changes that already have a good spot to be documented.
+2 -2
View File
@@ -28,8 +28,8 @@ jobs:
OPENCODE_PERMISSION: |
{
"bash": {
"gh issue*": "allow",
"*": "deny"
"*": "deny",
"gh issue*": "allow"
},
"webfetch": "deny"
}
+65
View File
@@ -0,0 +1,65 @@
name: Duplicate PR Check
on:
pull_request_target:
types: [opened]
jobs:
check-duplicates:
if: |
github.event.pull_request.user.login != 'actions-user' &&
github.event.pull_request.user.login != 'opencode' &&
github.event.pull_request.user.login != 'rekram1-node' &&
github.event.pull_request.user.login != 'thdxr' &&
github.event.pull_request.user.login != 'kommander' &&
github.event.pull_request.user.login != 'jayair' &&
github.event.pull_request.user.login != 'fwang' &&
github.event.pull_request.user.login != 'adamdotdevin' &&
github.event.pull_request.user.login != 'iamdavidhill' &&
github.event.pull_request.user.login != 'opencode-agent[bot]'
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Setup Bun
uses: ./.github/actions/setup-bun
- name: Install dependencies
run: bun install
- name: Install opencode
run: curl -fsSL https://opencode.ai/install | bash
- name: Build prompt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
{
echo "Check for duplicate PRs related to this new PR:"
echo ""
echo "CURRENT_PR_NUMBER: $PR_NUMBER"
echo ""
echo "Title: $(gh pr view "$PR_NUMBER" --json title --jq .title)"
echo ""
echo "Description:"
gh pr view "$PR_NUMBER" --json body --jq .body
} > pr_info.txt
- name: Check for duplicate PRs
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
COMMENT=$(bun script/duplicate-pr.ts -f pr_info.txt "Check the attached file for PR details and search for duplicates")
gh pr comment "$PR_NUMBER" --body "_The following comment was made by an LLM, it may be inaccurate:_
$COMMENT"
+42
View File
@@ -0,0 +1,42 @@
name: nix desktop
on:
push:
branches: [dev]
paths:
- "flake.nix"
- "flake.lock"
- "nix/**"
- "packages/app/**"
- "packages/desktop/**"
pull_request:
paths:
- "flake.nix"
- "flake.lock"
- "nix/**"
- "packages/app/**"
- "packages/desktop/**"
workflow_dispatch:
jobs:
build-desktop:
strategy:
fail-fast: false
matrix:
os:
- blacksmith-4vcpu-ubuntu-2404
- macos-latest
runs-on: ${{ matrix.os }}
timeout-minutes: 60
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup Nix
uses: DeterminateSystems/nix-installer-action@v21
- name: Build desktop via flake
run: |
set -euo pipefail
nix --version
nix build .#desktop -L
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
- uses: ./.github/actions/setup-bun
- name: Run opencode
uses: sst/opencode/github@latest
uses: anomalyco/opencode/github@latest
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
OPENCODE_PERMISSION: '{"bash": "deny"}'
+139
View File
@@ -0,0 +1,139 @@
name: PR Standards
on:
pull_request_target:
types: [opened, edited, synchronize]
jobs:
check-standards:
if: |
github.event.pull_request.user.login != 'actions-user' &&
github.event.pull_request.user.login != 'opencode' &&
github.event.pull_request.user.login != 'rekram1-node' &&
github.event.pull_request.user.login != 'thdxr' &&
github.event.pull_request.user.login != 'kommander' &&
github.event.pull_request.user.login != 'jayair' &&
github.event.pull_request.user.login != 'fwang' &&
github.event.pull_request.user.login != 'adamdotdevin' &&
github.event.pull_request.user.login != 'iamdavidhill' &&
github.event.pull_request.user.login != 'opencode-agent[bot]'
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Check PR standards
uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request;
const title = pr.title;
async function addLabel(label) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: [label]
});
}
async function removeLabel(label) {
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
name: label
});
} catch (e) {
// Label wasn't present, ignore
}
}
async function comment(marker, body) {
const markerText = `<!-- pr-standards:${marker} -->`;
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number
});
const existing = comments.find(c => c.body.includes(markerText));
if (existing) return;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: markerText + '\n' + body
});
}
// Step 1: Check title format
// Matches: feat:, feat(scope):, feat (scope):, etc.
const titlePattern = /^(feat|fix|docs|chore|refactor|test)\s*(\([a-zA-Z0-9-]+\))?\s*:/;
const hasValidTitle = titlePattern.test(title);
if (!hasValidTitle) {
await addLabel('needs:title');
await comment('title', `Hey! Your PR title \`${title}\` doesn't follow conventional commit format.
Please update it to start with one of:
- \`feat:\` or \`feat(scope):\` new feature
- \`fix:\` or \`fix(scope):\` bug fix
- \`docs:\` or \`docs(scope):\` documentation changes
- \`chore:\` or \`chore(scope):\` maintenance tasks
- \`refactor:\` or \`refactor(scope):\` code refactoring
- \`test:\` or \`test(scope):\` adding or updating tests
Where \`scope\` is the package name (e.g., \`app\`, \`desktop\`, \`opencode\`).
See [CONTRIBUTING.md](../blob/dev/CONTRIBUTING.md#pr-titles) for details.`);
return;
}
await removeLabel('needs:title');
// Step 2: Check for linked issue (skip for docs/refactor PRs)
const skipIssueCheck = /^(docs|refactor)\s*(\([a-zA-Z0-9-]+\))?\s*:/.test(title);
if (skipIssueCheck) {
await removeLabel('needs:issue');
console.log('Skipping issue check for docs/refactor PR');
return;
}
const query = `
query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
closingIssuesReferences(first: 1) {
totalCount
}
}
}
}
`;
const result = await github.graphql(query, {
owner: context.repo.owner,
repo: context.repo.repo,
number: pr.number
});
const linkedIssues = result.repository.pullRequest.closingIssuesReferences.totalCount;
if (linkedIssues === 0) {
await addLabel('needs:issue');
await comment('issue', `Thanks for your contribution!
This PR doesn't have a linked issue. All PRs must reference an existing issue.
Please:
1. Open an issue describing the bug/feature (if one doesn't exist)
2. Add \`Fixes #<number>\` or \`Closes #<number>\` to this PR description
See [CONTRIBUTING.md](../blob/dev/CONTRIBUTING.md#issue-first-policy) for details.`);
return;
}
await removeLabel('needs:issue');
console.log('PR meets all standards');
+29 -17
View File
@@ -31,7 +31,7 @@ permissions:
jobs:
publish:
runs-on: blacksmith-4vcpu-ubuntu-2404
if: github.repository == 'sst/opencode'
if: github.repository == 'anomalyco/opencode'
steps:
- uses: actions/checkout@v3
with:
@@ -79,6 +79,12 @@ jobs:
AUR_KEY: ${{ secrets.AUR_KEY }}
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
NPM_CONFIG_PROVENANCE: false
- uses: actions/upload-artifact@v4
with:
name: opencode-cli
path: packages/opencode/dist
outputs:
release: ${{ steps.publish.outputs.release }}
tag: ${{ steps.publish.outputs.tag }}
@@ -86,7 +92,7 @@ jobs:
publish-tauri:
needs: publish
continue-on-error: true
continue-on-error: false
strategy:
fail-fast: false
matrix:
@@ -99,6 +105,8 @@ jobs:
target: x86_64-pc-windows-msvc
- host: blacksmith-4vcpu-ubuntu-2404
target: x86_64-unknown-linux-gnu
- host: blacksmith-4vcpu-ubuntu-2404-arm
target: aarch64-unknown-linux-gnu
runs-on: ${{ matrix.settings.host }}
steps:
- uses: actions/checkout@v3
@@ -143,13 +151,12 @@ jobs:
- uses: Swatinem/rust-cache@v2
with:
workspaces: packages/tauri/src-tauri
workspaces: packages/desktop/src-tauri
shared-key: ${{ matrix.settings.target }}
- name: Prepare
if: inputs.bump || inputs.version
run: |
cd packages/tauri
cd packages/desktop
bun ./scripts/prepare.ts
env:
OPENCODE_VERSION: ${{ needs.publish.outputs.version }}
@@ -159,6 +166,7 @@ jobs:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
RUST_TARGET: ${{ matrix.settings.target }}
GH_TOKEN: ${{ github.token }}
GITHUB_RUN_ID: ${{ github.run_id }}
# Fixes AppImage build issues, can be removed when https://github.com/tauri-apps/tauri/pull/12491 is released
- name: Install tauri-cli from portable appimage branch
@@ -169,8 +177,22 @@ jobs:
cargo tauri --version
- name: Build and upload artifacts
timeout-minutes: 20
uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a
uses: Wandalen/wretry.action@v3
timeout-minutes: 60
with:
attempt_limit: 3
attempt_delay: 10000
action: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a
with: |
projectPath: packages/desktop
uploadWorkflowArtifacts: true
tauriScript: ${{ (contains(matrix.settings.host, 'ubuntu') && 'cargo tauri') || '' }}
args: --target ${{ matrix.settings.target }} --config ./src-tauri/tauri.prod.conf.json --verbose
updaterJsonPreferNsis: true
releaseId: ${{ needs.publish.outputs.release }}
tagName: ${{ needs.publish.outputs.tag }}
releaseAssetNamePattern: opencode-desktop-[platform]-[arch][ext]
releaseDraft: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_BUNDLER_NEW_APPIMAGE_FORMAT: true
@@ -182,16 +204,6 @@ jobs:
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_PATH: ${{ runner.temp }}/apple-api-key.p8
with:
projectPath: packages/tauri
uploadWorkflowArtifacts: true
tauriScript: ${{ (contains(matrix.settings.host, 'ubuntu') && 'cargo tauri') || '' }}
args: --target ${{ matrix.settings.target }} --config src-tauri/tauri.prod.conf.json
updaterJsonPreferNsis: true
releaseId: ${{ needs.publish.outputs.release }}
tagName: ${{ needs.publish.outputs.tag }}
releaseAssetNamePattern: opencode-desktop-[platform]-[arch][ext]
releaseDraft: true
publish-release:
needs:
+2 -2
View File
@@ -47,7 +47,7 @@ jobs:
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "gh pr review*": "deny", "*": "deny" } }'
OPENCODE_PERMISSION: '{ "bash": { "*": "deny", "gh*": "allow", "gh pr review*": "deny" } }'
PR_TITLE: ${{ steps.pr-details.outputs.title }}
run: |
PR_BODY=$(jq -r .body pr_data.json)
@@ -64,7 +64,7 @@ jobs:
Please check all the code changes in this pull request against the style guide, also look for any bugs if they exist. Diffs are important but make sure you read the entire file to get proper context. Make it clear the suggestions are merely suggestions and the human can decide what to do
When critiquing code against the style guide, be sure that the code is ACTUALLY in violation, don't complain about else statements if they already use early returns there. You may complain about excessive nesting though, regardless of else statement usage.
When critiquing code style don't be a zealot, we don't like "let" statements but sometimes they are the simpliest option, if someone does a bunch of nesting with let, they should consider using iife (see packages/opencode/src/util.iife.ts)
When critiquing code style don't be a zealot, we don't like "let" statements but sometimes they are the simplest option, if someone does a bunch of nesting with let, they should consider using iife (see packages/opencode/src/util.iife.ts)
Use the gh cli to create comments on the files for the violations. Try to leave the comment on the exact line number. If you have a suggested fix include it in a suggestion code block.
If you are writing suggested fixes, BE SURE THAT the change you are recommending is actually valid typescript, often I have seen missing closing "}" or other syntax errors.
+33
View File
@@ -0,0 +1,33 @@
name: "Auto-close stale issues"
on:
schedule:
- cron: "30 1 * * *" # Daily at 1:30 AM
workflow_dispatch:
env:
DAYS_BEFORE_STALE: 90
DAYS_BEFORE_CLOSE: 7
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/stale@v10
with:
days-before-stale: ${{ env.DAYS_BEFORE_STALE }}
days-before-close: ${{ env.DAYS_BEFORE_CLOSE }}
stale-issue-label: "stale"
close-issue-message: |
[automated] Closing due to ${{ env.DAYS_BEFORE_STALE }}+ days of inactivity.
Feel free to reopen if you still need this!
stale-issue-message: |
[automated] This issue has had no activity for ${{ env.DAYS_BEFORE_STALE }} days.
It will be closed in ${{ env.DAYS_BEFORE_CLOSE }} days if there's no new activity.
remove-stale-when-updated: true
exempt-issue-labels: "pinned,security,feature-request,on-hold"
start-date: "2025-12-27"
+3
View File
@@ -5,8 +5,11 @@ on:
- cron: "0 12 * * *" # Run daily at 12:00 UTC
workflow_dispatch: # Allow manual trigger
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
stats:
if: github.repository == 'anomalyco/opencode'
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: write
+4 -3
View File
@@ -2,8 +2,8 @@ name: "sync-zed-extension"
on:
workflow_dispatch:
# release:
# types: [published]
release:
types: [published]
jobs:
zed:
@@ -31,4 +31,5 @@ jobs:
run: |
./script/sync-zed.ts ${{ steps.get_tag.outputs.tag }}
env:
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
ZED_EXTENSIONS_PAT: ${{ secrets.ZED_EXTENSIONS_PAT }}
ZED_PR_PAT: ${{ secrets.ZED_PR_PAT }}
+2 -4
View File
@@ -2,11 +2,9 @@ name: test
on:
push:
branches-ignore:
- production
branches:
- dev
pull_request:
branches-ignore:
- production
workflow_dispatch:
jobs:
test:
+92 -7
View File
@@ -17,7 +17,7 @@ on:
- "packages/*/package.json"
jobs:
update:
update-linux:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: blacksmith-4vcpu-ubuntu-2404
env:
@@ -47,14 +47,14 @@ jobs:
nix flake update
echo "✅ flake.lock updated successfully"
- name: Update node_modules hash
- name: Update node_modules hash for x86_64-linux
run: |
set -euo pipefail
echo "🔄 Updating node_modules hash..."
echo "🔄 Updating node_modules hash for x86_64-linux..."
nix/scripts/update-hashes.sh
echo "✅ node_modules hash updated successfully"
echo "✅ node_modules hash for x86_64-linux updated successfully"
- name: Commit hash changes
- name: Commit Linux hash changes
env:
TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}
run: |
@@ -65,7 +65,7 @@ jobs:
summarize() {
local status="$1"
{
echo "### Nix Hash Update"
echo "### Nix Hash Update (x86_64-linux)"
echo ""
echo "- ref: ${GITHUB_REF_NAME}"
echo "- status: ${status}"
@@ -89,7 +89,92 @@ jobs:
echo "🔗 Staging files..."
git add "${FILES[@]}"
echo "💾 Committing changes..."
git commit -m "Update Nix flake.lock and hashes"
git commit -m "Update Nix flake.lock and x86_64-linux hash"
echo "✅ Changes committed"
BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
echo "🌳 Pulling latest from branch: $BRANCH"
git pull --rebase origin "$BRANCH"
echo "🚀 Pushing changes to branch: $BRANCH"
git push origin HEAD:"$BRANCH"
echo "✅ Changes pushed successfully"
summarize "committed $(git rev-parse --short HEAD)"
update-macos:
needs: update-linux
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: macos-latest
env:
SYSTEM: aarch64-darwin
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
ref: ${{ github.head_ref || github.ref_name }}
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
- name: Setup Nix
uses: DeterminateSystems/nix-installer-action@v20
- name: Configure git
run: |
git config --global user.email "action@github.com"
git config --global user.name "Github Action"
- name: Pull latest changes
env:
TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}
run: |
BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
git pull origin "$BRANCH"
- name: Update node_modules hash for aarch64-darwin
run: |
set -euo pipefail
echo "🔄 Updating node_modules hash for aarch64-darwin..."
nix/scripts/update-hashes.sh
echo "✅ node_modules hash for aarch64-darwin updated successfully"
- name: Commit macOS hash changes
env:
TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}
run: |
set -euo pipefail
echo "🔍 Checking for changes in tracked Nix files..."
summarize() {
local status="$1"
{
echo "### Nix Hash Update (aarch64-darwin)"
echo ""
echo "- ref: ${GITHUB_REF_NAME}"
echo "- status: ${status}"
} >> "$GITHUB_STEP_SUMMARY"
if [ -n "${GITHUB_SERVER_URL:-}" ] && [ -n "${GITHUB_REPOSITORY:-}" ] && [ -n "${GITHUB_RUN_ID:-}" ]; then
echo "- run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" >> "$GITHUB_STEP_SUMMARY"
fi
echo "" >> "$GITHUB_STEP_SUMMARY"
}
FILES=(nix/hashes.json)
STATUS="$(git status --short -- "${FILES[@]}" || true)"
if [ -z "$STATUS" ]; then
echo "✅ No changes detected. Hash is already up to date."
summarize "no changes"
exit 0
fi
echo "📝 Changes detected:"
echo "$STATUS"
echo "🔗 Staging files..."
git add "${FILES[@]}"
echo "💾 Committing changes..."
git commit -m "Update aarch64-darwin hash"
echo "✅ Changes committed"
BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
+5
View File
@@ -20,3 +20,8 @@ opencode.json
a.out
target
.scripts
# Local dev files
opencode-dev
logs/
*.bun-build
+3
View File
@@ -1,11 +1,14 @@
---
description: ALWAYS use this when writing docs
color: "#38A3EE"
---
You are an expert technical documentation writer
You are not verbose
Use a relaxed and friendly tone
The title of the page should be a word or a 2-3 word phrase
The description should be one short line, should not start with "The", should
+26
View File
@@ -0,0 +1,26 @@
---
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.
-10
View File
@@ -1,10 +0,0 @@
---
description: Use this agent when you are asked to commit and push code changes to a git repository.
mode: subagent
---
You commit and push to git
Commit messages should be brief since they are used to generate release notes.
Messages should say WHY the change was made and not WHAT was changed.
+3 -2
View File
@@ -2,6 +2,7 @@
mode: primary
hidden: true
model: opencode/claude-haiku-4-5
color: "#44BA81"
tools:
"*": false
"github-triage": true
@@ -44,9 +45,9 @@ Desktop app issues:
#### zen
**Only** add if the issue mentions "zen" or "opencode zen". Zen is our gateway for coding models. **Do not** add for other gateways or inference providers.
**Only** add if the issue mentions "zen" or "opencode zen" or "opencode black".
If the issue doesn't have "zen" in it then don't add zen label
If the issue doesn't have "zen" or "opencode black" in it then don't add zen label
#### docs
+1 -1
View File
@@ -3,7 +3,7 @@ description: "find issue(s) on github"
model: opencode/claude-haiku-4-5
---
Search through existing issues in sst/opencode using the gh cli to find issues matching this query:
Search through existing issues in anomalyco/opencode using the gh cli to find issues matching this query:
$ARGUMENTS
+7 -1
View File
@@ -10,8 +10,14 @@
"options": {},
},
},
"mcp": {},
"mcp": {
"context7": {
"type": "remote",
"url": "https://mcp.context7.com/mcp",
},
},
"tools": {
"github-triage": false,
"github-pr-search": false,
},
}
+6
View File
@@ -0,0 +1,6 @@
---
name: test-skill
description: use this when asked to test skill
---
woah this is a test skill
+57
View File
@@ -0,0 +1,57 @@
/// <reference path="../env.d.ts" />
import { tool } from "@opencode-ai/plugin"
import DESCRIPTION from "./github-pr-search.txt"
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: DESCRIPTION,
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}`
},
})
+10
View File
@@ -0,0 +1,10 @@
Use this tool to search GitHub pull requests by title and description.
This tool searches PRs in the sst/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.
+1 -1
View File
@@ -40,7 +40,7 @@ export default tool({
async execute(args) {
const issue = getIssueNumber()
// const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
const owner = "sst"
const owner = "anomalyco"
const repo = "opencode"
const results: string[] = []
+4 -34
View File
@@ -1,34 +1,4 @@
## Debugging
- To test opencode in the `packages/opencode` directory you can run `bun dev`
## Tool Calling
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE. Here is an example illustrating how to execute 3 parallel file reads in this chat environment:
json
{
"recipient_name": "multi_tool_use.parallel",
"parameters": {
"tool_uses": [
{
"recipient_name": "functions.read",
"parameters": {
"filePath": "path/to/file.tsx"
}
},
{
"recipient_name": "functions.read",
"parameters": {
"filePath": "path/to/file.ts"
}
},
{
"recipient_name": "functions.read",
"parameters": {
"filePath": "path/to/file.md"
}
}
]
}
}
- To test opencode in `packages/opencode`, run `bun dev`.
- To regenerate the JavaScript SDK, run `./packages/sdk/js/script/build.ts`.
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE.
- The default branch in this repo is `dev`.
+135 -12
View File
@@ -14,10 +14,10 @@ However, any UI or core product feature must go through a design review with the
If you are unsure if a PR would be accepted, feel free to ask a maintainer or look for issues with any of the following labels:
- [`help wanted`](https://github.com/sst/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Ahelp-wanted)
- [`good first issue`](https://github.com/sst/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22)
- [`bug`](https://github.com/sst/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug)
- [`perf`](https://github.com/sst/opencode/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22perf%22)
- [`help wanted`](https://github.com/anomalyco/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Ahelp-wanted)
- [`good first issue`](https://github.com/anomalyco/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22)
- [`bug`](https://github.com/anomalyco/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug)
- [`perf`](https://github.com/anomalyco/opencode/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22perf%22)
> [!NOTE]
> PRs that ignore these guardrails will likely be closed.
@@ -34,11 +34,82 @@ Want to take on an issue? Leave a comment and a maintainer may assign it to you
bun dev
```
### Running against a different directory
By default, `bun dev` runs OpenCode in the `packages/opencode` directory. To run it against a different directory or repository:
```bash
bun dev <directory>
```
To run OpenCode in the root of the opencode repo itself:
```bash
bun dev .
```
### Building a "localcode"
To compile a standalone executable:
```bash
./packages/opencode/script/build.ts --single
```
Then run it with:
```bash
./packages/opencode/dist/opencode-<platform>/bin/opencode
```
Replace `<platform>` with your platform (e.g., `darwin-arm64`, `linux-x64`).
- Core pieces:
- `packages/opencode`: OpenCode core business logic & server.
- `packages/opencode/src/cli/cmd/tui/`: The TUI code, written in SolidJS with [opentui](https://github.com/sst/opentui)
- `packages/app`: The shared web UI components, written in SolidJS
- `packages/desktop`: The native desktop app, built with Tauri (wraps `packages/app`)
- `packages/plugin`: Source for `@opencode-ai/plugin`
### Running the Web App
To test UI changes during development, run the web app:
```bash
bun run --cwd packages/app dev
```
This starts a local dev server at http://localhost:5173 (or similar port shown in output). Most UI changes can be tested here.
### Running the Desktop App
The desktop app is a native Tauri application that wraps the web UI.
To run the native desktop app:
```bash
bun run --cwd packages/desktop tauri dev
```
This starts the web dev server on http://localhost:1420 and opens the native window.
If you only want the web dev server (no native shell):
```bash
bun run --cwd packages/desktop dev
```
To create a production `dist/` and build the native app bundle:
```bash
bun run --cwd packages/desktop tauri build
```
This runs `bun run --cwd packages/desktop build` automatically via Tauris `beforeBuildCommand`.
> [!NOTE]
> Running the desktop app requires additional Tauri dependencies (Rust toolchain, platform-specific libraries). See the [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/) for setup instructions.
> [!NOTE]
> If you make changes to the API or SDK (e.g. `packages/opencode/src/server/server.ts`), run `./script/generate.ts` to regenerate the SDK and related files.
@@ -53,12 +124,12 @@ your debugger via that URL. Other methods can result in breakpoints being mapped
Caveats:
- `*.tsx` files won't have their breakpoints correctly mapped. This seems due to Bun currently not supporting source maps on code transformed
via `BunPlugin`s (currently necessary due to our dependency on `@opentui/solid`). Currently, the best you can do in terms of debugging `*.tsx`
files is writing a `debugger;` statement. Debugging facilities like stepping won't work, but at least you will be informed if a specific code
is triggered.
- If you want to run the OpenCode TUI and have breakpoints triggered in the server code, you might need to run `bun dev spawn` instead of
the usual `bun dev`. This is because `bun dev` runs the server in a worker thread and breakpoints might not work there.
- If `spawn` does not work for you, you can debug the server separately:
- Debug server: `bun run --inspect=ws://localhost:6499/ ./src/index.ts serve --port 4096`,
then attach TUI with `opencode attach http://localhost:4096`
- Debug TUI: `bun run --inspect=ws://localhost:6499/ --conditions=browser ./src/index.ts`
Other tips and tricks:
@@ -78,11 +149,63 @@ With that said, you may want to try these methods, as they might work for you.
## Pull Request Expectations
- Try to keep pull requests small and focused.
- Link relevant issue(s) in the description
### Issue First Policy
**All PRs must reference an existing issue.** Before opening a PR, open an issue describing the bug or feature. This helps maintainers triage and prevents duplicate work. PRs without a linked issue may be closed without review.
- Use `Fixes #123` or `Closes #123` in your PR description to link the issue
- For small fixes, a brief issue is fine - just enough context for maintainers to understand the problem
### General Requirements
- Keep pull requests small and focused
- Explain the issue and why your change fixes it
- Avoid having verbose LLM generated PR descriptions
- Before adding new functions or functionality, ensure that such behavior doesn't already exist elsewhere in the codebase.
- Before adding new functionality, ensure it doesn't already exist elsewhere in the codebase
### UI Changes
If your PR includes UI changes, please include screenshots or videos showing the before and after. This helps maintainers review faster and gives you quicker feedback.
### Logic Changes
For non-UI changes (bug fixes, new features, refactors), explain **how you verified it works**:
- What did you test?
- How can a reviewer reproduce/confirm the fix?
### No AI-Generated Walls of Text
Long, AI-generated PR descriptions and issues are not acceptable and may be ignored. Respect the maintainers' time:
- Write short, focused descriptions
- Explain what changed and why in your own words
- If you can't explain it briefly, your PR might be too large
### PR Titles
PR titles should follow conventional commit standards:
- `feat:` new feature or functionality
- `fix:` bug fix
- `docs:` documentation or README changes
- `chore:` maintenance tasks, dependency updates, etc.
- `refactor:` code refactoring without changing behavior
- `test:` adding or updating tests
You can optionally include a scope to indicate which package is affected:
- `feat(app):` feature in the app package
- `fix(desktop):` bug fix in the desktop package
- `chore(opencode):` maintenance in the opencode package
Examples:
- `docs: update contributing guidelines`
- `fix: resolve crash on startup`
- `feat: add dark mode support`
- `feat(app): add dark mode support`
- `fix(desktop): resolve crash on startup`
- `chore: bump dependency versions`
### Style Preferences
+9 -13
View File
@@ -11,7 +11,7 @@
<p align="center">
<a href="https://opencode.ai/discord"><img alt="Discord" src="https://img.shields.io/discord/1391832426048651334?style=flat-square&label=discord" /></a>
<a href="https://www.npmjs.com/package/opencode-ai"><img alt="npm" src="https://img.shields.io/npm/v/opencode-ai?style=flat-square" /></a>
<a href="https://github.com/sst/opencode/actions/workflows/publish.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/sst/opencode/publish.yml?style=flat-square&branch=dev" /></a>
<a href="https://github.com/anomalyco/opencode/actions/workflows/publish.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/anomalyco/opencode/publish.yml?style=flat-square&branch=dev" /></a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)
@@ -28,10 +28,11 @@ curl -fsSL https://opencode.ai/install | bash
npm i -g opencode-ai@latest # or bun/pnpm/yarn
scoop bucket add extras; scoop install extras/opencode # Windows
choco install opencode # Windows
brew install opencode # macOS and Linux
brew install anomalyco/tap/opencode # macOS and Linux (recommended, always up to date)
brew install opencode # macOS and Linux (official brew formula, updated less)
paru -S opencode-bin # Arch Linux
mise use -g github:sst/opencode # Any OS
nix run nixpkgs#opencode # or github:sst/opencode for latest dev branch
mise use -g opencode # Any OS
nix run nixpkgs#opencode # or github:anomalyco/opencode for latest dev branch
```
> [!TIP]
@@ -39,7 +40,7 @@ nix run nixpkgs#opencode # or github:sst/opencode for latest dev branc
### Desktop App (BETA)
OpenCode is also available as a desktop application. Download directly from the [releases page](https://github.com/sst/opencode/releases) or [opencode.ai/download](https://opencode.ai/download).
OpenCode is also available as a desktop application. Download directly from the [releases page](https://github.com/anomalyco/opencode/releases) or [opencode.ai/download](https://opencode.ai/download).
| Platform | Download |
| --------------------- | ------------------------------------- |
@@ -70,8 +71,7 @@ XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash
### Agents
OpenCode includes two built-in agents you can switch between,
you can switch between these using the `Tab` key.
OpenCode includes two built-in agents you can switch between with the `Tab` key.
- **build** - Default, full access agent for development work
- **plan** - Read-only agent for analysis and code exploration
@@ -79,7 +79,7 @@ you can switch between these using the `Tab` key.
- Asks permission before running bash commands
- Ideal for exploring unfamiliar codebases or planning changes
Also, included is a **general** subagent for complex searches and multi-step tasks.
Also, included is a **general** subagent for complex searches and multistep tasks.
This is used internally and can be invoked using `@general` in messages.
Learn more about [agents](https://opencode.ai/docs/agents).
@@ -98,7 +98,7 @@ If you are working on a project that's related to OpenCode and is using "opencod
### FAQ
#### How is this different than Claude Code?
#### How is this different from Claude Code?
It's very similar to Claude Code in terms of capability. Here are the key differences:
@@ -108,10 +108,6 @@ It's very similar to Claude Code in terms of capability. Here are the key differ
- A focus on TUI. OpenCode is built by neovim users and the creators of [terminal.shop](https://terminal.shop); we are going to push the limits of what's possible in the terminal.
- A client/server architecture. This for example can allow OpenCode to run on your computer, while you can drive it remotely from a mobile app. Meaning that the TUI frontend is just one of the possible clients.
#### What's the other repo?
The other confusingly named repo has no relation to this one. You can [read the story behind it here](https://x.com/thdxr/status/1933561254481666466).
---
**Join our community** [Discord](https://discord.gg/opencode) | [X.com](https://x.com/opencode)
+116
View File
@@ -0,0 +1,116 @@
<p align="center">
<a href="https://opencode.ai">
<picture>
<source srcset="packages/console/app/src/asset/logo-ornate-dark.svg" media="(prefers-color-scheme: dark)">
<source srcset="packages/console/app/src/asset/logo-ornate-light.svg" media="(prefers-color-scheme: light)">
<img src="packages/console/app/src/asset/logo-ornate-light.svg" alt="OpenCode logo">
</picture>
</a>
</p>
<p align="center">开源的 AI Coding Agent。</p>
<p align="center">
<a href="https://opencode.ai/discord"><img alt="Discord" src="https://img.shields.io/discord/1391832426048651334?style=flat-square&label=discord" /></a>
<a href="https://www.npmjs.com/package/opencode-ai"><img alt="npm" src="https://img.shields.io/npm/v/opencode-ai?style=flat-square" /></a>
<a href="https://github.com/anomalyco/opencode/actions/workflows/publish.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/anomalyco/opencode/publish.yml?style=flat-square&branch=dev" /></a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)
---
### 安装
```bash
# 直接安装 (YOLO)
curl -fsSL https://opencode.ai/install | bash
# 软件包管理器
npm i -g opencode-ai@latest # 也可使用 bun/pnpm/yarn
scoop bucket add extras; scoop install extras/opencode # Windows
choco install opencode # Windows
brew install anomalyco/tap/opencode # macOS 和 Linux(推荐,始终保持最新)
brew install opencode # macOS 和 Linux(官方 brew formula,更新频率较低)
paru -S opencode-bin # Arch Linux
mise use -g opencode # 任意系统
nix run nixpkgs#opencode # 或用 github:anomalyco/opencode 获取最新 dev 分支
```
> [!TIP]
> 安装前请先移除 0.1.x 之前的旧版本。
### 桌面应用程序 (BETA)
OpenCode 也提供桌面版应用。可直接从 [发布页 (releases page)](https://github.com/anomalyco/opencode/releases) 或 [opencode.ai/download](https://opencode.ai/download) 下载。
| 平台 | 下载文件 |
| --------------------- | ------------------------------------- |
| macOS (Apple Silicon) | `opencode-desktop-darwin-aarch64.dmg` |
| macOS (Intel) | `opencode-desktop-darwin-x64.dmg` |
| Windows | `opencode-desktop-windows-x64.exe` |
| Linux | `.deb``.rpm` 或 AppImage |
```bash
# macOS (Homebrew Cask)
brew install --cask opencode-desktop
```
#### 安装目录
安装脚本按照以下优先级决定安装路径:
1. `$OPENCODE_INSTALL_DIR` - 自定义安装目录
2. `$XDG_BIN_DIR` - 符合 XDG 基础目录规范的路径
3. `$HOME/bin` - 如果存在或可创建的用户二进制目录
4. `$HOME/.opencode/bin` - 默认备用路径
```bash
# 示例
OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash
XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash
```
### Agents
OpenCode 内置两种 Agent,可用 `Tab` 键快速切换:
- **build** - 默认模式,具备完整权限,适合开发工作
- **plan** - 只读模式,适合代码分析与探索
- 默认拒绝修改文件
- 运行 bash 命令前会询问
- 便于探索未知代码库或规划改动
另外还包含一个 **general** 子 Agent,用于复杂搜索和多步任务,内部使用,也可在消息中输入 `@general` 调用。
了解更多 [Agents](https://opencode.ai/docs/agents) 相关信息。
### 文档
更多配置说明请查看我们的 [**官方文档**](https://opencode.ai/docs)。
### 参与贡献
如有兴趣贡献代码,请在提交 PR 前阅读 [贡献指南 (Contributing Docs)](./CONTRIBUTING.md)。
### 基于 OpenCode 进行开发
如果你在项目名中使用了 “opencode”(如 “opencode-dashboard” 或 “opencode-mobile”),请在 README 里注明该项目不是 OpenCode 团队官方开发,且不存在隶属关系。
### 常见问题 (FAQ)
#### 这和 Claude Code 有什么不同?
功能上很相似,关键差异:
- 100% 开源。
- 不绑定特定提供商。推荐使用 [OpenCode Zen](https://opencode.ai/zen) 的模型,但也可搭配 Claude、OpenAI、Google 甚至本地模型。模型迭代会缩小差异、降低成本,因此保持 provider-agnostic 很重要。
- 内置 LSP 支持。
- 聚焦终端界面 (TUI)。OpenCode 由 Neovim 爱好者和 [terminal.shop](https://terminal.shop) 的创建者打造,会持续探索终端的极限。
- 客户端/服务器架构。可在本机运行,同时用移动设备远程驱动。TUI 只是众多潜在客户端之一。
#### 另一个同名的仓库是什么?
另一个名字相近的仓库与本项目无关。[点击这里了解背后故事](https://x.com/thdxr/status/1933561254481666466)。
---
**加入我们的社区** [Discord](https://discord.gg/opencode) | [X.com](https://x.com/opencode)
+6 -5
View File
@@ -11,7 +11,7 @@
<p align="center">
<a href="https://opencode.ai/discord"><img alt="Discord" src="https://img.shields.io/discord/1391832426048651334?style=flat-square&label=discord" /></a>
<a href="https://www.npmjs.com/package/opencode-ai"><img alt="npm" src="https://img.shields.io/npm/v/opencode-ai?style=flat-square" /></a>
<a href="https://github.com/sst/opencode/actions/workflows/publish.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/sst/opencode/publish.yml?style=flat-square&branch=dev" /></a>
<a href="https://github.com/anomalyco/opencode/actions/workflows/publish.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/anomalyco/opencode/publish.yml?style=flat-square&branch=dev" /></a>
</p>
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)
@@ -28,10 +28,11 @@ curl -fsSL https://opencode.ai/install | bash
npm i -g opencode-ai@latest # 也可使用 bun/pnpm/yarn
scoop bucket add extras; scoop install extras/opencode # Windows
choco install opencode # Windows
brew install opencode # macOS 與 Linux
brew install anomalyco/tap/opencode # macOS 與 Linux(推薦,始終保持最新)
brew install opencode # macOS 與 Linux(官方 brew formula,更新頻率較低)
paru -S opencode-bin # Arch Linux
mise use -g github:sst/opencode # 任何作業系統
nix run nixpkgs#opencode # 或使用 github:sst/opencode 以取得最新開發分支
mise use -g github:anomalyco/opencode # 任何作業系統
nix run nixpkgs#opencode # 或使用 github:anomalyco/opencode 以取得最新開發分支
```
> [!TIP]
@@ -39,7 +40,7 @@ nix run nixpkgs#opencode # 或使用 github:sst/opencode 以取得最
### 桌面應用程式 (BETA)
OpenCode 也提供桌面版應用程式。您可以直接從 [發佈頁面 (releases page)](https://github.com/sst/opencode/releases) 或 [opencode.ai/download](https://opencode.ai/download) 下載。
OpenCode 也提供桌面版應用程式。您可以直接從 [發佈頁面 (releases page)](https://github.com/anomalyco/opencode/releases) 或 [opencode.ai/download](https://opencode.ai/download) 下載。
| 平台 | 下載連結 |
| --------------------- | ------------------------------------- |
+11
View File
@@ -0,0 +1,11 @@
# Reporting Security Issues
We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/anomalyco/opencode/security/advisories/new) tab.
The team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
## Escalation
If you do not receive an acknowledgement of your report within 6 business days, you may send an email to security@anoma.ly
+200 -177
View File
@@ -1,179 +1,202 @@
# Download Stats
| Date | GitHub Downloads | npm Downloads | Total |
| ---------- | ------------------- | ------------------- | ------------------- |
| 2025-06-29 | 18,789 (+0) | 39,420 (+0) | 58,209 (+0) |
| 2025-06-30 | 20,127 (+1,338) | 41,059 (+1,639) | 61,186 (+2,977) |
| 2025-07-01 | 22,108 (+1,981) | 43,745 (+2,686) | 65,853 (+4,667) |
| 2025-07-02 | 24,814 (+2,706) | 46,168 (+2,423) | 70,982 (+5,129) |
| 2025-07-03 | 27,834 (+3,020) | 49,955 (+3,787) | 77,789 (+6,807) |
| 2025-07-04 | 30,608 (+2,774) | 54,758 (+4,803) | 85,366 (+7,577) |
| 2025-07-05 | 32,524 (+1,916) | 58,371 (+3,613) | 90,895 (+5,529) |
| 2025-07-06 | 33,766 (+1,242) | 59,694 (+1,323) | 93,460 (+2,565) |
| 2025-07-08 | 38,052 (+4,286) | 64,468 (+4,774) | 102,520 (+9,060) |
| 2025-07-09 | 40,924 (+2,872) | 67,935 (+3,467) | 108,859 (+6,339) |
| 2025-07-10 | 43,796 (+2,872) | 71,402 (+3,467) | 115,198 (+6,339) |
| 2025-07-11 | 46,982 (+3,186) | 77,462 (+6,060) | 124,444 (+9,246) |
| 2025-07-12 | 49,302 (+2,320) | 82,177 (+4,715) | 131,479 (+7,035) |
| 2025-07-13 | 50,803 (+1,501) | 86,394 (+4,217) | 137,197 (+5,718) |
| 2025-07-14 | 53,283 (+2,480) | 87,860 (+1,466) | 141,143 (+3,946) |
| 2025-07-15 | 57,590 (+4,307) | 91,036 (+3,176) | 148,626 (+7,483) |
| 2025-07-16 | 62,313 (+4,723) | 95,258 (+4,222) | 157,571 (+8,945) |
| 2025-07-17 | 66,684 (+4,371) | 100,048 (+4,790) | 166,732 (+9,161) |
| 2025-07-18 | 70,379 (+3,695) | 102,587 (+2,539) | 172,966 (+6,234) |
| 2025-07-19 | 73,497 (+3,117) | 105,904 (+3,317) | 179,401 (+6,434) |
| 2025-07-20 | 76,453 (+2,956) | 109,044 (+3,140) | 185,497 (+6,096) |
| 2025-07-21 | 80,197 (+3,744) | 113,537 (+4,493) | 193,734 (+8,237) |
| 2025-07-22 | 84,251 (+4,054) | 118,073 (+4,536) | 202,324 (+8,590) |
| 2025-07-23 | 88,589 (+4,338) | 121,436 (+3,363) | 210,025 (+7,701) |
| 2025-07-24 | 92,469 (+3,880) | 124,091 (+2,655) | 216,560 (+6,535) |
| 2025-07-25 | 96,417 (+3,948) | 126,985 (+2,894) | 223,402 (+6,842) |
| 2025-07-26 | 100,646 (+4,229) | 131,411 (+4,426) | 232,057 (+8,655) |
| 2025-07-27 | 102,644 (+1,998) | 134,736 (+3,325) | 237,380 (+5,323) |
| 2025-07-28 | 105,446 (+2,802) | 136,016 (+1,280) | 241,462 (+4,082) |
| 2025-07-29 | 108,998 (+3,552) | 137,542 (+1,526) | 246,540 (+5,078) |
| 2025-07-30 | 113,544 (+4,546) | 140,317 (+2,775) | 253,861 (+7,321) |
| 2025-07-31 | 118,339 (+4,795) | 143,344 (+3,027) | 261,683 (+7,822) |
| 2025-08-01 | 123,539 (+5,200) | 146,680 (+3,336) | 270,219 (+8,536) |
| 2025-08-02 | 127,864 (+4,325) | 149,236 (+2,556) | 277,100 (+6,881) |
| 2025-08-03 | 131,397 (+3,533) | 150,451 (+1,215) | 281,848 (+4,748) |
| 2025-08-04 | 136,266 (+4,869) | 153,260 (+2,809) | 289,526 (+7,678) |
| 2025-08-05 | 141,596 (+5,330) | 155,752 (+2,492) | 297,348 (+7,822) |
| 2025-08-06 | 147,067 (+5,471) | 158,309 (+2,557) | 305,376 (+8,028) |
| 2025-08-07 | 152,591 (+5,524) | 160,889 (+2,580) | 313,480 (+8,104) |
| 2025-08-08 | 158,187 (+5,596) | 163,448 (+2,559) | 321,635 (+8,155) |
| 2025-08-09 | 162,770 (+4,583) | 165,721 (+2,273) | 328,491 (+6,856) |
| 2025-08-10 | 165,695 (+2,925) | 167,109 (+1,388) | 332,804 (+4,313) |
| 2025-08-11 | 169,297 (+3,602) | 167,953 (+844) | 337,250 (+4,446) |
| 2025-08-12 | 176,307 (+7,010) | 171,876 (+3,923) | 348,183 (+10,933) |
| 2025-08-13 | 182,997 (+6,690) | 177,182 (+5,306) | 360,179 (+11,996) |
| 2025-08-14 | 189,063 (+6,066) | 179,741 (+2,559) | 368,804 (+8,625) |
| 2025-08-15 | 193,608 (+4,545) | 181,792 (+2,051) | 375,400 (+6,596) |
| 2025-08-16 | 198,118 (+4,510) | 184,558 (+2,766) | 382,676 (+7,276) |
| 2025-08-17 | 201,299 (+3,181) | 186,269 (+1,711) | 387,568 (+4,892) |
| 2025-08-18 | 204,559 (+3,260) | 187,399 (+1,130) | 391,958 (+4,390) |
| 2025-08-19 | 209,814 (+5,255) | 189,668 (+2,269) | 399,482 (+7,524) |
| 2025-08-20 | 214,497 (+4,683) | 191,481 (+1,813) | 405,978 (+6,496) |
| 2025-08-21 | 220,465 (+5,968) | 194,784 (+3,303) | 415,249 (+9,271) |
| 2025-08-22 | 225,899 (+5,434) | 197,204 (+2,420) | 423,103 (+7,854) |
| 2025-08-23 | 229,005 (+3,106) | 199,238 (+2,034) | 428,243 (+5,140) |
| 2025-08-24 | 232,098 (+3,093) | 201,157 (+1,919) | 433,255 (+5,012) |
| 2025-08-25 | 236,607 (+4,509) | 202,650 (+1,493) | 439,257 (+6,002) |
| 2025-08-26 | 242,783 (+6,176) | 205,242 (+2,592) | 448,025 (+8,768) |
| 2025-08-27 | 248,409 (+5,626) | 205,242 (+0) | 453,651 (+5,626) |
| 2025-08-28 | 252,796 (+4,387) | 205,242 (+0) | 458,038 (+4,387) |
| 2025-08-29 | 256,045 (+3,249) | 211,075 (+5,833) | 467,120 (+9,082) |
| 2025-08-30 | 258,863 (+2,818) | 212,397 (+1,322) | 471,260 (+4,140) |
| 2025-08-31 | 262,004 (+3,141) | 213,944 (+1,547) | 475,948 (+4,688) |
| 2025-09-01 | 265,359 (+3,355) | 215,115 (+1,171) | 480,474 (+4,526) |
| 2025-09-02 | 270,483 (+5,124) | 217,075 (+1,960) | 487,558 (+7,084) |
| 2025-09-03 | 274,793 (+4,310) | 219,755 (+2,680) | 494,548 (+6,990) |
| 2025-09-04 | 280,430 (+5,637) | 222,103 (+2,348) | 502,533 (+7,985) |
| 2025-09-05 | 283,769 (+3,339) | 223,793 (+1,690) | 507,562 (+5,029) |
| 2025-09-06 | 286,245 (+2,476) | 225,036 (+1,243) | 511,281 (+3,719) |
| 2025-09-07 | 288,623 (+2,378) | 225,866 (+830) | 514,489 (+3,208) |
| 2025-09-08 | 293,341 (+4,718) | 227,073 (+1,207) | 520,414 (+5,925) |
| 2025-09-09 | 300,036 (+6,695) | 229,788 (+2,715) | 529,824 (+9,410) |
| 2025-09-10 | 307,287 (+7,251) | 233,435 (+3,647) | 540,722 (+10,898) |
| 2025-09-11 | 314,083 (+6,796) | 237,356 (+3,921) | 551,439 (+10,717) |
| 2025-09-12 | 321,046 (+6,963) | 240,728 (+3,372) | 561,774 (+10,335) |
| 2025-09-13 | 324,894 (+3,848) | 245,539 (+4,811) | 570,433 (+8,659) |
| 2025-09-14 | 328,876 (+3,982) | 248,245 (+2,706) | 577,121 (+6,688) |
| 2025-09-15 | 334,201 (+5,325) | 250,983 (+2,738) | 585,184 (+8,063) |
| 2025-09-16 | 342,609 (+8,408) | 255,264 (+4,281) | 597,873 (+12,689) |
| 2025-09-17 | 351,117 (+8,508) | 260,970 (+5,706) | 612,087 (+14,214) |
| 2025-09-18 | 358,717 (+7,600) | 266,922 (+5,952) | 625,639 (+13,552) |
| 2025-09-19 | 365,401 (+6,684) | 271,859 (+4,937) | 637,260 (+11,621) |
| 2025-09-20 | 372,092 (+6,691) | 276,917 (+5,058) | 649,009 (+11,749) |
| 2025-09-21 | 377,079 (+4,987) | 280,261 (+3,344) | 657,340 (+8,331) |
| 2025-09-22 | 382,492 (+5,413) | 284,009 (+3,748) | 666,501 (+9,161) |
| 2025-09-23 | 387,008 (+4,516) | 289,129 (+5,120) | 676,137 (+9,636) |
| 2025-09-24 | 393,325 (+6,317) | 294,927 (+5,798) | 688,252 (+12,115) |
| 2025-09-25 | 398,879 (+5,554) | 301,663 (+6,736) | 700,542 (+12,290) |
| 2025-09-26 | 404,334 (+5,455) | 306,713 (+5,050) | 711,047 (+10,505) |
| 2025-09-27 | 411,618 (+7,284) | 317,763 (+11,050) | 729,381 (+18,334) |
| 2025-09-28 | 414,910 (+3,292) | 322,522 (+4,759) | 737,432 (+8,051) |
| 2025-09-29 | 419,919 (+5,009) | 328,033 (+5,511) | 747,952 (+10,520) |
| 2025-09-30 | 427,991 (+8,072) | 336,472 (+8,439) | 764,463 (+16,511) |
| 2025-10-01 | 433,591 (+5,600) | 341,742 (+5,270) | 775,333 (+10,870) |
| 2025-10-02 | 440,852 (+7,261) | 348,099 (+6,357) | 788,951 (+13,618) |
| 2025-10-03 | 446,829 (+5,977) | 359,937 (+11,838) | 806,766 (+17,815) |
| 2025-10-04 | 452,561 (+5,732) | 370,386 (+10,449) | 822,947 (+16,181) |
| 2025-10-05 | 455,559 (+2,998) | 374,745 (+4,359) | 830,304 (+7,357) |
| 2025-10-06 | 460,927 (+5,368) | 379,489 (+4,744) | 840,416 (+10,112) |
| 2025-10-07 | 467,336 (+6,409) | 385,438 (+5,949) | 852,774 (+12,358) |
| 2025-10-08 | 474,643 (+7,307) | 394,139 (+8,701) | 868,782 (+16,008) |
| 2025-10-09 | 479,203 (+4,560) | 400,526 (+6,387) | 879,729 (+10,947) |
| 2025-10-10 | 484,374 (+5,171) | 406,015 (+5,489) | 890,389 (+10,660) |
| 2025-10-11 | 488,427 (+4,053) | 414,699 (+8,684) | 903,126 (+12,737) |
| 2025-10-12 | 492,125 (+3,698) | 418,745 (+4,046) | 910,870 (+7,744) |
| 2025-10-14 | 505,130 (+13,005) | 429,286 (+10,541) | 934,416 (+23,546) |
| 2025-10-15 | 512,717 (+7,587) | 439,290 (+10,004) | 952,007 (+17,591) |
| 2025-10-16 | 517,719 (+5,002) | 447,137 (+7,847) | 964,856 (+12,849) |
| 2025-10-17 | 526,239 (+8,520) | 457,467 (+10,330) | 983,706 (+18,850) |
| 2025-10-18 | 531,564 (+5,325) | 465,272 (+7,805) | 996,836 (+13,130) |
| 2025-10-19 | 536,209 (+4,645) | 469,078 (+3,806) | 1,005,287 (+8,451) |
| 2025-10-20 | 541,264 (+5,055) | 472,952 (+3,874) | 1,014,216 (+8,929) |
| 2025-10-21 | 548,721 (+7,457) | 479,703 (+6,751) | 1,028,424 (+14,208) |
| 2025-10-22 | 557,949 (+9,228) | 491,395 (+11,692) | 1,049,344 (+20,920) |
| 2025-10-23 | 564,716 (+6,767) | 498,736 (+7,341) | 1,063,452 (+14,108) |
| 2025-10-24 | 572,692 (+7,976) | 506,905 (+8,169) | 1,079,597 (+16,145) |
| 2025-10-25 | 578,927 (+6,235) | 516,129 (+9,224) | 1,095,056 (+15,459) |
| 2025-10-26 | 584,409 (+5,482) | 521,179 (+5,050) | 1,105,588 (+10,532) |
| 2025-10-27 | 589,999 (+5,590) | 526,001 (+4,822) | 1,116,000 (+10,412) |
| 2025-10-28 | 595,776 (+5,777) | 532,438 (+6,437) | 1,128,214 (+12,214) |
| 2025-10-29 | 606,259 (+10,483) | 542,064 (+9,626) | 1,148,323 (+20,109) |
| 2025-10-30 | 613,746 (+7,487) | 542,064 (+0) | 1,155,810 (+7,487) |
| 2025-10-30 | 617,846 (+4,100) | 555,026 (+12,962) | 1,172,872 (+17,062) |
| 2025-10-31 | 626,612 (+8,766) | 564,579 (+9,553) | 1,191,191 (+18,319) |
| 2025-11-01 | 636,100 (+9,488) | 581,806 (+17,227) | 1,217,906 (+26,715) |
| 2025-11-02 | 644,067 (+7,967) | 590,004 (+8,198) | 1,234,071 (+16,165) |
| 2025-11-03 | 653,130 (+9,063) | 597,139 (+7,135) | 1,250,269 (+16,198) |
| 2025-11-04 | 663,912 (+10,782) | 608,056 (+10,917) | 1,271,968 (+21,699) |
| 2025-11-05 | 675,074 (+11,162) | 619,690 (+11,634) | 1,294,764 (+22,796) |
| 2025-11-06 | 686,252 (+11,178) | 630,885 (+11,195) | 1,317,137 (+22,373) |
| 2025-11-07 | 696,646 (+10,394) | 642,146 (+11,261) | 1,338,792 (+21,655) |
| 2025-11-08 | 706,035 (+9,389) | 653,489 (+11,343) | 1,359,524 (+20,732) |
| 2025-11-09 | 713,462 (+7,427) | 660,459 (+6,970) | 1,373,921 (+14,397) |
| 2025-11-10 | 722,288 (+8,826) | 668,225 (+7,766) | 1,390,513 (+16,592) |
| 2025-11-11 | 729,769 (+7,481) | 677,501 (+9,276) | 1,407,270 (+16,757) |
| 2025-11-12 | 740,180 (+10,411) | 686,454 (+8,953) | 1,426,634 (+19,364) |
| 2025-11-13 | 749,905 (+9,725) | 696,157 (+9,703) | 1,446,062 (+19,428) |
| 2025-11-14 | 759,928 (+10,023) | 705,237 (+9,080) | 1,465,165 (+19,103) |
| 2025-11-15 | 765,955 (+6,027) | 712,870 (+7,633) | 1,478,825 (+13,660) |
| 2025-11-16 | 771,069 (+5,114) | 716,596 (+3,726) | 1,487,665 (+8,840) |
| 2025-11-17 | 780,161 (+9,092) | 723,339 (+6,743) | 1,503,500 (+15,835) |
| 2025-11-18 | 791,563 (+11,402) | 732,544 (+9,205) | 1,524,107 (+20,607) |
| 2025-11-19 | 804,409 (+12,846) | 747,624 (+15,080) | 1,552,033 (+27,926) |
| 2025-11-20 | 814,620 (+10,211) | 757,907 (+10,283) | 1,572,527 (+20,494) |
| 2025-11-21 | 826,309 (+11,689) | 769,307 (+11,400) | 1,595,616 (+23,089) |
| 2025-11-22 | 837,269 (+10,960) | 780,996 (+11,689) | 1,618,265 (+22,649) |
| 2025-11-23 | 846,609 (+9,340) | 795,069 (+14,073) | 1,641,678 (+23,413) |
| 2025-11-24 | 856,733 (+10,124) | 804,033 (+8,964) | 1,660,766 (+19,088) |
| 2025-11-25 | 869,423 (+12,690) | 817,339 (+13,306) | 1,686,762 (+25,996) |
| 2025-11-26 | 881,414 (+11,991) | 832,518 (+15,179) | 1,713,932 (+27,170) |
| 2025-11-27 | 893,960 (+12,546) | 846,180 (+13,662) | 1,740,140 (+26,208) |
| 2025-11-28 | 901,741 (+7,781) | 856,482 (+10,302) | 1,758,223 (+18,083) |
| 2025-11-29 | 908,689 (+6,948) | 863,361 (+6,879) | 1,772,050 (+13,827) |
| 2025-11-30 | 916,116 (+7,427) | 870,194 (+6,833) | 1,786,310 (+14,260) |
| 2025-12-01 | 925,898 (+9,782) | 876,500 (+6,306) | 1,802,398 (+16,088) |
| 2025-12-02 | 939,250 (+13,352) | 890,919 (+14,419) | 1,830,169 (+27,771) |
| 2025-12-03 | 952,249 (+12,999) | 903,713 (+12,794) | 1,855,962 (+25,793) |
| 2025-12-04 | 965,611 (+13,362) | 916,471 (+12,758) | 1,882,082 (+26,120) |
| 2025-12-05 | 977,996 (+12,385) | 930,616 (+14,145) | 1,908,612 (+26,530) |
| 2025-12-06 | 987,884 (+9,888) | 943,773 (+13,157) | 1,931,657 (+23,045) |
| 2025-12-07 | 994,046 (+6,162) | 951,425 (+7,652) | 1,945,471 (+13,814) |
| 2025-12-08 | 1,000,898 (+6,852) | 957,149 (+5,724) | 1,958,047 (+12,576) |
| 2025-12-09 | 1,011,488 (+10,590) | 973,922 (+16,773) | 1,985,410 (+27,363) |
| 2025-12-10 | 1,025,891 (+14,403) | 991,708 (+17,786) | 2,017,599 (+32,189) |
| 2025-12-11 | 1,045,110 (+19,219) | 1,010,559 (+18,851) | 2,055,669 (+38,070) |
| 2025-12-12 | 1,061,340 (+16,230) | 1,030,838 (+20,279) | 2,092,178 (+36,509) |
| 2025-12-13 | 1,073,561 (+12,221) | 1,044,608 (+13,770) | 2,118,169 (+25,991) |
| 2025-12-14 | 1,082,042 (+8,481) | 1,052,425 (+7,817) | 2,134,467 (+16,298) |
| 2025-12-15 | 1,093,632 (+11,590) | 1,059,078 (+6,653) | 2,152,710 (+18,243) |
| 2025-12-16 | 1,120,477 (+26,845) | 1,078,022 (+18,944) | 2,198,499 (+45,789) |
| 2025-12-17 | 1,151,067 (+30,590) | 1,097,661 (+19,639) | 2,248,728 (+50,229) |
| 2025-12-18 | 1,178,658 (+27,591) | 1,113,418 (+15,757) | 2,292,076 (+43,348) |
| 2025-12-19 | 1,203,485 (+24,827) | 1,129,698 (+16,280) | 2,333,183 (+41,107) |
| 2025-12-20 | 1,223,000 (+19,515) | 1,146,258 (+16,560) | 2,369,258 (+36,075) |
| 2025-12-21 | 1,242,675 (+19,675) | 1,158,909 (+12,651) | 2,401,584 (+32,326) |
| Date | GitHub Downloads | npm Downloads | Total |
| ---------- | -------------------- | ------------------- | -------------------- |
| 2025-06-29 | 18,789 (+0) | 39,420 (+0) | 58,209 (+0) |
| 2025-06-30 | 20,127 (+1,338) | 41,059 (+1,639) | 61,186 (+2,977) |
| 2025-07-01 | 22,108 (+1,981) | 43,745 (+2,686) | 65,853 (+4,667) |
| 2025-07-02 | 24,814 (+2,706) | 46,168 (+2,423) | 70,982 (+5,129) |
| 2025-07-03 | 27,834 (+3,020) | 49,955 (+3,787) | 77,789 (+6,807) |
| 2025-07-04 | 30,608 (+2,774) | 54,758 (+4,803) | 85,366 (+7,577) |
| 2025-07-05 | 32,524 (+1,916) | 58,371 (+3,613) | 90,895 (+5,529) |
| 2025-07-06 | 33,766 (+1,242) | 59,694 (+1,323) | 93,460 (+2,565) |
| 2025-07-08 | 38,052 (+4,286) | 64,468 (+4,774) | 102,520 (+9,060) |
| 2025-07-09 | 40,924 (+2,872) | 67,935 (+3,467) | 108,859 (+6,339) |
| 2025-07-10 | 43,796 (+2,872) | 71,402 (+3,467) | 115,198 (+6,339) |
| 2025-07-11 | 46,982 (+3,186) | 77,462 (+6,060) | 124,444 (+9,246) |
| 2025-07-12 | 49,302 (+2,320) | 82,177 (+4,715) | 131,479 (+7,035) |
| 2025-07-13 | 50,803 (+1,501) | 86,394 (+4,217) | 137,197 (+5,718) |
| 2025-07-14 | 53,283 (+2,480) | 87,860 (+1,466) | 141,143 (+3,946) |
| 2025-07-15 | 57,590 (+4,307) | 91,036 (+3,176) | 148,626 (+7,483) |
| 2025-07-16 | 62,313 (+4,723) | 95,258 (+4,222) | 157,571 (+8,945) |
| 2025-07-17 | 66,684 (+4,371) | 100,048 (+4,790) | 166,732 (+9,161) |
| 2025-07-18 | 70,379 (+3,695) | 102,587 (+2,539) | 172,966 (+6,234) |
| 2025-07-19 | 73,497 (+3,117) | 105,904 (+3,317) | 179,401 (+6,434) |
| 2025-07-20 | 76,453 (+2,956) | 109,044 (+3,140) | 185,497 (+6,096) |
| 2025-07-21 | 80,197 (+3,744) | 113,537 (+4,493) | 193,734 (+8,237) |
| 2025-07-22 | 84,251 (+4,054) | 118,073 (+4,536) | 202,324 (+8,590) |
| 2025-07-23 | 88,589 (+4,338) | 121,436 (+3,363) | 210,025 (+7,701) |
| 2025-07-24 | 92,469 (+3,880) | 124,091 (+2,655) | 216,560 (+6,535) |
| 2025-07-25 | 96,417 (+3,948) | 126,985 (+2,894) | 223,402 (+6,842) |
| 2025-07-26 | 100,646 (+4,229) | 131,411 (+4,426) | 232,057 (+8,655) |
| 2025-07-27 | 102,644 (+1,998) | 134,736 (+3,325) | 237,380 (+5,323) |
| 2025-07-28 | 105,446 (+2,802) | 136,016 (+1,280) | 241,462 (+4,082) |
| 2025-07-29 | 108,998 (+3,552) | 137,542 (+1,526) | 246,540 (+5,078) |
| 2025-07-30 | 113,544 (+4,546) | 140,317 (+2,775) | 253,861 (+7,321) |
| 2025-07-31 | 118,339 (+4,795) | 143,344 (+3,027) | 261,683 (+7,822) |
| 2025-08-01 | 123,539 (+5,200) | 146,680 (+3,336) | 270,219 (+8,536) |
| 2025-08-02 | 127,864 (+4,325) | 149,236 (+2,556) | 277,100 (+6,881) |
| 2025-08-03 | 131,397 (+3,533) | 150,451 (+1,215) | 281,848 (+4,748) |
| 2025-08-04 | 136,266 (+4,869) | 153,260 (+2,809) | 289,526 (+7,678) |
| 2025-08-05 | 141,596 (+5,330) | 155,752 (+2,492) | 297,348 (+7,822) |
| 2025-08-06 | 147,067 (+5,471) | 158,309 (+2,557) | 305,376 (+8,028) |
| 2025-08-07 | 152,591 (+5,524) | 160,889 (+2,580) | 313,480 (+8,104) |
| 2025-08-08 | 158,187 (+5,596) | 163,448 (+2,559) | 321,635 (+8,155) |
| 2025-08-09 | 162,770 (+4,583) | 165,721 (+2,273) | 328,491 (+6,856) |
| 2025-08-10 | 165,695 (+2,925) | 167,109 (+1,388) | 332,804 (+4,313) |
| 2025-08-11 | 169,297 (+3,602) | 167,953 (+844) | 337,250 (+4,446) |
| 2025-08-12 | 176,307 (+7,010) | 171,876 (+3,923) | 348,183 (+10,933) |
| 2025-08-13 | 182,997 (+6,690) | 177,182 (+5,306) | 360,179 (+11,996) |
| 2025-08-14 | 189,063 (+6,066) | 179,741 (+2,559) | 368,804 (+8,625) |
| 2025-08-15 | 193,608 (+4,545) | 181,792 (+2,051) | 375,400 (+6,596) |
| 2025-08-16 | 198,118 (+4,510) | 184,558 (+2,766) | 382,676 (+7,276) |
| 2025-08-17 | 201,299 (+3,181) | 186,269 (+1,711) | 387,568 (+4,892) |
| 2025-08-18 | 204,559 (+3,260) | 187,399 (+1,130) | 391,958 (+4,390) |
| 2025-08-19 | 209,814 (+5,255) | 189,668 (+2,269) | 399,482 (+7,524) |
| 2025-08-20 | 214,497 (+4,683) | 191,481 (+1,813) | 405,978 (+6,496) |
| 2025-08-21 | 220,465 (+5,968) | 194,784 (+3,303) | 415,249 (+9,271) |
| 2025-08-22 | 225,899 (+5,434) | 197,204 (+2,420) | 423,103 (+7,854) |
| 2025-08-23 | 229,005 (+3,106) | 199,238 (+2,034) | 428,243 (+5,140) |
| 2025-08-24 | 232,098 (+3,093) | 201,157 (+1,919) | 433,255 (+5,012) |
| 2025-08-25 | 236,607 (+4,509) | 202,650 (+1,493) | 439,257 (+6,002) |
| 2025-08-26 | 242,783 (+6,176) | 205,242 (+2,592) | 448,025 (+8,768) |
| 2025-08-27 | 248,409 (+5,626) | 205,242 (+0) | 453,651 (+5,626) |
| 2025-08-28 | 252,796 (+4,387) | 205,242 (+0) | 458,038 (+4,387) |
| 2025-08-29 | 256,045 (+3,249) | 211,075 (+5,833) | 467,120 (+9,082) |
| 2025-08-30 | 258,863 (+2,818) | 212,397 (+1,322) | 471,260 (+4,140) |
| 2025-08-31 | 262,004 (+3,141) | 213,944 (+1,547) | 475,948 (+4,688) |
| 2025-09-01 | 265,359 (+3,355) | 215,115 (+1,171) | 480,474 (+4,526) |
| 2025-09-02 | 270,483 (+5,124) | 217,075 (+1,960) | 487,558 (+7,084) |
| 2025-09-03 | 274,793 (+4,310) | 219,755 (+2,680) | 494,548 (+6,990) |
| 2025-09-04 | 280,430 (+5,637) | 222,103 (+2,348) | 502,533 (+7,985) |
| 2025-09-05 | 283,769 (+3,339) | 223,793 (+1,690) | 507,562 (+5,029) |
| 2025-09-06 | 286,245 (+2,476) | 225,036 (+1,243) | 511,281 (+3,719) |
| 2025-09-07 | 288,623 (+2,378) | 225,866 (+830) | 514,489 (+3,208) |
| 2025-09-08 | 293,341 (+4,718) | 227,073 (+1,207) | 520,414 (+5,925) |
| 2025-09-09 | 300,036 (+6,695) | 229,788 (+2,715) | 529,824 (+9,410) |
| 2025-09-10 | 307,287 (+7,251) | 233,435 (+3,647) | 540,722 (+10,898) |
| 2025-09-11 | 314,083 (+6,796) | 237,356 (+3,921) | 551,439 (+10,717) |
| 2025-09-12 | 321,046 (+6,963) | 240,728 (+3,372) | 561,774 (+10,335) |
| 2025-09-13 | 324,894 (+3,848) | 245,539 (+4,811) | 570,433 (+8,659) |
| 2025-09-14 | 328,876 (+3,982) | 248,245 (+2,706) | 577,121 (+6,688) |
| 2025-09-15 | 334,201 (+5,325) | 250,983 (+2,738) | 585,184 (+8,063) |
| 2025-09-16 | 342,609 (+8,408) | 255,264 (+4,281) | 597,873 (+12,689) |
| 2025-09-17 | 351,117 (+8,508) | 260,970 (+5,706) | 612,087 (+14,214) |
| 2025-09-18 | 358,717 (+7,600) | 266,922 (+5,952) | 625,639 (+13,552) |
| 2025-09-19 | 365,401 (+6,684) | 271,859 (+4,937) | 637,260 (+11,621) |
| 2025-09-20 | 372,092 (+6,691) | 276,917 (+5,058) | 649,009 (+11,749) |
| 2025-09-21 | 377,079 (+4,987) | 280,261 (+3,344) | 657,340 (+8,331) |
| 2025-09-22 | 382,492 (+5,413) | 284,009 (+3,748) | 666,501 (+9,161) |
| 2025-09-23 | 387,008 (+4,516) | 289,129 (+5,120) | 676,137 (+9,636) |
| 2025-09-24 | 393,325 (+6,317) | 294,927 (+5,798) | 688,252 (+12,115) |
| 2025-09-25 | 398,879 (+5,554) | 301,663 (+6,736) | 700,542 (+12,290) |
| 2025-09-26 | 404,334 (+5,455) | 306,713 (+5,050) | 711,047 (+10,505) |
| 2025-09-27 | 411,618 (+7,284) | 317,763 (+11,050) | 729,381 (+18,334) |
| 2025-09-28 | 414,910 (+3,292) | 322,522 (+4,759) | 737,432 (+8,051) |
| 2025-09-29 | 419,919 (+5,009) | 328,033 (+5,511) | 747,952 (+10,520) |
| 2025-09-30 | 427,991 (+8,072) | 336,472 (+8,439) | 764,463 (+16,511) |
| 2025-10-01 | 433,591 (+5,600) | 341,742 (+5,270) | 775,333 (+10,870) |
| 2025-10-02 | 440,852 (+7,261) | 348,099 (+6,357) | 788,951 (+13,618) |
| 2025-10-03 | 446,829 (+5,977) | 359,937 (+11,838) | 806,766 (+17,815) |
| 2025-10-04 | 452,561 (+5,732) | 370,386 (+10,449) | 822,947 (+16,181) |
| 2025-10-05 | 455,559 (+2,998) | 374,745 (+4,359) | 830,304 (+7,357) |
| 2025-10-06 | 460,927 (+5,368) | 379,489 (+4,744) | 840,416 (+10,112) |
| 2025-10-07 | 467,336 (+6,409) | 385,438 (+5,949) | 852,774 (+12,358) |
| 2025-10-08 | 474,643 (+7,307) | 394,139 (+8,701) | 868,782 (+16,008) |
| 2025-10-09 | 479,203 (+4,560) | 400,526 (+6,387) | 879,729 (+10,947) |
| 2025-10-10 | 484,374 (+5,171) | 406,015 (+5,489) | 890,389 (+10,660) |
| 2025-10-11 | 488,427 (+4,053) | 414,699 (+8,684) | 903,126 (+12,737) |
| 2025-10-12 | 492,125 (+3,698) | 418,745 (+4,046) | 910,870 (+7,744) |
| 2025-10-14 | 505,130 (+13,005) | 429,286 (+10,541) | 934,416 (+23,546) |
| 2025-10-15 | 512,717 (+7,587) | 439,290 (+10,004) | 952,007 (+17,591) |
| 2025-10-16 | 517,719 (+5,002) | 447,137 (+7,847) | 964,856 (+12,849) |
| 2025-10-17 | 526,239 (+8,520) | 457,467 (+10,330) | 983,706 (+18,850) |
| 2025-10-18 | 531,564 (+5,325) | 465,272 (+7,805) | 996,836 (+13,130) |
| 2025-10-19 | 536,209 (+4,645) | 469,078 (+3,806) | 1,005,287 (+8,451) |
| 2025-10-20 | 541,264 (+5,055) | 472,952 (+3,874) | 1,014,216 (+8,929) |
| 2025-10-21 | 548,721 (+7,457) | 479,703 (+6,751) | 1,028,424 (+14,208) |
| 2025-10-22 | 557,949 (+9,228) | 491,395 (+11,692) | 1,049,344 (+20,920) |
| 2025-10-23 | 564,716 (+6,767) | 498,736 (+7,341) | 1,063,452 (+14,108) |
| 2025-10-24 | 572,692 (+7,976) | 506,905 (+8,169) | 1,079,597 (+16,145) |
| 2025-10-25 | 578,927 (+6,235) | 516,129 (+9,224) | 1,095,056 (+15,459) |
| 2025-10-26 | 584,409 (+5,482) | 521,179 (+5,050) | 1,105,588 (+10,532) |
| 2025-10-27 | 589,999 (+5,590) | 526,001 (+4,822) | 1,116,000 (+10,412) |
| 2025-10-28 | 595,776 (+5,777) | 532,438 (+6,437) | 1,128,214 (+12,214) |
| 2025-10-29 | 606,259 (+10,483) | 542,064 (+9,626) | 1,148,323 (+20,109) |
| 2025-10-30 | 613,746 (+7,487) | 542,064 (+0) | 1,155,810 (+7,487) |
| 2025-10-30 | 617,846 (+4,100) | 555,026 (+12,962) | 1,172,872 (+17,062) |
| 2025-10-31 | 626,612 (+8,766) | 564,579 (+9,553) | 1,191,191 (+18,319) |
| 2025-11-01 | 636,100 (+9,488) | 581,806 (+17,227) | 1,217,906 (+26,715) |
| 2025-11-02 | 644,067 (+7,967) | 590,004 (+8,198) | 1,234,071 (+16,165) |
| 2025-11-03 | 653,130 (+9,063) | 597,139 (+7,135) | 1,250,269 (+16,198) |
| 2025-11-04 | 663,912 (+10,782) | 608,056 (+10,917) | 1,271,968 (+21,699) |
| 2025-11-05 | 675,074 (+11,162) | 619,690 (+11,634) | 1,294,764 (+22,796) |
| 2025-11-06 | 686,252 (+11,178) | 630,885 (+11,195) | 1,317,137 (+22,373) |
| 2025-11-07 | 696,646 (+10,394) | 642,146 (+11,261) | 1,338,792 (+21,655) |
| 2025-11-08 | 706,035 (+9,389) | 653,489 (+11,343) | 1,359,524 (+20,732) |
| 2025-11-09 | 713,462 (+7,427) | 660,459 (+6,970) | 1,373,921 (+14,397) |
| 2025-11-10 | 722,288 (+8,826) | 668,225 (+7,766) | 1,390,513 (+16,592) |
| 2025-11-11 | 729,769 (+7,481) | 677,501 (+9,276) | 1,407,270 (+16,757) |
| 2025-11-12 | 740,180 (+10,411) | 686,454 (+8,953) | 1,426,634 (+19,364) |
| 2025-11-13 | 749,905 (+9,725) | 696,157 (+9,703) | 1,446,062 (+19,428) |
| 2025-11-14 | 759,928 (+10,023) | 705,237 (+9,080) | 1,465,165 (+19,103) |
| 2025-11-15 | 765,955 (+6,027) | 712,870 (+7,633) | 1,478,825 (+13,660) |
| 2025-11-16 | 771,069 (+5,114) | 716,596 (+3,726) | 1,487,665 (+8,840) |
| 2025-11-17 | 780,161 (+9,092) | 723,339 (+6,743) | 1,503,500 (+15,835) |
| 2025-11-18 | 791,563 (+11,402) | 732,544 (+9,205) | 1,524,107 (+20,607) |
| 2025-11-19 | 804,409 (+12,846) | 747,624 (+15,080) | 1,552,033 (+27,926) |
| 2025-11-20 | 814,620 (+10,211) | 757,907 (+10,283) | 1,572,527 (+20,494) |
| 2025-11-21 | 826,309 (+11,689) | 769,307 (+11,400) | 1,595,616 (+23,089) |
| 2025-11-22 | 837,269 (+10,960) | 780,996 (+11,689) | 1,618,265 (+22,649) |
| 2025-11-23 | 846,609 (+9,340) | 795,069 (+14,073) | 1,641,678 (+23,413) |
| 2025-11-24 | 856,733 (+10,124) | 804,033 (+8,964) | 1,660,766 (+19,088) |
| 2025-11-25 | 869,423 (+12,690) | 817,339 (+13,306) | 1,686,762 (+25,996) |
| 2025-11-26 | 881,414 (+11,991) | 832,518 (+15,179) | 1,713,932 (+27,170) |
| 2025-11-27 | 893,960 (+12,546) | 846,180 (+13,662) | 1,740,140 (+26,208) |
| 2025-11-28 | 901,741 (+7,781) | 856,482 (+10,302) | 1,758,223 (+18,083) |
| 2025-11-29 | 908,689 (+6,948) | 863,361 (+6,879) | 1,772,050 (+13,827) |
| 2025-11-30 | 916,116 (+7,427) | 870,194 (+6,833) | 1,786,310 (+14,260) |
| 2025-12-01 | 925,898 (+9,782) | 876,500 (+6,306) | 1,802,398 (+16,088) |
| 2025-12-02 | 939,250 (+13,352) | 890,919 (+14,419) | 1,830,169 (+27,771) |
| 2025-12-03 | 952,249 (+12,999) | 903,713 (+12,794) | 1,855,962 (+25,793) |
| 2025-12-04 | 965,611 (+13,362) | 916,471 (+12,758) | 1,882,082 (+26,120) |
| 2025-12-05 | 977,996 (+12,385) | 930,616 (+14,145) | 1,908,612 (+26,530) |
| 2025-12-06 | 987,884 (+9,888) | 943,773 (+13,157) | 1,931,657 (+23,045) |
| 2025-12-07 | 994,046 (+6,162) | 951,425 (+7,652) | 1,945,471 (+13,814) |
| 2025-12-08 | 1,000,898 (+6,852) | 957,149 (+5,724) | 1,958,047 (+12,576) |
| 2025-12-09 | 1,011,488 (+10,590) | 973,922 (+16,773) | 1,985,410 (+27,363) |
| 2025-12-10 | 1,025,891 (+14,403) | 991,708 (+17,786) | 2,017,599 (+32,189) |
| 2025-12-11 | 1,045,110 (+19,219) | 1,010,559 (+18,851) | 2,055,669 (+38,070) |
| 2025-12-12 | 1,061,340 (+16,230) | 1,030,838 (+20,279) | 2,092,178 (+36,509) |
| 2025-12-13 | 1,073,561 (+12,221) | 1,044,608 (+13,770) | 2,118,169 (+25,991) |
| 2025-12-14 | 1,082,042 (+8,481) | 1,052,425 (+7,817) | 2,134,467 (+16,298) |
| 2025-12-15 | 1,093,632 (+11,590) | 1,059,078 (+6,653) | 2,152,710 (+18,243) |
| 2025-12-16 | 1,120,477 (+26,845) | 1,078,022 (+18,944) | 2,198,499 (+45,789) |
| 2025-12-17 | 1,151,067 (+30,590) | 1,097,661 (+19,639) | 2,248,728 (+50,229) |
| 2025-12-18 | 1,178,658 (+27,591) | 1,113,418 (+15,757) | 2,292,076 (+43,348) |
| 2025-12-19 | 1,203,485 (+24,827) | 1,129,698 (+16,280) | 2,333,183 (+41,107) |
| 2025-12-20 | 1,223,000 (+19,515) | 1,146,258 (+16,560) | 2,369,258 (+36,075) |
| 2025-12-21 | 1,242,675 (+19,675) | 1,158,909 (+12,651) | 2,401,584 (+32,326) |
| 2025-12-22 | 1,262,522 (+19,847) | 1,169,121 (+10,212) | 2,431,643 (+30,059) |
| 2025-12-23 | 1,286,548 (+24,026) | 1,186,439 (+17,318) | 2,472,987 (+41,344) |
| 2025-12-24 | 1,309,323 (+22,775) | 1,203,767 (+17,328) | 2,513,090 (+40,103) |
| 2025-12-25 | 1,333,032 (+23,709) | 1,217,283 (+13,516) | 2,550,315 (+37,225) |
| 2025-12-26 | 1,352,411 (+19,379) | 1,227,615 (+10,332) | 2,580,026 (+29,711) |
| 2025-12-27 | 1,371,771 (+19,360) | 1,238,236 (+10,621) | 2,610,007 (+29,981) |
| 2025-12-28 | 1,390,388 (+18,617) | 1,245,690 (+7,454) | 2,636,078 (+26,071) |
| 2025-12-29 | 1,415,560 (+25,172) | 1,257,101 (+11,411) | 2,672,661 (+36,583) |
| 2025-12-30 | 1,445,450 (+29,890) | 1,272,689 (+15,588) | 2,718,139 (+45,478) |
| 2025-12-31 | 1,479,598 (+34,148) | 1,293,235 (+20,546) | 2,772,833 (+54,694) |
| 2026-01-01 | 1,508,883 (+29,285) | 1,309,874 (+16,639) | 2,818,757 (+45,924) |
| 2026-01-02 | 1,563,474 (+54,591) | 1,320,959 (+11,085) | 2,884,433 (+65,676) |
| 2026-01-03 | 1,618,065 (+54,591) | 1,331,914 (+10,955) | 2,949,979 (+65,546) |
| 2026-01-04 | 1,672,656 (+39,702) | 1,339,883 (+7,969) | 3,012,539 (+62,560) |
| 2026-01-05 | 1,738,171 (+65,515) | 1,353,043 (+13,160) | 3,091,214 (+78,675) |
| 2026-01-06 | 1,960,988 (+222,817) | 1,377,377 (+24,334) | 3,338,365 (+247,151) |
| 2026-01-07 | 2,123,239 (+162,251) | 1,398,648 (+21,271) | 3,521,887 (+183,522) |
| 2026-01-08 | 2,272,630 (+149,391) | 1,432,480 (+33,832) | 3,705,110 (+183,223) |
| 2026-01-09 | 2,443,565 (+170,935) | 1,469,451 (+36,971) | 3,913,016 (+207,906) |
| 2026-01-10 | 2,632,023 (+188,458) | 1,503,670 (+34,219) | 4,135,693 (+222,677) |
| 2026-01-11 | 2,836,394 (+204,371) | 1,530,479 (+26,809) | 4,366,873 (+231,180) |
| 2026-01-12 | 3,053,594 (+217,200) | 1,553,671 (+23,192) | 4,607,265 (+240,392) |
| 2026-01-13 | 3,297,078 (+243,484) | 1,595,062 (+41,391) | 4,892,140 (+284,875) |
+69 -10
View File
@@ -1,12 +1,71 @@
## Style Guide
- Try to keep things in one function unless composable or reusable
- DO NOT do unnecessary destructuring of variables
- DO NOT use `else` statements unless necessary
- DO NOT use `try`/`catch` if it can be avoided
- AVOID `try`/`catch` where possible
- AVOID `else` statements
- AVOID using `any` type
- AVOID `let` statements
- PREFER single word variable names where possible
- Use as many bun apis as possible like Bun.file()
- Keep things in one function unless composable or reusable
- Avoid unnecessary destructuring. Instead of `const { a, b } = obj`, use `obj.a` and `obj.b` to preserve context
- Avoid `try`/`catch` where possible
- Avoid using the `any` type
- Prefer single word variable names where possible
- Use Bun APIs when possible, like `Bun.file()`
# Avoid let statements
We don't like `let` statements, especially combined with if/else statements.
Prefer `const`.
Good:
```ts
const foo = condition ? 1 : 2
```
Bad:
```ts
let foo
if (condition) foo = 1
else foo = 2
```
# Avoid else statements
Prefer early returns or using an `iife` to avoid else statements.
Good:
```ts
function foo() {
if (condition) return 1
return 2
}
```
Bad:
```ts
function foo() {
if (condition) return 1
else return 2
}
```
# Prefer single word naming
Try your best to find a single word name for your variables, functions, etc.
Only use multiple words if you cannot.
Good:
```ts
const foo = 1
const bar = 2
const baz = 3
```
Bad:
```ts
const fooBar = 1
const barBaz = 2
const bazFoo = 3
```
+431 -358
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -1,2 +1,6 @@
[install]
exact = true
[test]
root = "./do-not-run-tests-from-root"
Generated
+3 -3
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1766125104,
"narHash": "sha256-l/YGrEpLromL4viUo5GmFH3K5M1j0Mb9O+LiaeCPWEM=",
"lastModified": 1768178648,
"narHash": "sha256-kz/F6mhESPvU1diB7tOM3nLcBfQe7GU7GQCymRlTi/s=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7d853e518814cca2a657b72eeba67ae20ebf7059",
"rev": "3fbab70c6e69c87ea2b6e48aa6629da2aa6a23b0",
"type": "github"
},
"original": {
+42 -10
View File
@@ -17,7 +17,7 @@
"aarch64-darwin"
"x86_64-darwin"
];
lib = nixpkgs.lib;
inherit (nixpkgs) lib;
forEachSystem = lib.genAttrs systems;
pkgsFor = system: nixpkgs.legacyPackages.${system};
packageJson = builtins.fromJSON (builtins.readFile ./packages/opencode/package.json);
@@ -27,11 +27,28 @@
"aarch64-darwin" = "bun-darwin-arm64";
"x86_64-darwin" = "bun-darwin-x64";
};
defaultNodeModules = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
# Parse "bun-{os}-{cpu}" to {os, cpu}
parseBunTarget =
target:
let
parts = lib.splitString "-" target;
in
{
os = builtins.elemAt parts 1;
cpu = builtins.elemAt parts 2;
};
hashesFile = "${./nix}/hashes.json";
hashesData =
if builtins.pathExists hashesFile then builtins.fromJSON (builtins.readFile hashesFile) else { };
nodeModulesHash = hashesData.nodeModules or defaultNodeModules;
# Lookup hash: supports per-system ({system: hash}) or legacy single hash
nodeModulesHashFor =
system:
if builtins.isAttrs hashesData.nodeModules then
hashesData.nodeModules.${system}
else
hashesData.nodeModules;
modelsDev = forEachSystem (
system:
let
@@ -63,20 +80,35 @@
system:
let
pkgs = pkgsFor system;
bunPlatform = parseBunTarget bunTarget.${system};
mkNodeModules = pkgs.callPackage ./nix/node-modules.nix {
hash = nodeModulesHash;
hash = nodeModulesHashFor system;
bunCpu = bunPlatform.cpu;
bunOs = bunPlatform.os;
};
mkPackage = pkgs.callPackage ./nix/opencode.nix { };
in
{
default = mkPackage {
version = packageJson.version;
mkOpencode = pkgs.callPackage ./nix/opencode.nix { };
mkDesktop = pkgs.callPackage ./nix/desktop.nix { };
opencodePkg = mkOpencode {
inherit (packageJson) version;
src = ./.;
scripts = ./nix/scripts;
target = bunTarget.${system};
modelsDev = "${modelsDev.${system}}/dist/_api.json";
mkNodeModules = mkNodeModules;
inherit mkNodeModules;
};
desktopPkg = mkDesktop {
inherit (packageJson) version;
src = ./.;
scripts = ./nix/scripts;
mkNodeModules = mkNodeModules;
opencode = opencodePkg;
};
in
{
default = opencodePkg;
desktop = desktopPkg;
}
);
+8 -7
View File
@@ -81,13 +81,14 @@ This will walk you through installing the GitHub app, creating the workflow, and
permissions:
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 1
persist-credentials: false
- name: Run opencode
uses: sst/opencode/github@latest
- name: Run opencode
uses: anomalyco/opencode/github@latest
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
with:
@@ -98,7 +99,7 @@ This will walk you through installing the GitHub app, creating the workflow, and
## Support
This is an early release. If you encounter issues or have feedback, please create an issue at https://github.com/sst/opencode/issues.
This is an early release. If you encounter issues or have feedback, please create an issue at https://github.com/anomalyco/opencode/issues.
## Development
+1 -1
View File
@@ -41,7 +41,7 @@ runs:
id: version
shell: bash
run: |
VERSION=$(curl -sf https://api.github.com/repos/sst/opencode/releases/latest | grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4)
VERSION=$(curl -sf https://api.github.com/repos/anomalyco/opencode/releases/latest | grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4)
echo "version=${VERSION:-latest}" >> $GITHUB_OUTPUT
- name: Cache opencode
+1 -1
View File
@@ -281,7 +281,7 @@ async function assertOpencodeConnected() {
connected = true
break
} catch (e) {}
await new Promise((resolve) => setTimeout(resolve, 300))
await Bun.sleep(300)
} while (retry++ < 30)
if (!connected) {
+1
View File
@@ -3,6 +3,7 @@
"module": "index.ts",
"type": "module",
"private": true,
"license": "MIT",
"devDependencies": {
"@types/bun": "catalog:"
},
+9
View File
@@ -44,3 +44,12 @@ new sst.cloudflare.x.Astro("Web", {
VITE_API_URL: api.url.apply((url) => url!),
},
})
new sst.cloudflare.StaticSite("WebApp", {
domain: "app." + domain,
path: "packages/app",
build: {
command: "bun turbo build",
output: "./dist",
},
})
+21
View File
@@ -76,6 +76,7 @@ export const stripeWebhook = new stripe.WebhookEndpoint("StripeWebhookEndpoint",
"checkout.session.completed",
"checkout.session.expired",
"charge.refunded",
"invoice.payment_succeeded",
"customer.created",
"customer.deleted",
"customer.updated",
@@ -97,13 +98,29 @@ export const stripeWebhook = new stripe.WebhookEndpoint("StripeWebhookEndpoint",
],
})
const zenProduct = new stripe.Product("ZenBlack", {
name: "OpenCode Black",
})
const zenPrice = new stripe.Price("ZenBlackPrice", {
product: zenProduct.id,
unitAmount: 20000,
currency: "usd",
recurring: {
interval: "month",
intervalCount: 1,
},
})
const ZEN_MODELS = [
new sst.Secret("ZEN_MODELS1"),
new sst.Secret("ZEN_MODELS2"),
new sst.Secret("ZEN_MODELS3"),
new sst.Secret("ZEN_MODELS4"),
new sst.Secret("ZEN_MODELS5"),
new sst.Secret("ZEN_MODELS6"),
new sst.Secret("ZEN_MODELS7"),
]
const ZEN_BLACK = new sst.Secret("ZEN_BLACK")
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
properties: { value: auth.url.apply((url) => url!) },
@@ -118,6 +135,7 @@ const gatewayKv = new sst.cloudflare.Kv("GatewayKv")
////////////////
const bucket = new sst.cloudflare.Bucket("ZenData")
const bucketNew = new sst.cloudflare.Bucket("ZenDataNew")
const AWS_SES_ACCESS_KEY_ID = new sst.Secret("AWS_SES_ACCESS_KEY_ID")
const AWS_SES_SECRET_ACCESS_KEY = new sst.Secret("AWS_SES_SECRET_ACCESS_KEY")
@@ -136,6 +154,7 @@ new sst.cloudflare.x.SolidStart("Console", {
path: "packages/console/app",
link: [
bucket,
bucketNew,
database,
AUTH_API_URL,
STRIPE_WEBHOOK_SECRET,
@@ -143,6 +162,8 @@ new sst.cloudflare.x.SolidStart("Console", {
EMAILOCTOPUS_API_KEY,
AWS_SES_ACCESS_KEY_ID,
AWS_SES_SECRET_ACCESS_KEY,
ZEN_BLACK,
new sst.Secret("ZEN_SESSION_SECRET"),
...ZEN_MODELS,
...($dev
? [
-10
View File
@@ -1,10 +0,0 @@
import { domain } from "./stage"
new sst.cloudflare.StaticSite("Desktop", {
domain: "desktop." + domain,
path: "packages/desktop",
build: {
command: "bun turbo build",
output: "./dist",
},
})
+225 -144
View File
@@ -7,112 +7,187 @@ RED='\033[0;31m'
ORANGE='\033[38;5;214m'
NC='\033[0m' # No Color
usage() {
cat <<EOF
OpenCode Installer
Usage: install.sh [options]
Options:
-h, --help Display this help message
-v, --version <version> Install a specific version (e.g., 1.0.180)
-b, --binary <path> Install from a local binary instead of downloading
--no-modify-path Don't modify shell config files (.zshrc, .bashrc, etc.)
Examples:
curl -fsSL https://opencode.ai/install | bash
curl -fsSL https://opencode.ai/install | bash -s -- --version 1.0.180
./install --binary /path/to/opencode
EOF
}
requested_version=${VERSION:-}
no_modify_path=false
binary_path=""
raw_os=$(uname -s)
os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]')
case "$raw_os" in
Darwin*) os="darwin" ;;
Linux*) os="linux" ;;
MINGW*|MSYS*|CYGWIN*) os="windows" ;;
esac
arch=$(uname -m)
if [[ "$arch" == "aarch64" ]]; then
arch="arm64"
fi
if [[ "$arch" == "x86_64" ]]; then
arch="x64"
fi
if [ "$os" = "darwin" ] && [ "$arch" = "x64" ]; then
rosetta_flag=$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)
if [ "$rosetta_flag" = "1" ]; then
arch="arm64"
fi
fi
combo="$os-$arch"
case "$combo" in
linux-x64|linux-arm64|darwin-x64|darwin-arm64|windows-x64)
;;
*)
echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}"
exit 1
;;
esac
archive_ext=".zip"
if [ "$os" = "linux" ]; then
archive_ext=".tar.gz"
fi
is_musl=false
if [ "$os" = "linux" ]; then
if [ -f /etc/alpine-release ]; then
is_musl=true
fi
if command -v ldd >/dev/null 2>&1; then
if ldd --version 2>&1 | grep -qi musl; then
is_musl=true
fi
fi
fi
needs_baseline=false
if [ "$arch" = "x64" ]; then
if [ "$os" = "linux" ]; then
if ! grep -qi avx2 /proc/cpuinfo 2>/dev/null; then
needs_baseline=true
fi
fi
if [ "$os" = "darwin" ]; then
avx2=$(sysctl -n hw.optional.avx2_0 2>/dev/null || echo 0)
if [ "$avx2" != "1" ]; then
needs_baseline=true
fi
fi
fi
target="$os-$arch"
if [ "$needs_baseline" = "true" ]; then
target="$target-baseline"
fi
if [ "$is_musl" = "true" ]; then
target="$target-musl"
fi
filename="$APP-$target$archive_ext"
if [ "$os" = "linux" ]; then
if ! command -v tar >/dev/null 2>&1; then
echo -e "${RED}Error: 'tar' is required but not installed.${NC}"
exit 1
fi
else
if ! command -v unzip >/dev/null 2>&1; then
echo -e "${RED}Error: 'unzip' is required but not installed.${NC}"
exit 1
fi
fi
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
-v|--version)
if [[ -n "${2:-}" ]]; then
requested_version="$2"
shift 2
else
echo -e "${RED}Error: --version requires a version argument${NC}"
exit 1
fi
;;
-b|--binary)
if [[ -n "${2:-}" ]]; then
binary_path="$2"
shift 2
else
echo -e "${RED}Error: --binary requires a path argument${NC}"
exit 1
fi
;;
--no-modify-path)
no_modify_path=true
shift
;;
*)
echo -e "${ORANGE}Warning: Unknown option '$1'${NC}" >&2
shift
;;
esac
done
INSTALL_DIR=$HOME/.opencode/bin
mkdir -p "$INSTALL_DIR"
if [ -z "$requested_version" ]; then
url="https://github.com/sst/opencode/releases/latest/download/$filename"
specific_version=$(curl -s https://api.github.com/repos/sst/opencode/releases/latest | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
if [[ $? -ne 0 || -z "$specific_version" ]]; then
echo -e "${RED}Failed to fetch version information${NC}"
# If --binary is provided, skip all download/detection logic
if [ -n "$binary_path" ]; then
if [ ! -f "$binary_path" ]; then
echo -e "${RED}Error: Binary not found at ${binary_path}${NC}"
exit 1
fi
specific_version="local"
else
url="https://github.com/sst/opencode/releases/download/v${requested_version}/$filename"
specific_version=$requested_version
raw_os=$(uname -s)
os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]')
case "$raw_os" in
Darwin*) os="darwin" ;;
Linux*) os="linux" ;;
MINGW*|MSYS*|CYGWIN*) os="windows" ;;
esac
arch=$(uname -m)
if [[ "$arch" == "aarch64" ]]; then
arch="arm64"
fi
if [[ "$arch" == "x86_64" ]]; then
arch="x64"
fi
if [ "$os" = "darwin" ] && [ "$arch" = "x64" ]; then
rosetta_flag=$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)
if [ "$rosetta_flag" = "1" ]; then
arch="arm64"
fi
fi
combo="$os-$arch"
case "$combo" in
linux-x64|linux-arm64|darwin-x64|darwin-arm64|windows-x64)
;;
*)
echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}"
exit 1
;;
esac
archive_ext=".zip"
if [ "$os" = "linux" ]; then
archive_ext=".tar.gz"
fi
is_musl=false
if [ "$os" = "linux" ]; then
if [ -f /etc/alpine-release ]; then
is_musl=true
fi
if command -v ldd >/dev/null 2>&1; then
if ldd --version 2>&1 | grep -qi musl; then
is_musl=true
fi
fi
fi
needs_baseline=false
if [ "$arch" = "x64" ]; then
if [ "$os" = "linux" ]; then
if ! grep -qi avx2 /proc/cpuinfo 2>/dev/null; then
needs_baseline=true
fi
fi
if [ "$os" = "darwin" ]; then
avx2=$(sysctl -n hw.optional.avx2_0 2>/dev/null || echo 0)
if [ "$avx2" != "1" ]; then
needs_baseline=true
fi
fi
fi
target="$os-$arch"
if [ "$needs_baseline" = "true" ]; then
target="$target-baseline"
fi
if [ "$is_musl" = "true" ]; then
target="$target-musl"
fi
filename="$APP-$target$archive_ext"
if [ "$os" = "linux" ]; then
if ! command -v tar >/dev/null 2>&1; then
echo -e "${RED}Error: 'tar' is required but not installed.${NC}"
exit 1
fi
else
if ! command -v unzip >/dev/null 2>&1; then
echo -e "${RED}Error: 'unzip' is required but not installed.${NC}"
exit 1
fi
fi
if [ -z "$requested_version" ]; then
url="https://github.com/anomalyco/opencode/releases/latest/download/$filename"
specific_version=$(curl -s https://api.github.com/repos/anomalyco/opencode/releases/latest | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
if [[ $? -ne 0 || -z "$specific_version" ]]; then
echo -e "${RED}Failed to fetch version information${NC}"
exit 1
fi
else
# Strip leading 'v' if present
requested_version="${requested_version#v}"
url="https://github.com/anomalyco/opencode/releases/download/v${requested_version}/$filename"
specific_version=$requested_version
# Verify the release exists before downloading
http_status=$(curl -sI -o /dev/null -w "%{http_code}" "https://github.com/anomalyco/opencode/releases/tag/v${requested_version}")
if [ "$http_status" = "404" ]; then
echo -e "${RED}Error: Release v${requested_version} not found${NC}"
echo -e "${MUTED}Available releases: https://github.com/anomalyco/opencode/releases${NC}"
exit 1
fi
fi
fi
print_message() {
@@ -133,11 +208,8 @@ check_version() {
if command -v opencode >/dev/null 2>&1; then
opencode_path=$(which opencode)
## TODO: check if version is installed
# installed_version=$(opencode version)
installed_version="0.0.1"
installed_version=$(echo $installed_version | awk '{print $2}')
## Check the installed version
installed_version=$(opencode --version 2>/dev/null || echo "")
if [[ "$installed_version" != "$specific_version" ]]; then
print_message info "${MUTED}Installed version: ${NC}$installed_version."
@@ -213,11 +285,11 @@ download_with_progress() {
{
local length=0
local bytes=0
while IFS=" " read -r -a line; do
[ "${#line[@]}" -lt 2 ] && continue
local tag="${line[0]} ${line[1]}"
if [ "$tag" = "0000: content-length:" ]; then
length="${line[2]}"
length=$(echo "$length" | tr -d '\r')
@@ -242,7 +314,7 @@ download_and_install() {
print_message info "\n${MUTED}Installing ${NC}opencode ${MUTED}version: ${NC}$specific_version"
local tmp_dir="${TMPDIR:-/tmp}/opencode_install_$$"
mkdir -p "$tmp_dir"
if [[ "$os" == "windows" ]] || ! [ -t 2 ] || ! download_with_progress "$url" "$tmp_dir/$filename"; then
# Fallback to standard curl on Windows, non-TTY environments, or if custom progress fails
curl -# -L -o "$tmp_dir/$filename" "$url"
@@ -253,14 +325,24 @@ download_and_install() {
else
unzip -q "$tmp_dir/$filename" -d "$tmp_dir"
fi
mv "$tmp_dir/opencode" "$INSTALL_DIR"
chmod 755 "${INSTALL_DIR}/opencode"
rm -rf "$tmp_dir"
}
check_version
download_and_install
install_from_binary() {
print_message info "\n${MUTED}Installing ${NC}opencode ${MUTED}from: ${NC}$binary_path"
cp "$binary_path" "${INSTALL_DIR}/opencode"
chmod 755 "${INSTALL_DIR}/opencode"
}
if [ -n "$binary_path" ]; then
install_from_binary
else
check_version
download_and_install
fi
add_to_path() {
@@ -304,42 +386,42 @@ case $current_shell in
;;
esac
config_file=""
for file in $config_files; do
if [[ -f $file ]]; then
config_file=$file
break
if [[ "$no_modify_path" != "true" ]]; then
config_file=""
for file in $config_files; do
if [[ -f $file ]]; then
config_file=$file
break
fi
done
if [[ -z $config_file ]]; then
print_message warning "No config file found for $current_shell. You may need to manually add to PATH:"
print_message info " export PATH=$INSTALL_DIR:\$PATH"
elif [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
case $current_shell in
fish)
add_to_path "$config_file" "fish_add_path $INSTALL_DIR"
;;
zsh)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
bash)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
ash)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
sh)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
*)
export PATH=$INSTALL_DIR:$PATH
print_message warning "Manually add the directory to $config_file (or similar):"
print_message info " export PATH=$INSTALL_DIR:\$PATH"
;;
esac
fi
done
if [[ -z $config_file ]]; then
print_message error "No config file found for $current_shell. Checked files: ${config_files[@]}"
exit 1
fi
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
case $current_shell in
fish)
add_to_path "$config_file" "fish_add_path $INSTALL_DIR"
;;
zsh)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
bash)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
ash)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
sh)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
*)
export PATH=$INSTALL_DIR:$PATH
print_message warning "Manually add the directory to $config_file (or similar):"
print_message info " export PATH=$INSTALL_DIR:\$PATH"
;;
esac
fi
if [ -n "${GITHUB_ACTIONS-}" ] && [ "${GITHUB_ACTIONS}" == "true" ]; then
@@ -362,4 +444,3 @@ echo -e ""
echo -e "${MUTED}For more information visit ${NC}https://opencode.ai/docs"
echo -e ""
echo -e ""
+145
View File
@@ -0,0 +1,145 @@
{
lib,
stdenv,
rustPlatform,
bun,
pkg-config,
dbus ? null,
openssl,
glib ? null,
gtk3 ? null,
libsoup_3 ? null,
webkitgtk_4_1 ? null,
librsvg ? null,
libappindicator-gtk3 ? null,
cargo,
rustc,
makeBinaryWrapper,
nodejs,
jq,
}:
args:
let
scripts = args.scripts;
mkModules =
attrs:
args.mkNodeModules (
attrs
// {
canonicalizeScript = scripts + "/canonicalize-node-modules.ts";
normalizeBinsScript = scripts + "/normalize-bun-binaries.ts";
}
);
in
rustPlatform.buildRustPackage rec {
pname = "opencode-desktop";
version = args.version;
src = args.src;
# We need to set the root for cargo, but we also need access to the whole repo.
postUnpack = ''
# Update sourceRoot to point to the tauri app
sourceRoot+=/packages/desktop/src-tauri
'';
cargoLock = {
lockFile = ../packages/desktop/src-tauri/Cargo.lock;
allowBuiltinFetchGit = true;
};
node_modules = mkModules {
version = version;
src = src;
};
nativeBuildInputs = [
pkg-config
bun
makeBinaryWrapper
cargo
rustc
nodejs
jq
];
buildInputs = [
openssl
]
++ lib.optionals stdenv.isLinux [
dbus
glib
gtk3
libsoup_3
webkitgtk_4_1
librsvg
libappindicator-gtk3
];
preBuild = ''
# Restore node_modules
pushd ../../..
# Copy node_modules from the fixed-output derivation
# We use cp -r --no-preserve=mode to ensure we can write to them if needed,
# though we usually just read.
cp -r ${node_modules}/node_modules .
cp -r ${node_modules}/packages .
# Ensure node_modules is writable so patchShebangs can update script headers
chmod -R u+w node_modules
# Ensure workspace packages are writable for tsgo incremental outputs (.tsbuildinfo)
chmod -R u+w packages
# Patch shebangs so scripts can run
patchShebangs node_modules
# Copy sidecar
mkdir -p packages/desktop/src-tauri/sidecars
targetTriple=${stdenv.hostPlatform.rust.rustcTarget}
cp ${args.opencode}/bin/opencode packages/desktop/src-tauri/sidecars/opencode-cli-$targetTriple
# Merge prod config into tauri.conf.json
if ! jq -s '.[0] * .[1]' \
packages/desktop/src-tauri/tauri.conf.json \
packages/desktop/src-tauri/tauri.prod.conf.json \
> packages/desktop/src-tauri/tauri.conf.json.tmp; then
echo "Error: failed to merge tauri.conf.json with tauri.prod.conf.json" >&2
exit 1
fi
mv packages/desktop/src-tauri/tauri.conf.json.tmp packages/desktop/src-tauri/tauri.conf.json
# Build the frontend
cd packages/desktop
# The 'build' script runs 'bun run typecheck && vite build'.
bun run build
popd
'';
# Tauri bundles the assets during the rust build phase (which happens after preBuild).
# It looks for them in the location specified in tauri.conf.json.
postInstall = lib.optionalString stdenv.isLinux ''
# Wrap the binary to ensure it finds the libraries
wrapProgram $out/bin/opencode-desktop \
--prefix LD_LIBRARY_PATH : ${
lib.makeLibraryPath [
gtk3
webkitgtk_4_1
librsvg
glib
libsoup_3
]
}
'';
meta = with lib; {
description = "OpenCode Desktop App";
homepage = "https://opencode.ai";
license = licenses.mit;
maintainers = with maintainers; [ ];
mainProgram = "opencode-desktop";
platforms = platforms.linux ++ platforms.darwin;
};
}
+4 -1
View File
@@ -1,3 +1,6 @@
{
"nodeModules": "sha256-cpXmqJQJeFj3eED/aOb4YLUdkZFV//7u4f0STBxzUhk="
"nodeModules": {
"x86_64-linux": "sha256-UCPTTk4b7d2bets7KgCeYBHWAUwUAPUyKm+xDYkSexE=",
"aarch64-darwin": "sha256-Y3o6lovahSWoG9un/l1qxu7hCmIlZXm2LxOLKNiPQfQ="
}
}
+22 -12
View File
@@ -1,18 +1,28 @@
{ hash, lib, stdenvNoCC, bun, cacert, curl }:
{
hash,
lib,
stdenvNoCC,
bun,
cacert,
curl,
bunCpu,
bunOs,
}:
args:
stdenvNoCC.mkDerivation {
pname = "opencode-node_modules";
version = args.version;
src = args.src;
inherit (args) version src;
impureEnvVars =
lib.fetchers.proxyImpureEnvVars
++ [
"GIT_PROXY_COMMAND"
"SOCKS_SERVER"
];
impureEnvVars = lib.fetchers.proxyImpureEnvVars ++ [
"GIT_PROXY_COMMAND"
"SOCKS_SERVER"
];
nativeBuildInputs = [ bun cacert curl ];
nativeBuildInputs = [
bun
cacert
curl
];
dontConfigure = true;
@@ -21,8 +31,8 @@ stdenvNoCC.mkDerivation {
export HOME=$(mktemp -d)
export BUN_INSTALL_CACHE_DIR=$(mktemp -d)
bun install \
--cpu="*" \
--os="*" \
--cpu="${bunCpu}" \
--os="${bunOs}" \
--frozen-lockfile \
--ignore-scripts \
--no-progress \
+11 -8
View File
@@ -1,7 +1,13 @@
{ lib, stdenvNoCC, bun, ripgrep, makeBinaryWrapper }:
{
lib,
stdenvNoCC,
bun,
ripgrep,
makeBinaryWrapper,
}:
args:
let
scripts = args.scripts;
inherit (args) scripts;
mkModules =
attrs:
args.mkNodeModules (
@@ -14,13 +20,10 @@ let
in
stdenvNoCC.mkDerivation (finalAttrs: {
pname = "opencode";
version = args.version;
src = args.src;
inherit (args) version src;
node_modules = mkModules {
version = finalAttrs.version;
src = finalAttrs.src;
inherit (finalAttrs) version src;
};
nativeBuildInputs = [
@@ -122,7 +125,7 @@ stdenvNoCC.mkDerivation (finalAttrs: {
It combines a TypeScript/JavaScript core with a Go-based TUI
to provide an interactive AI coding experience.
'';
homepage = "https://github.com/sst/opencode";
homepage = "https://github.com/anomalyco/opencode";
license = lib.licenses.mit;
platforms = [
"aarch64-linux"
+6 -1
View File
@@ -60,7 +60,12 @@ const result = await Bun.build({
compile: {
target,
outfile: "opencode",
execArgv: ["--user-agent=opencode/" + version, '--env-file=""', "--"],
autoloadBunfig: false,
autoloadDotenv: false,
//@ts-ignore (bun types aren't up to date)
autoloadTsconfig: true,
autoloadPackageJson: true,
execArgv: ["--user-agent=opencode/" + version, "--use-system-ca", "--"],
windows: {},
},
})
+7 -3
View File
@@ -31,9 +31,13 @@ for (const [name, wasmPath] of byName) {
next = next.replaceAll("tree-sitter.wasm", mainWasm).replaceAll("web-tree-sitter/tree-sitter.wasm", mainWasm)
// Collapse any relative prefixes before absolute store paths (e.g., "../../../..//nix/store/...")
const nixStorePrefix = process.env.NIX_STORE || "/nix/store"
next = next.replace(/(\.\/)+/g, "./")
next = next.replace(/(\.\.\/)+\/?(\/nix\/store[^"']+)/g, "/$2")
next = next.replace(/(["'])\/{2,}(\/nix\/store[^"']+)(["'])/g, "$1/$2$3")
next = next.replace(/(["'])\/\/(nix\/store[^"']+)(["'])/g, "$1/$2$3")
next = next.replace(
new RegExp(`(\\.\\.\\/)+\\/{1,2}(${nixStorePrefix.replace(/^\//, "").replace(/\//g, "\\/")}[^"']+)`, "g"),
"/$2",
)
next = next.replace(new RegExp(`(["'])\\/{2,}(\\/${nixStorePrefix.replace(/\//g, "\\/")}[^"']+)(["'])`, "g"), "$1$2$3")
next = next.replace(new RegExp(`(["'])\\/\\/(${nixStorePrefix.replace(/\//g, "\\/")}[^"']+)(["'])`, "g"), "$1$2$3")
if (next !== content) fs.writeFileSync(file, next)
+10 -3
View File
@@ -10,7 +10,7 @@ HASH_FILE=${HASH_FILE:-$DEFAULT_HASH_FILE}
if [ ! -f "$HASH_FILE" ]; then
cat >"$HASH_FILE" <<EOF
{
"nodeModules": "$DUMMY"
"nodeModules": {}
}
EOF
fi
@@ -33,9 +33,16 @@ trap cleanup EXIT
write_node_modules_hash() {
local value="$1"
local system="${2:-$SYSTEM}"
local temp
temp=$(mktemp)
jq --arg value "$value" '.nodeModules = $value' "$HASH_FILE" >"$temp"
if jq -e '.nodeModules | type == "object"' "$HASH_FILE" >/dev/null 2>&1; then
jq --arg system "$system" --arg value "$value" '.nodeModules[$system] = $value' "$HASH_FILE" >"$temp"
else
jq --arg system "$system" --arg value "$value" '.nodeModules = {($system): $value}' "$HASH_FILE" >"$temp"
fi
mv "$temp" "$HASH_FILE"
}
@@ -104,7 +111,7 @@ fi
write_node_modules_hash "$CORRECT_HASH"
jq -e --arg hash "$CORRECT_HASH" '.nodeModules == $hash' "$HASH_FILE" >/dev/null
jq -e --arg system "$SYSTEM" --arg hash "$CORRECT_HASH" '.nodeModules[$system] == $hash' "$HASH_FILE" >/dev/null
echo "node_modules hash updated for ${SYSTEM}: $CORRECT_HASH"
+11 -5
View File
@@ -10,7 +10,8 @@
"typecheck": "bun turbo typecheck",
"prepare": "husky",
"random": "echo 'Random script'",
"hello": "echo 'Hello World!'"
"hello": "echo 'Hello World!'",
"test": "echo 'do not run tests from root' && exit 1"
},
"workspaces": {
"packages": [
@@ -31,19 +32,23 @@
"@tsconfig/bun": "1.0.9",
"@cloudflare/workers-types": "4.20251008.0",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@pierre/diffs": "1.0.0-beta.3",
"@pierre/diffs": "1.0.2",
"@solid-primitives/storage": "4.3.3",
"@tailwindcss/vite": "4.1.11",
"diff": "8.0.2",
"ai": "5.0.97",
"dompurify": "3.3.1",
"ai": "5.0.119",
"hono": "4.10.7",
"hono-openapi": "1.1.2",
"fuzzysort": "3.1.0",
"luxon": "3.6.1",
"marked": "17.0.1",
"marked-shiki": "1.2.1",
"typescript": "5.8.2",
"@typescript/native-preview": "7.0.0-dev.20251207.1",
"zod": "4.1.8",
"remeda": "2.26.0",
"shiki": "3.20.0",
"solid-list": "0.3.0",
"tailwindcss": "4.1.11",
"virtua": "0.42.3",
@@ -56,6 +61,7 @@
}
},
"devDependencies": {
"@actions/artifact": "5.0.1",
"@tsconfig/bun": "catalog:",
"husky": "9.1.7",
"prettier": "3.6.2",
@@ -64,13 +70,14 @@
},
"dependencies": {
"@aws-sdk/client-s3": "3.933.0",
"@opencode-ai/plugin": "workspace:*",
"@opencode-ai/script": "workspace:*",
"@opencode-ai/sdk": "workspace:*",
"typescript": "catalog:"
},
"repository": {
"type": "git",
"url": "https://github.com/sst/opencode"
"url": "https://github.com/anomalyco/opencode"
},
"license": "MIT",
"prettier": {
@@ -80,7 +87,6 @@
"trustedDependencies": [
"esbuild",
"protobufjs",
"sharp",
"tree-sitter",
"tree-sitter-bash",
"web-tree-sitter"
+1
View File
@@ -0,0 +1 @@
src/assets/theme.css
+13
View File
@@ -0,0 +1,13 @@
## Debugging
- To test the opencode app, use the playwright MCP server, the app is already
running at http://localhost:3000
- NEVER try to restart the app, or the server process, EVER.
## SolidJS
- Always prefer `createStore` over multiple `createSignal` calls
## Tool Calling
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE.
+34
View File
@@ -0,0 +1,34 @@
## Usage
Dependencies for these templates are managed with [pnpm](https://pnpm.io) using `pnpm up -Lri`.
This is the reason you see a `pnpm-lock.yaml`. That said, any package manager will work. This file can safely be removed once you clone a template.
```bash
$ npm install # or pnpm install or yarn install
```
### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
## Available Scripts
In the project directory, you can run:
### `npm run dev` or `npm start`
Runs the app in the development mode.<br>
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br>
### `npm run build`
Builds the app for production to the `dist` folder.<br>
It correctly bundles Solid in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br>
Your app is ready to be deployed!
## Deployment
You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.)
@@ -1,5 +1,5 @@
<!doctype html>
<html lang="en">
<html lang="en" style="background-color: var(--background-base)">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
@@ -13,16 +13,12 @@
<meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" />
<meta property="og:image" content="/social-share.png" />
<meta property="twitter:image" content="/social-share.png" />
<!-- Theme preload script - applies cached theme to avoid FOUC -->
<script id="oc-theme-preload-script" src="/oc-theme-preload.js"></script>
</head>
<body class="antialiased overscroll-none text-12-regular overflow-hidden">
<script>
;(function () {
const savedTheme = localStorage.getItem("theme") || "oc-1"
document.documentElement.setAttribute("data-theme", savedTheme)
})()
</script>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" class="flex flex-col h-screen"></div>
<script src="/src/index.tsx" type="module"></script>
<div id="root" class="flex flex-col h-dvh"></div>
<script src="/src/entry.tsx" type="module"></script>
</body>
</html>
+62
View File
@@ -0,0 +1,62 @@
{
"name": "@opencode-ai/app",
"version": "1.1.17",
"description": "",
"type": "module",
"exports": {
".": "./src/index.ts",
"./vite": "./vite.js"
},
"scripts": {
"typecheck": "tsgo -b",
"start": "vite",
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"license": "MIT",
"devDependencies": {
"@happy-dom/global-registrator": "20.0.11",
"@tailwindcss/vite": "catalog:",
"@tsconfig/bun": "1.0.9",
"@types/bun": "catalog:",
"@types/luxon": "catalog:",
"@types/node": "catalog:",
"@typescript/native-preview": "catalog:",
"typescript": "catalog:",
"vite": "catalog:",
"vite-plugin-icons-spritesheet": "3.0.1",
"vite-plugin-solid": "catalog:"
},
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
"@opencode-ai/ui": "workspace:*",
"@opencode-ai/util": "workspace:*",
"@shikijs/transformers": "3.9.2",
"@solid-primitives/active-element": "2.1.3",
"@solid-primitives/audio": "1.4.2",
"@solid-primitives/event-bus": "1.1.2",
"@solid-primitives/media": "2.3.3",
"@solid-primitives/resize-observer": "2.1.3",
"@solid-primitives/scroll": "2.1.3",
"@solid-primitives/storage": "catalog:",
"@solid-primitives/websocket": "1.3.1",
"@solidjs/meta": "catalog:",
"@solidjs/router": "catalog:",
"@thisbeyond/solid-dnd": "0.7.5",
"diff": "catalog:",
"fuzzysort": "catalog:",
"ghostty-web": "0.3.0",
"luxon": "catalog:",
"marked": "catalog:",
"marked-shiki": "catalog:",
"remeda": "catalog:",
"shiki": "catalog:",
"solid-js": "catalog:",
"solid-list": "catalog:",
"tailwindcss": "catalog:",
"virtua": "catalog:",
"zod": "catalog:"
}
}
+17
View File
@@ -0,0 +1,17 @@
/assets/*.js
Content-Type: application/javascript
/assets/*.mjs
Content-Type: application/javascript
/assets/*.css
Content-Type: text/css
/*.js
Content-Type: application/javascript
/*.mjs
Content-Type: application/javascript
/*.css
Content-Type: text/css
+28
View File
@@ -0,0 +1,28 @@
;(function () {
var themeId = localStorage.getItem("opencode-theme-id")
if (!themeId) return
var scheme = localStorage.getItem("opencode-color-scheme") || "system"
var isDark = scheme === "dark" || (scheme === "system" && matchMedia("(prefers-color-scheme: dark)").matches)
var mode = isDark ? "dark" : "light"
document.documentElement.dataset.theme = themeId
document.documentElement.dataset.colorScheme = mode
if (themeId === "oc-1") return
var css = localStorage.getItem("opencode-theme-css-" + themeId + "-" + mode)
if (css) {
var style = document.createElement("style")
style.id = "oc-theme-preload"
style.textContent =
":root{color-scheme:" +
mode +
";--text-mix-blend-mode:" +
(isDark ? "plus-lighter" : "multiply") +
";" +
css +
"}"
document.head.appendChild(style)
}
})()
@@ -242,6 +242,53 @@ describe("SerializeAddon", () => {
expect(term2.buffer.active.getLine(2)?.translateToString(true)).toBe("total 42")
})
test("serialized output should restore after Terminal.reset()", async () => {
const { term, addon } = createTerminal()
const content = [
"\x1b[1;32m\x1b[0m \x1b[34mcd\x1b[0m /some/path",
"\x1b[1;32m\x1b[0m \x1b[34mls\x1b[0m -la",
"total 42",
].join("\r\n")
await writeAndWait(term, content)
const serialized = addon.serialize()
const { term: term2 } = createTerminal()
terminals.push(term2)
term2.reset()
await writeAndWait(term2, serialized)
expect(term2.buffer.active.getLine(0)?.translateToString(true)).toContain("cd /some/path")
expect(term2.buffer.active.getLine(1)?.translateToString(true)).toContain("ls -la")
expect(term2.buffer.active.getLine(2)?.translateToString(true)).toBe("total 42")
})
test("alternate buffer should round-trip without garbage", async () => {
const { term, addon } = createTerminal(20, 5)
await writeAndWait(term, "normal\r\n")
await writeAndWait(term, "\x1b[?1049h\x1b[HALT")
expect(term.buffer.active.type).toBe("alternate")
const serialized = addon.serialize()
const { term: term2 } = createTerminal(20, 5)
terminals.push(term2)
await writeAndWait(term2, serialized)
expect(term2.buffer.active.type).toBe("alternate")
const line = term2.buffer.active.getLine(0)
expect(line?.translateToString(true)).toBe("ALT")
// Ensure a cell beyond content isn't garbage
const cellCode = line?.getCell(10)?.getCode()
expect(cellCode === 0 || cellCode === 32).toBe(true)
})
test("serialized output written to new terminal should match original colors", async () => {
const { term, addon } = createTerminal(40, 5)
@@ -157,23 +157,6 @@ function equalFlags(cell1: IBufferCell, cell2: IBufferCell): boolean {
abstract class BaseSerializeHandler {
constructor(protected readonly _buffer: IBuffer) {}
private _isRealContent(codepoint: number): boolean {
if (codepoint === 0) return false
if (codepoint >= 0xf000) return false
return true
}
private _findLastContentColumn(line: IBufferLine): number {
let lastContent = -1
for (let col = 0; col < line.length; col++) {
const cell = line.getCell(col)
if (cell && this._isRealContent(cell.getCode())) {
lastContent = col
}
}
return lastContent + 1
}
public serialize(range: IBufferRange, excludeFinalCursorPosition?: boolean): string {
let oldCell = this._buffer.getNullCell()
@@ -182,14 +165,14 @@ abstract class BaseSerializeHandler {
const startColumn = range.start.x
const endColumn = range.end.x
this._beforeSerialize(endRow - startRow, startRow, endRow)
this._beforeSerialize(endRow - startRow + 1, startRow, endRow)
for (let row = startRow; row <= endRow; row++) {
const line = this._buffer.getLine(row)
if (line) {
const startLineColumn = row === range.start.y ? startColumn : 0
const maxColumn = row === range.end.y ? endColumn : this._findLastContentColumn(line)
const endLineColumn = Math.min(maxColumn, line.length)
const endLineColumn = Math.min(endColumn, line.length)
for (let col = startLineColumn; col < endLineColumn; col++) {
const c = line.getCell(col)
if (!c) {
@@ -243,6 +226,13 @@ class StringSerializeHandler extends BaseSerializeHandler {
protected _beforeSerialize(rows: number, start: number, _end: number): void {
this._allRows = new Array<string>(rows)
this._allRowSeparators = new Array<string>(rows)
this._rowIndex = 0
this._currentRow = ""
this._nullCellCount = 0
this._cursorStyle = this._buffer.getNullCell()
this._lastContentCursorRow = start
this._lastCursorRow = start
this._firstRow = start
@@ -251,6 +241,11 @@ class StringSerializeHandler extends BaseSerializeHandler {
protected _rowEnd(row: number, isLastRow: boolean): void {
let rowSeparator = ""
if (this._nullCellCount > 0) {
this._currentRow += " ".repeat(this._nullCellCount)
this._nullCellCount = 0
}
if (!isLastRow) {
const nextLine = this._buffer.getLine(row + 1)
@@ -388,7 +383,8 @@ class StringSerializeHandler extends BaseSerializeHandler {
}
const codepoint = cell.getCode()
const isGarbage = codepoint >= 0xf000
const isInvalidCodepoint = codepoint > 0x10ffff || (codepoint >= 0xd800 && codepoint <= 0xdfff)
const isGarbage = isInvalidCodepoint || (codepoint >= 0xf000 && cell.getWidth() === 1)
const isEmptyCell = codepoint === 0 || cell.getChars() === "" || isGarbage
const sgrSeq = this._diffStyle(cell, this._cursorStyle)
@@ -397,7 +393,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
if (styleChanged) {
if (this._nullCellCount > 0) {
this._currentRow += `\u001b[${this._nullCellCount}C`
this._currentRow += " ".repeat(this._nullCellCount)
this._nullCellCount = 0
}
@@ -417,7 +413,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
this._nullCellCount += cell.getWidth()
} else {
if (this._nullCellCount > 0) {
this._currentRow += `\u001b[${this._nullCellCount}C`
this._currentRow += " ".repeat(this._nullCellCount)
this._nullCellCount = 0
}
+127
View File
@@ -0,0 +1,127 @@
import "@/index.css"
import { ErrorBoundary, Show, lazy, type ParentProps } from "solid-js"
import { Router, Route, Navigate } from "@solidjs/router"
import { MetaProvider } from "@solidjs/meta"
import { Font } from "@opencode-ai/ui/font"
import { MarkedProvider } from "@opencode-ai/ui/context/marked"
import { DiffComponentProvider } from "@opencode-ai/ui/context/diff"
import { CodeComponentProvider } from "@opencode-ai/ui/context/code"
import { Diff } from "@opencode-ai/ui/diff"
import { Code } from "@opencode-ai/ui/code"
import { ThemeProvider } from "@opencode-ai/ui/theme"
import { GlobalSyncProvider } from "@/context/global-sync"
import { PermissionProvider } from "@/context/permission"
import { LayoutProvider } from "@/context/layout"
import { GlobalSDKProvider } from "@/context/global-sdk"
import { ServerProvider, useServer } from "@/context/server"
import { TerminalProvider } from "@/context/terminal"
import { PromptProvider } from "@/context/prompt"
import { FileProvider } from "@/context/file"
import { NotificationProvider } from "@/context/notification"
import { DialogProvider } from "@opencode-ai/ui/context/dialog"
import { CommandProvider } from "@/context/command"
import { Logo } from "@opencode-ai/ui/logo"
import Layout from "@/pages/layout"
import DirectoryLayout from "@/pages/directory-layout"
import { ErrorPage } from "./pages/error"
import { iife } from "@opencode-ai/util/iife"
import { Suspense } from "solid-js"
const Home = lazy(() => import("@/pages/home"))
const Session = lazy(() => import("@/pages/session"))
const Loading = () => <div class="size-full flex items-center justify-center text-text-weak">Loading...</div>
declare global {
interface Window {
__OPENCODE__?: { updaterEnabled?: boolean; serverPassword?: string }
}
}
export function AppBaseProviders(props: ParentProps) {
return (
<MetaProvider>
<Font />
<ThemeProvider>
<ErrorBoundary fallback={(error) => <ErrorPage error={error} />}>
<DialogProvider>
<MarkedProvider>
<DiffComponentProvider component={Diff}>
<CodeComponentProvider component={Code}>{props.children}</CodeComponentProvider>
</DiffComponentProvider>
</MarkedProvider>
</DialogProvider>
</ErrorBoundary>
</ThemeProvider>
</MetaProvider>
)
}
function ServerKey(props: ParentProps) {
const server = useServer()
return (
<Show when={server.url} keyed>
{props.children}
</Show>
)
}
export function AppInterface(props: { defaultUrl?: string }) {
const defaultServerUrl = () => {
if (props.defaultUrl) return props.defaultUrl
if (location.hostname.includes("opencode.ai")) return "http://localhost:4096"
if (import.meta.env.DEV)
return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}`
return window.location.origin
}
return (
<ServerProvider defaultUrl={defaultServerUrl()}>
<ServerKey>
<GlobalSDKProvider>
<GlobalSyncProvider>
<Router
root={(props) => (
<PermissionProvider>
<LayoutProvider>
<NotificationProvider>
<CommandProvider>
<Layout>{props.children}</Layout>
</CommandProvider>
</NotificationProvider>
</LayoutProvider>
</PermissionProvider>
)}
>
<Route
path="/"
component={() => (
<Suspense fallback={<Loading />}>
<Home />
</Suspense>
)}
/>
<Route path="/:dir" component={DirectoryLayout}>
<Route path="/" component={() => <Navigate href="session" />} />
<Route
path="/session/:id?"
component={() => (
<TerminalProvider>
<FileProvider>
<PromptProvider>
<Suspense fallback={<Loading />}>
<Session />
</Suspense>
</PromptProvider>
</FileProvider>
</TerminalProvider>
)}
/>
</Route>
</Router>
</GlobalSyncProvider>
</GlobalSDKProvider>
</ServerKey>
</ServerProvider>
)
}
@@ -1,24 +1,24 @@
import type { ProviderAuthAuthorization } from "@opencode-ai/sdk/v2/client"
import { Button } from "@opencode-ai/ui/button"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { Icon } from "@opencode-ai/ui/icon"
import { IconButton } from "@opencode-ai/ui/icon-button"
import type { IconName } from "@opencode-ai/ui/icons/provider"
import { List, type ListRef } from "@opencode-ai/ui/list"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { Spinner } from "@opencode-ai/ui/spinner"
import { TextField } from "@opencode-ai/ui/text-field"
import { showToast } from "@opencode-ai/ui/toast"
import { iife } from "@opencode-ai/util/iife"
import { createMemo, Match, onCleanup, onMount, Switch } from "solid-js"
import { createStore, produce } from "solid-js/store"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { useGlobalSync } from "@/context/global-sync"
import { useGlobalSDK } from "@/context/global-sdk"
import { usePlatform } from "@/context/platform"
import { ProviderAuthAuthorization } from "@opencode-ai/sdk/v2/client"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List, ListRef } from "@opencode-ai/ui/list"
import { Button } from "@opencode-ai/ui/button"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { TextField } from "@opencode-ai/ui/text-field"
import { Spinner } from "@opencode-ai/ui/spinner"
import { Icon } from "@opencode-ai/ui/icon"
import { showToast } from "@opencode-ai/ui/toast"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { IconName } from "@opencode-ai/ui/icons/provider"
import { iife } from "@opencode-ai/util/iife"
import { Link } from "@/components/link"
import { DialogSelectProvider } from "./dialog-select-provider"
import { useGlobalSDK } from "@/context/global-sdk"
import { useGlobalSync } from "@/context/global-sync"
import { usePlatform } from "@/context/platform"
import { DialogSelectModel } from "./dialog-select-model"
import { DialogSelectProvider } from "./dialog-select-provider"
export function DialogConnectProvider(props: { provider: string }) {
const dialog = useDialog()
@@ -154,7 +154,9 @@ export function DialogConnectProvider(props: { provider: string }) {
<div class="text-14-regular text-text-base">Select login method for {provider().name}.</div>
<div class="">
<List
ref={(ref) => (listRef = ref)}
ref={(ref) => {
listRef = ref
}}
items={methods}
key={(m) => m?.label}
onSelect={async (method, index) => {
@@ -163,7 +165,7 @@ export function DialogConnectProvider(props: { provider: string }) {
}}
>
{(i) => (
<div class="w-full flex items-center gap-x-4">
<div class="w-full flex items-center gap-x-2">
<div class="w-4 h-2 rounded-[1px] bg-input-base shadow-xs-border-base flex items-center justify-center">
<div class="w-2.5 h-0.5 bg-icon-strong-base hidden" data-slot="list-item-extra-icon" />
</div>
@@ -175,7 +177,7 @@ export function DialogConnectProvider(props: { provider: string }) {
</Match>
<Match when={store.state === "pending"}>
<div class="text-14-regular text-text-base">
<div class="flex items-center gap-x-4">
<div class="flex items-center gap-x-2">
<Spinner />
<span>Authorization in progress...</span>
</div>
@@ -183,7 +185,7 @@ export function DialogConnectProvider(props: { provider: string }) {
</Match>
<Match when={store.state === "error"}>
<div class="text-14-regular text-text-base">
<div class="flex items-center gap-x-4">
<div class="flex items-center gap-x-2">
<Icon name="circle-ban-sign" class="text-icon-critical-base" />
<span>Authorization failed: {store.error}</span>
</div>
@@ -0,0 +1,176 @@
import { Button } from "@opencode-ai/ui/button"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { TextField } from "@opencode-ai/ui/text-field"
import { Icon } from "@opencode-ai/ui/icon"
import { createMemo, createSignal, For, Show } from "solid-js"
import { createStore } from "solid-js/store"
import { useGlobalSDK } from "@/context/global-sdk"
import { type LocalProject, getAvatarColors } from "@/context/layout"
import { getFilename } from "@opencode-ai/util/path"
import { Avatar } from "@opencode-ai/ui/avatar"
const AVATAR_COLOR_KEYS = ["pink", "mint", "orange", "purple", "cyan", "lime"] as const
export function DialogEditProject(props: { project: LocalProject }) {
const dialog = useDialog()
const globalSDK = useGlobalSDK()
const folderName = createMemo(() => getFilename(props.project.worktree))
const defaultName = createMemo(() => props.project.name || folderName())
const [store, setStore] = createStore({
name: defaultName(),
color: props.project.icon?.color || "pink",
iconUrl: props.project.icon?.url || "",
saving: false,
})
const [dragOver, setDragOver] = createSignal(false)
function handleFileSelect(file: File) {
if (!file.type.startsWith("image/")) return
const reader = new FileReader()
reader.onload = (e) => setStore("iconUrl", e.target?.result as string)
reader.readAsDataURL(file)
}
function handleDrop(e: DragEvent) {
e.preventDefault()
setDragOver(false)
const file = e.dataTransfer?.files[0]
if (file) handleFileSelect(file)
}
function handleDragOver(e: DragEvent) {
e.preventDefault()
setDragOver(true)
}
function handleDragLeave() {
setDragOver(false)
}
function handleInputChange(e: Event) {
const input = e.target as HTMLInputElement
const file = input.files?.[0]
if (file) handleFileSelect(file)
}
function clearIcon() {
setStore("iconUrl", "")
}
async function handleSubmit(e: SubmitEvent) {
e.preventDefault()
if (!props.project.id) return
setStore("saving", true)
const name = store.name.trim() === folderName() ? "" : store.name.trim()
await globalSDK.client.project.update({
projectID: props.project.id,
name,
icon: { color: store.color, url: store.iconUrl },
})
setStore("saving", false)
dialog.close()
}
return (
<Dialog title="Edit project">
<form onSubmit={handleSubmit} class="flex flex-col gap-6 px-2.5 pb-3">
<div class="flex flex-col gap-4">
<TextField
autofocus
type="text"
label="Name"
placeholder={folderName()}
value={store.name}
onChange={(v) => setStore("name", v)}
/>
<div class="flex flex-col gap-2">
<label class="text-12-medium text-text-weak">Icon</label>
<div class="flex gap-3 items-start">
<div class="relative">
<div
class="size-16 rounded-lg overflow-hidden border border-dashed transition-colors cursor-pointer"
classList={{
"border-text-interactive-base bg-surface-info-base/20": dragOver(),
"border-border-base hover:border-border-strong": !dragOver(),
}}
onDrop={handleDrop}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onClick={() => document.getElementById("icon-upload")?.click()}
>
<Show
when={store.iconUrl}
fallback={
<div class="size-full flex items-center justify-center">
<Avatar
fallback={store.name || defaultName()}
{...getAvatarColors(store.color)}
class="size-full"
/>
</div>
}
>
<img src={store.iconUrl} alt="Project icon" class="size-full object-cover" />
</Show>
</div>
<Show when={store.iconUrl}>
<button
type="button"
class="absolute -top-1.5 -right-1.5 size-5 rounded-full bg-surface-raised-base border border-border-base flex items-center justify-center hover:bg-surface-raised-base-hover"
onClick={clearIcon}
>
<Icon name="close" class="size-3 text-icon-base" />
</button>
</Show>
</div>
<input id="icon-upload" type="file" accept="image/*" class="hidden" onChange={handleInputChange} />
<div class="flex flex-col gap-1.5 text-12-regular text-text-weak">
<span>Click or drag an image</span>
<span>Recommended: 128x128px</span>
</div>
</div>
</div>
<Show when={!store.iconUrl}>
<div class="flex flex-col gap-2">
<label class="text-12-medium text-text-weak">Color</label>
<div class="flex gap-2">
<For each={AVATAR_COLOR_KEYS}>
{(color) => (
<button
type="button"
class="relative size-8 rounded-md transition-all"
classList={{
"ring-2 ring-offset-2 ring-offset-surface-base ring-text-interactive-base":
store.color === color,
}}
style={{ background: getAvatarColors(color).background }}
onClick={() => setStore("color", color)}
>
<Avatar fallback={store.name || defaultName()} {...getAvatarColors(color)} class="size-full" />
</button>
)}
</For>
</div>
</div>
</Show>
</div>
<div class="flex justify-end gap-2">
<Button type="button" variant="ghost" size="large" onClick={() => dialog.close()}>
Cancel
</Button>
<Button type="submit" variant="primary" size="large" disabled={store.saving}>
{store.saving ? "Saving..." : "Save"}
</Button>
</div>
</form>
</Dialog>
)
}
@@ -0,0 +1,99 @@
import { Component, createMemo } from "solid-js"
import { useNavigate, useParams } from "@solidjs/router"
import { useSync } from "@/context/sync"
import { useSDK } from "@/context/sdk"
import { usePrompt } from "@/context/prompt"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
import { extractPromptFromParts } from "@/utils/prompt"
import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client"
import { base64Encode } from "@opencode-ai/util/encode"
interface ForkableMessage {
id: string
text: string
time: string
}
function formatTime(date: Date): string {
return date.toLocaleTimeString(undefined, { timeStyle: "short" })
}
export const DialogFork: Component = () => {
const params = useParams()
const navigate = useNavigate()
const sync = useSync()
const sdk = useSDK()
const prompt = usePrompt()
const dialog = useDialog()
const messages = createMemo((): ForkableMessage[] => {
const sessionID = params.id
if (!sessionID) return []
const msgs = sync.data.message[sessionID] ?? []
const result: ForkableMessage[] = []
for (const message of msgs) {
if (message.role !== "user") continue
const parts = sync.data.part[message.id] ?? []
const textPart = parts.find((x): x is SDKTextPart => x.type === "text" && !x.synthetic && !x.ignored)
if (!textPart) continue
result.push({
id: message.id,
text: textPart.text.replace(/\n/g, " ").slice(0, 200),
time: formatTime(new Date(message.time.created)),
})
}
return result.reverse()
})
const handleSelect = (item: ForkableMessage | undefined) => {
if (!item) return
const sessionID = params.id
if (!sessionID) return
const parts = sync.data.part[item.id] ?? []
const restored = extractPromptFromParts(parts, { directory: sdk.directory })
dialog.close()
sdk.client.session.fork({ sessionID, messageID: item.id }).then((forked) => {
if (!forked.data) return
navigate(`/${base64Encode(sdk.directory)}/session/${forked.data.id}`)
requestAnimationFrame(() => {
prompt.set(restored)
})
})
}
return (
<Dialog title="Fork from message">
<List
class="flex-1 min-h-0 [&_[data-slot=list-scroll]]:flex-1 [&_[data-slot=list-scroll]]:min-h-0"
search={{ placeholder: "Search", autofocus: true }}
emptyMessage="No messages to fork from"
key={(x) => x.id}
items={messages}
filterKeys={["text"]}
onSelect={handleSelect}
>
{(item) => (
<div class="w-full flex items-center gap-2">
<span class="truncate flex-1 min-w-0 text-left" style={{ "font-weight": "400" }}>
{item.text}
</span>
<span class="text-text-weak shrink-0" style={{ "font-weight": "400" }}>
{item.time}
</span>
</div>
)}
</List>
</Dialog>
)
}
@@ -1,16 +1,15 @@
import { Component } from "solid-js"
import { useLocal } from "@/context/local"
import { popularProviders } from "@/hooks/use-providers"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
import { Switch } from "@opencode-ai/ui/switch"
import type { Component } from "solid-js"
import { useLocal } from "@/context/local"
import { popularProviders } from "@/hooks/use-providers"
export const DialogManageModels: Component = () => {
const local = useLocal()
return (
<Dialog title="Manage models" description="Customize which models appear in the model selector.">
<List
class="px-2.5"
search={{ placeholder: "Search models", autofocus: true }}
emptyMessage="No model results"
key={(x) => `${x?.provider?.id}:${x?.id}`}
@@ -27,16 +26,24 @@ export const DialogManageModels: Component = () => {
}}
onSelect={(x) => {
if (!x) return
const visible = local.model.visible({ modelID: x.id, providerID: x.provider.id })
const visible = local.model.visible({
modelID: x.id,
providerID: x.provider.id,
})
local.model.setVisibility({ modelID: x.id, providerID: x.provider.id }, !visible)
}}
>
{(i) => (
<div class="w-full flex items-center justify-between gap-x-2.5">
<div class="w-full flex items-center justify-between gap-x-3">
<span>{i.name}</span>
<div onClick={(e) => e.stopPropagation()}>
<Switch
checked={!!local.model.visible({ modelID: i.id, providerID: i.provider.id })}
checked={
!!local.model.visible({
modelID: i.id,
providerID: i.provider.id,
})
}
onChange={(checked) => {
local.model.setVisibility({ modelID: i.id, providerID: i.provider.id }, checked)
}}
@@ -0,0 +1,114 @@
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { FileIcon } from "@opencode-ai/ui/file-icon"
import { List } from "@opencode-ai/ui/list"
import { getDirectory, getFilename } from "@opencode-ai/util/path"
import { createMemo } from "solid-js"
import { useGlobalSDK } from "@/context/global-sdk"
import { useGlobalSync } from "@/context/global-sync"
interface DialogSelectDirectoryProps {
title?: string
multiple?: boolean
onSelect: (result: string | string[] | null) => void
}
export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
const sync = useGlobalSync()
const sdk = useGlobalSDK()
const dialog = useDialog()
const home = createMemo(() => sync.data.path.home)
const root = createMemo(() => sync.data.path.home || sync.data.path.directory)
function join(base: string | undefined, rel: string) {
const b = (base ?? "").replace(/[\\/]+$/, "")
const r = rel.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")
if (!b) return r
if (!r) return b
return b + "/" + r
}
function display(rel: string) {
const full = join(root(), rel)
const h = home()
if (!h) return full
if (full === h) return "~"
if (full.startsWith(h + "/") || full.startsWith(h + "\\")) {
return "~" + full.slice(h.length)
}
return full
}
function normalizeQuery(query: string) {
const h = home()
if (!query) return query
if (query.startsWith("~/")) return query.slice(2)
if (h) {
const lc = query.toLowerCase()
const hc = h.toLowerCase()
if (lc === hc || lc.startsWith(hc + "/") || lc.startsWith(hc + "\\")) {
return query.slice(h.length).replace(/^[\\/]+/, "")
}
}
return query
}
async function fetchDirs(query: string) {
const directory = root()
if (!directory) return [] as string[]
const results = await sdk.client.find
.files({ directory, query, type: "directory", limit: 50 })
.then((x) => x.data ?? [])
.catch(() => [])
return results.map((x) => x.replace(/[\\/]+$/, ""))
}
const directories = async (filter: string) => {
const query = normalizeQuery(filter.trim())
return fetchDirs(query)
}
function resolve(rel: string) {
const absolute = join(root(), rel)
props.onSelect(props.multiple ? [absolute] : absolute)
dialog.close()
}
return (
<Dialog title={props.title ?? "Open project"}>
<List
search={{ placeholder: "Search folders", autofocus: true }}
emptyMessage="No folders found"
items={directories}
key={(x) => x}
onSelect={(path) => {
if (!path) return
resolve(path)
}}
>
{(rel) => {
const path = display(rel)
return (
<div class="w-full flex items-center justify-between rounded-md">
<div class="flex items-center gap-x-3 grow min-w-0">
<FileIcon node={{ path: rel, type: "directory" }} class="shrink-0 size-4" />
<div class="flex items-center text-14-regular min-w-0">
<span class="text-text-weak whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
{getDirectory(path)}
</span>
<span class="text-text-strong whitespace-nowrap">{getFilename(path)}</span>
</div>
</div>
</div>
)
}}
</List>
</Dialog>
)
}
@@ -1,38 +1,41 @@
import { useLocal } from "@/context/local"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
import { FileIcon } from "@opencode-ai/ui/file-icon"
import { getDirectory, getFilename } from "@opencode-ai/util/path"
import { useLayout } from "@/context/layout"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { FileIcon } from "@opencode-ai/ui/file-icon"
import { List } from "@opencode-ai/ui/list"
import { getDirectory, getFilename } from "@opencode-ai/util/path"
import { useParams } from "@solidjs/router"
import { createMemo } from "solid-js"
import { useLayout } from "@/context/layout"
import { useFile } from "@/context/file"
export function DialogSelectFile() {
const layout = useLayout()
const local = useLocal()
const file = useFile()
const dialog = useDialog()
const params = useParams()
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
const tabs = createMemo(() => layout.tabs(sessionKey()))
const view = createMemo(() => layout.view(sessionKey()))
return (
<Dialog title="Select file">
<List
class="px-2.5"
search={{ placeholder: "Search files", autofocus: true }}
emptyMessage="No files found"
items={local.file.searchFiles}
items={file.searchFiles}
key={(x) => x}
onSelect={(path) => {
if (path) {
tabs().open("file://" + path)
const value = file.tab(path)
tabs().open(value)
file.load(path)
view().reviewPanel.open()
}
dialog.close()
}}
>
{(i) => (
<div class="w-full flex items-center justify-between rounded-md">
<div class="flex items-center gap-x-2 grow min-w-0">
<div class="flex items-center gap-x-3 grow min-w-0">
<FileIcon node={{ path: i, type: "file" }} class="shrink-0 size-4" />
<div class="flex items-center text-14-regular">
<span class="text-text-weak whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
@@ -0,0 +1,91 @@
import { Component, createMemo, createSignal, Show } from "solid-js"
import { useSync } from "@/context/sync"
import { useSDK } from "@/context/sdk"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
import { Switch } from "@opencode-ai/ui/switch"
export const DialogSelectMcp: Component = () => {
const sync = useSync()
const sdk = useSDK()
const [loading, setLoading] = createSignal<string | null>(null)
const items = createMemo(() =>
Object.entries(sync.data.mcp ?? {})
.map(([name, status]) => ({ name, status: status.status }))
.sort((a, b) => a.name.localeCompare(b.name)),
)
const toggle = async (name: string) => {
if (loading()) return
setLoading(name)
const status = sync.data.mcp[name]
if (status?.status === "connected") {
await sdk.client.mcp.disconnect({ name })
} else {
await sdk.client.mcp.connect({ name })
}
const result = await sdk.client.mcp.status()
if (result.data) sync.set("mcp", result.data)
setLoading(null)
}
const enabledCount = createMemo(() => items().filter((i) => i.status === "connected").length)
const totalCount = createMemo(() => items().length)
return (
<Dialog title="MCPs" description={`${enabledCount()} of ${totalCount()} enabled`}>
<List
search={{ placeholder: "Search", autofocus: true }}
emptyMessage="No MCPs configured"
key={(x) => x?.name ?? ""}
items={items}
filterKeys={["name", "status"]}
sortBy={(a, b) => a.name.localeCompare(b.name)}
onSelect={(x) => {
if (x) toggle(x.name)
}}
>
{(i) => {
const mcpStatus = () => sync.data.mcp[i.name]
const status = () => mcpStatus()?.status
const error = () => {
const s = mcpStatus()
return s?.status === "failed" ? s.error : undefined
}
const enabled = () => status() === "connected"
return (
<div class="w-full flex items-center justify-between gap-x-3">
<div class="flex flex-col gap-0.5 min-w-0">
<div class="flex items-center gap-2">
<span class="truncate">{i.name}</span>
<Show when={status() === "connected"}>
<span class="text-11-regular text-text-weaker">connected</span>
</Show>
<Show when={status() === "failed"}>
<span class="text-11-regular text-text-weaker">failed</span>
</Show>
<Show when={status() === "needs_auth"}>
<span class="text-11-regular text-text-weaker">needs auth</span>
</Show>
<Show when={status() === "disabled"}>
<span class="text-11-regular text-text-weaker">disabled</span>
</Show>
<Show when={loading() === i.name}>
<span class="text-11-regular text-text-weak">...</span>
</Show>
</div>
<Show when={error()}>
<span class="text-11-regular text-text-weaker truncate">{error()}</span>
</Show>
</div>
<div onClick={(e) => e.stopPropagation()}>
<Switch checked={enabled()} disabled={loading() === i.name} onChange={() => toggle(i.name)} />
</div>
</div>
)
}}
</List>
</Dialog>
)
}
@@ -1,15 +1,15 @@
import { Component, onCleanup, onMount, Show } from "solid-js"
import { useLocal } from "@/context/local"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { popularProviders, useProviders } from "@/hooks/use-providers"
import { Button } from "@opencode-ai/ui/button"
import { Tag } from "@opencode-ai/ui/tag"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List, ListRef } from "@opencode-ai/ui/list"
import type { IconName } from "@opencode-ai/ui/icons/provider"
import { List, type ListRef } from "@opencode-ai/ui/list"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { IconName } from "@opencode-ai/ui/icons/provider"
import { DialogSelectProvider } from "./dialog-select-provider"
import { Tag } from "@opencode-ai/ui/tag"
import { type Component, onCleanup, onMount, Show } from "solid-js"
import { useLocal } from "@/context/local"
import { popularProviders, useProviders } from "@/hooks/use-providers"
import { DialogConnectProvider } from "./dialog-connect-provider"
import { DialogSelectProvider } from "./dialog-select-provider"
export const DialogSelectModelUnpaid: Component = () => {
const local = useLocal()
@@ -64,7 +64,7 @@ export const DialogSelectModelUnpaid: Component = () => {
<div class="px-2 text-14-medium text-text-base">Add more models from popular providers</div>
<div class="w-full">
<List
class="w-full"
class="w-full px-0"
key={(x) => x?.id}
items={providers.popular}
activeIcon="plus-small"
@@ -79,17 +79,8 @@ export const DialogSelectModelUnpaid: Component = () => {
}}
>
{(i) => (
<div class="w-full flex items-center gap-x-4">
<ProviderIcon
data-slot="list-item-extra-icon"
id={i.id as IconName}
// TODO: clean this up after we update icon in models.dev
classList={{
"text-icon-weak-base": true,
"size-4 mx-0.5": i.id === "opencode",
"size-5": i.id !== "opencode",
}}
/>
<div class="w-full flex items-center gap-x-3">
<ProviderIcon data-slot="list-item-extra-icon" id={i.id as IconName} />
<span>{i.name}</span>
<Show when={i.id === "opencode"}>
<Tag>Recommended</Tag>
@@ -0,0 +1,124 @@
import { Popover as Kobalte } from "@kobalte/core/popover"
import { Component, createMemo, createSignal, JSX, Show } from "solid-js"
import { useLocal } from "@/context/local"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { popularProviders } from "@/hooks/use-providers"
import { Button } from "@opencode-ai/ui/button"
import { Tag } from "@opencode-ai/ui/tag"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import type { IconName } from "@opencode-ai/ui/icons/provider"
import { DialogSelectProvider } from "./dialog-select-provider"
import { DialogManageModels } from "./dialog-manage-models"
const ModelList: Component<{
provider?: string
class?: string
onSelect: () => void
}> = (props) => {
const local = useLocal()
const models = createMemo(() =>
local.model
.list()
.filter((m) => local.model.visible({ modelID: m.id, providerID: m.provider.id }))
.filter((m) => (props.provider ? m.provider.id === props.provider : true)),
)
return (
<List
class={`flex-1 min-h-0 [&_[data-slot=list-scroll]]:flex-1 [&_[data-slot=list-scroll]]:min-h-0 ${props.class ?? ""}`}
search={{ placeholder: "Search models", autofocus: true }}
emptyMessage="No model results"
key={(x) => `${x.provider.id}:${x.id}`}
items={models}
current={local.model.current()}
filterKeys={["provider.name", "name", "id"]}
sortBy={(a, b) => a.name.localeCompare(b.name)}
groupBy={(x) => x.provider.name}
groupHeader={(group) => (
<div class="flex items-center gap-x-3">
<ProviderIcon data-slot="list-item-extra-icon" id={group.items[0].provider.id as IconName} />
<span>{group.category}</span>
</div>
)}
sortGroupsBy={(a, b) => {
if (a.category === "Recent" && b.category !== "Recent") return -1
if (b.category === "Recent" && a.category !== "Recent") return 1
const aProvider = a.items[0].provider.id
const bProvider = b.items[0].provider.id
if (popularProviders.includes(aProvider) && !popularProviders.includes(bProvider)) return -1
if (!popularProviders.includes(aProvider) && popularProviders.includes(bProvider)) return 1
return popularProviders.indexOf(aProvider) - popularProviders.indexOf(bProvider)
}}
onSelect={(x) => {
local.model.set(x ? { modelID: x.id, providerID: x.provider.id } : undefined, {
recent: true,
})
props.onSelect()
}}
>
{(i) => (
<div class="w-full flex items-center gap-x-3 pl-1 text-13-regular">
<ProviderIcon data-slot="list-item-extra-icon" id={i.provider.id as IconName} />
<span class="truncate">{i.name}</span>
<Show when={i.provider.id === "opencode" && (!i.cost || i.cost?.input === 0)}>
<Tag>Free</Tag>
</Show>
<Show when={i.latest}>
<Tag>Latest</Tag>
</Show>
</div>
)}
</List>
)
}
export const ModelSelectorPopover: Component<{
provider?: string
children: JSX.Element
}> = (props) => {
const [open, setOpen] = createSignal(false)
return (
<Kobalte open={open()} onOpenChange={setOpen} placement="top-start" gutter={8}>
<Kobalte.Trigger as="div">{props.children}</Kobalte.Trigger>
<Kobalte.Portal>
<Kobalte.Content class="w-72 h-80 flex flex-col rounded-md border border-border-base bg-surface-raised-stronger-non-alpha shadow-md z-50 outline-none overflow-hidden">
<Kobalte.Title class="sr-only">Select model</Kobalte.Title>
<ModelList provider={props.provider} onSelect={() => setOpen(false)} class="p-1" />
</Kobalte.Content>
</Kobalte.Portal>
</Kobalte>
)
}
export const DialogSelectModel: Component<{ provider?: string }> = (props) => {
const dialog = useDialog()
return (
<Dialog
title="Select model"
action={
<Button
class="h-7 -my-1 text-14-medium"
icon="plus-small"
tabIndex={-1}
onClick={() => dialog.show(() => <DialogSelectProvider />)}
>
Connect provider
</Button>
}
>
<ModelList provider={props.provider} onSelect={() => dialog.close()} />
<Button
variant="ghost"
class="ml-3 mt-5 mb-6 text-text-base self-start"
onClick={() => dialog.show(() => <DialogManageModels />)}
>
Manage models
</Button>
</Dialog>
)
}
@@ -15,7 +15,6 @@ export const DialogSelectProvider: Component = () => {
return (
<Dialog title="Connect provider">
<List
class="px-2.5"
search={{ placeholder: "Search providers", autofocus: true }}
activeIcon="plus-small"
key={(x) => x?.id}
@@ -38,17 +37,8 @@ export const DialogSelectProvider: Component = () => {
}}
>
{(i) => (
<div class="px-1.25 w-full flex items-center gap-x-4">
<ProviderIcon
data-slot="list-item-extra-icon"
id={i.id as IconName}
// TODO: clean this up after we update icon in models.dev
classList={{
"text-icon-weak-base": true,
"size-4 mx-0.5": i.id === "opencode",
"size-5": i.id !== "opencode",
}}
/>
<div class="px-1.25 w-full flex items-center gap-x-3">
<ProviderIcon data-slot="list-item-extra-icon" id={i.id as IconName} />
<span>{i.name}</span>
<Show when={i.id === "opencode"}>
<Tag>Recommended</Tag>
@@ -0,0 +1,246 @@
import { createResource, createEffect, createMemo, onCleanup, Show } from "solid-js"
import { createStore, reconcile } from "solid-js/store"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
import { TextField } from "@opencode-ai/ui/text-field"
import { Button } from "@opencode-ai/ui/button"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { normalizeServerUrl, serverDisplayName, useServer } from "@/context/server"
import { usePlatform } from "@/context/platform"
import { createOpencodeClient } from "@opencode-ai/sdk/v2/client"
import { useNavigate } from "@solidjs/router"
type ServerStatus = { healthy: boolean; version?: string }
async function checkHealth(url: string, fetch?: typeof globalThis.fetch): Promise<ServerStatus> {
const sdk = createOpencodeClient({
baseUrl: url,
fetch,
signal: AbortSignal.timeout(3000),
})
return sdk.global
.health()
.then((x) => ({ healthy: x.data?.healthy === true, version: x.data?.version }))
.catch(() => ({ healthy: false }))
}
export function DialogSelectServer() {
const navigate = useNavigate()
const dialog = useDialog()
const server = useServer()
const platform = usePlatform()
const [store, setStore] = createStore({
url: "",
adding: false,
error: "",
status: {} as Record<string, ServerStatus | undefined>,
})
const [defaultUrl, defaultUrlActions] = createResource(() => platform.getDefaultServerUrl?.())
const isDesktop = platform.platform === "desktop"
const items = createMemo(() => {
const current = server.url
const list = server.list
if (!current) return list
if (!list.includes(current)) return [current, ...list]
return [current, ...list.filter((x) => x !== current)]
})
const current = createMemo(() => items().find((x) => x === server.url) ?? items()[0])
const sortedItems = createMemo(() => {
const list = items()
if (!list.length) return list
const active = current()
const order = new Map(list.map((url, index) => [url, index] as const))
const rank = (value?: ServerStatus) => {
if (value?.healthy === true) return 0
if (value?.healthy === false) return 2
return 1
}
return list.slice().sort((a, b) => {
if (a === active) return -1
if (b === active) return 1
const diff = rank(store.status[a]) - rank(store.status[b])
if (diff !== 0) return diff
return (order.get(a) ?? 0) - (order.get(b) ?? 0)
})
})
async function refreshHealth() {
const results: Record<string, ServerStatus> = {}
await Promise.all(
items().map(async (url) => {
results[url] = await checkHealth(url, platform.fetch)
}),
)
setStore("status", reconcile(results))
}
createEffect(() => {
items()
refreshHealth()
const interval = setInterval(refreshHealth, 10_000)
onCleanup(() => clearInterval(interval))
})
function select(value: string, persist?: boolean) {
if (!persist && store.status[value]?.healthy === false) return
dialog.close()
if (persist) {
server.add(value)
navigate("/")
return
}
server.setActive(value)
navigate("/")
}
async function handleSubmit(e: SubmitEvent) {
e.preventDefault()
const value = normalizeServerUrl(store.url)
if (!value) return
setStore("adding", true)
setStore("error", "")
const result = await checkHealth(value, platform.fetch)
setStore("adding", false)
if (!result.healthy) {
setStore("error", "Could not connect to server")
return
}
setStore("url", "")
select(value, true)
}
async function handleRemove(url: string) {
server.remove(url)
}
return (
<Dialog title="Servers" description="Switch which OpenCode server this app connects to.">
<div class="flex flex-col gap-4 pb-4">
<List
search={{ placeholder: "Search servers", autofocus: true }}
emptyMessage="No servers yet"
items={sortedItems}
key={(x) => x}
current={current()}
onSelect={(x) => {
if (x) select(x)
}}
>
{(i) => (
<div class="flex items-center gap-2 min-w-0 flex-1 group/item">
<div
class="flex items-center gap-2 min-w-0 flex-1"
classList={{ "opacity-50": store.status[i]?.healthy === false }}
>
<div
classList={{
"size-1.5 rounded-full shrink-0": true,
"bg-icon-success-base": store.status[i]?.healthy === true,
"bg-icon-critical-base": store.status[i]?.healthy === false,
"bg-border-weak-base": store.status[i] === undefined,
}}
/>
<span class="truncate">{serverDisplayName(i)}</span>
<span class="text-text-weak">{store.status[i]?.version}</span>
</div>
<Show when={current() !== i && server.list.includes(i)}>
<IconButton
icon="circle-x"
variant="ghost"
class="bg-transparent transition-opacity shrink-0 hover:scale-110"
onClick={(e) => {
e.stopPropagation()
handleRemove(i)
}}
/>
</Show>
</div>
)}
</List>
<div class="mt-6 px-3 flex flex-col gap-1.5">
<div class="px-3">
<h3 class="text-14-regular text-text-weak">Add a server</h3>
</div>
<form onSubmit={handleSubmit}>
<div class="flex items-start gap-2">
<div class="flex-1 min-w-0 h-auto">
<TextField
type="text"
label="Server URL"
hideLabel
placeholder="http://localhost:4096"
value={store.url}
onChange={(v) => {
setStore("url", v)
setStore("error", "")
}}
validationState={store.error ? "invalid" : "valid"}
error={store.error}
/>
</div>
<Button type="submit" variant="secondary" icon="plus-small" size="large" disabled={store.adding}>
{store.adding ? "Checking..." : "Add"}
</Button>
</div>
</form>
</div>
<Show when={isDesktop}>
<div class="mt-6 px-3 flex flex-col gap-1.5">
<div class="px-3">
<h3 class="text-14-regular text-text-weak">Default server</h3>
<p class="text-12-regular text-text-weak mt-1">
Connect to this server on app launch instead of starting a local server. Requires restart.
</p>
</div>
<div class="flex items-center gap-2 px-3 py-2">
<Show
when={defaultUrl()}
fallback={
<Show
when={server.url}
fallback={<span class="text-14-regular text-text-weak">No server selected</span>}
>
<Button
variant="secondary"
size="small"
onClick={async () => {
await platform.setDefaultServerUrl?.(server.url)
defaultUrlActions.refetch(server.url)
}}
>
Set current server as default
</Button>
</Show>
}
>
<div class="flex items-center gap-2 flex-1 min-w-0">
<span class="truncate text-14-regular">{serverDisplayName(defaultUrl()!)}</span>
</div>
<Button
variant="ghost"
size="small"
onClick={async () => {
await platform.setDefaultServerUrl?.(null)
defaultUrlActions.refetch()
}}
>
Clear
</Button>
</Show>
</div>
</div>
</Show>
</div>
</Dialog>
)
}
@@ -2,7 +2,7 @@ import { useLocal, type LocalFile } from "@/context/local"
import { Collapsible } from "@opencode-ai/ui/collapsible"
import { FileIcon } from "@opencode-ai/ui/file-icon"
import { Tooltip } from "@opencode-ai/ui/tooltip"
import { For, Match, Switch, Show, type ComponentProps, type ParentProps } from "solid-js"
import { For, Match, Switch, type ComponentProps, type ParentProps } from "solid-js"
import { Dynamic } from "solid-js/web"
export default function FileTree(props: {
@@ -57,14 +57,14 @@ export default function FileTree(props: {
"text-text-muted/40": p.node.ignored,
"text-text-muted/80": !p.node.ignored,
// "!text-text": local.file.active()?.path === p.node.path,
"!text-primary": local.file.changed(p.node.path),
// "!text-primary": local.file.changed(p.node.path),
}}
>
{p.node.name}
</span>
<Show when={local.file.changed(p.node.path)}>
<span class="ml-auto mr-1 w-1.5 h-1.5 rounded-full bg-primary/50 shrink-0" />
</Show>
{/* <Show when={local.file.changed(p.node.path)}> */}
{/* <span class="ml-auto mr-1 w-1.5 h-1.5 rounded-full bg-primary/50 shrink-0" /> */}
{/* </Show> */}
</Dynamic>
)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,103 @@
import { Match, Show, Switch, createMemo } from "solid-js"
import { Tooltip } from "@opencode-ai/ui/tooltip"
import { ProgressCircle } from "@opencode-ai/ui/progress-circle"
import { Button } from "@opencode-ai/ui/button"
import { useParams } from "@solidjs/router"
import { AssistantMessage } from "@opencode-ai/sdk/v2/client"
import { useLayout } from "@/context/layout"
import { useSync } from "@/context/sync"
interface SessionContextUsageProps {
variant?: "button" | "indicator"
}
export function SessionContextUsage(props: SessionContextUsageProps) {
const sync = useSync()
const params = useParams()
const layout = useLayout()
const variant = createMemo(() => props.variant ?? "button")
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
const tabs = createMemo(() => layout.tabs(sessionKey()))
const view = createMemo(() => layout.view(sessionKey()))
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
const cost = createMemo(() => {
const total = messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0)
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
}).format(total)
})
const context = createMemo(() => {
const last = messages().findLast((x) => {
if (x.role !== "assistant") return false
const total = x.tokens.input + x.tokens.output + x.tokens.reasoning + x.tokens.cache.read + x.tokens.cache.write
return total > 0
}) as AssistantMessage
if (!last) return
const total =
last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write
const model = sync.data.provider.all.find((x) => x.id === last.providerID)?.models[last.modelID]
return {
tokens: total.toLocaleString(),
percentage: model?.limit.context ? Math.round((total / model.limit.context) * 100) : null,
}
})
const openContext = () => {
if (!params.id) return
view().reviewPanel.open()
tabs().open("context")
tabs().setActive("context")
}
const circle = () => (
<div class="p-1">
<ProgressCircle size={16} strokeWidth={2} percentage={context()?.percentage ?? 0} />
</div>
)
const tooltipValue = () => (
<div>
<Show when={context()}>
{(ctx) => (
<>
<div class="flex items-center gap-2">
<span class="text-text-invert-strong">{ctx().tokens}</span>
<span class="text-text-invert-base">Tokens</span>
</div>
<div class="flex items-center gap-2">
<span class="text-text-invert-strong">{ctx().percentage ?? 0}%</span>
<span class="text-text-invert-base">Usage</span>
</div>
</>
)}
</Show>
<div class="flex items-center gap-2">
<span class="text-text-invert-strong">{cost()}</span>
<span class="text-text-invert-base">Cost</span>
</div>
<Show when={variant() === "button"}>
<div class="text-11-regular text-text-invert-base mt-1">Click to view context</div>
</Show>
</div>
)
return (
<Show when={params.id}>
<Tooltip value={tooltipValue()} placement="top">
<Switch>
<Match when={variant() === "indicator"}>{circle()}</Match>
<Match when={true}>
<Button type="button" variant="ghost" class="size-6" onClick={openContext}>
{circle()}
</Button>
</Match>
</Switch>
</Tooltip>
</Show>
)
}
@@ -0,0 +1,38 @@
import { createMemo, Show } from "solid-js"
import { useSync } from "@/context/sync"
import { Tooltip } from "@opencode-ai/ui/tooltip"
export function SessionLspIndicator() {
const sync = useSync()
const lspStats = createMemo(() => {
const lsp = sync.data.lsp ?? []
const connected = lsp.filter((s) => s.status === "connected").length
const hasError = lsp.some((s) => s.status === "error")
const total = lsp.length
return { connected, hasError, total }
})
const tooltipContent = createMemo(() => {
const lsp = sync.data.lsp ?? []
if (lsp.length === 0) return "No LSP servers"
return lsp.map((s) => s.name).join(", ")
})
return (
<Show when={lspStats().total > 0}>
<Tooltip placement="top" value={tooltipContent()}>
<div class="flex items-center gap-1 px-2 cursor-default select-none">
<div
classList={{
"size-1.5 rounded-full": true,
"bg-icon-critical-base": lspStats().hasError,
"bg-icon-success-base": !lspStats().hasError && lspStats().connected > 0,
}}
/>
<span class="text-12-regular text-text-weak">{lspStats().connected} LSP</span>
</div>
</Tooltip>
</Show>
)
}
@@ -0,0 +1,34 @@
import { createMemo, Show } from "solid-js"
import { Button } from "@opencode-ai/ui/button"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { useSync } from "@/context/sync"
import { DialogSelectMcp } from "@/components/dialog-select-mcp"
export function SessionMcpIndicator() {
const sync = useSync()
const dialog = useDialog()
const mcpStats = createMemo(() => {
const mcp = sync.data.mcp ?? {}
const entries = Object.entries(mcp)
const enabled = entries.filter(([, status]) => status.status === "connected").length
const failed = entries.some(([, status]) => status.status === "failed")
const total = entries.length
return { enabled, failed, total }
})
return (
<Show when={mcpStats().total > 0}>
<Button variant="ghost" onClick={() => dialog.show(() => <DialogSelectMcp />)}>
<div
classList={{
"size-1.5 rounded-full": true,
"bg-icon-critical-base": mcpStats().failed,
"bg-icon-success-base": !mcpStats().failed && mcpStats().enabled > 0,
}}
/>
<span class="text-12-regular text-text-weak">{mcpStats().enabled} MCP</span>
</Button>
</Show>
)
}
@@ -0,0 +1,5 @@
export { SessionHeader } from "./session-header"
export { SessionContextTab } from "./session-context-tab"
export { SortableTab, FileVisual } from "./session-sortable-tab"
export { SortableTerminalTab } from "./session-sortable-terminal-tab"
export { NewSessionView } from "./session-new-view"
@@ -0,0 +1,425 @@
import { createMemo, createEffect, on, onCleanup, For, Show } from "solid-js"
import type { JSX } from "solid-js"
import { useParams } from "@solidjs/router"
import { DateTime } from "luxon"
import { useSync } from "@/context/sync"
import { useLayout } from "@/context/layout"
import { checksum } from "@opencode-ai/util/encode"
import { Icon } from "@opencode-ai/ui/icon"
import { Accordion } from "@opencode-ai/ui/accordion"
import { StickyAccordionHeader } from "@opencode-ai/ui/sticky-accordion-header"
import { Code } from "@opencode-ai/ui/code"
import { Markdown } from "@opencode-ai/ui/markdown"
import type { AssistantMessage, Message, Part, UserMessage } from "@opencode-ai/sdk/v2/client"
interface SessionContextTabProps {
messages: () => Message[]
visibleUserMessages: () => UserMessage[]
view: () => ReturnType<ReturnType<typeof useLayout>["view"]>
info: () => ReturnType<ReturnType<typeof useSync>["session"]["get"]>
}
export function SessionContextTab(props: SessionContextTabProps) {
const params = useParams()
const sync = useSync()
const ctx = createMemo(() => {
const last = props.messages().findLast((x) => {
if (x.role !== "assistant") return false
const total = x.tokens.input + x.tokens.output + x.tokens.reasoning + x.tokens.cache.read + x.tokens.cache.write
return total > 0
}) as AssistantMessage
if (!last) return
const provider = sync.data.provider.all.find((x) => x.id === last.providerID)
const model = provider?.models[last.modelID]
const limit = model?.limit.context
const input = last.tokens.input
const output = last.tokens.output
const reasoning = last.tokens.reasoning
const cacheRead = last.tokens.cache.read
const cacheWrite = last.tokens.cache.write
const total = input + output + reasoning + cacheRead + cacheWrite
const usage = limit ? Math.round((total / limit) * 100) : null
return {
message: last,
provider,
model,
limit,
input,
output,
reasoning,
cacheRead,
cacheWrite,
total,
usage,
}
})
const cost = createMemo(() => {
const total = props.messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0)
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
}).format(total)
})
const counts = createMemo(() => {
const all = props.messages()
const user = all.reduce((count, x) => count + (x.role === "user" ? 1 : 0), 0)
const assistant = all.reduce((count, x) => count + (x.role === "assistant" ? 1 : 0), 0)
return {
all: all.length,
user,
assistant,
}
})
const systemPrompt = createMemo(() => {
const msg = props.visibleUserMessages().findLast((m) => !!m.system)
const system = msg?.system
if (!system) return
const trimmed = system.trim()
if (!trimmed) return
return trimmed
})
const number = (value: number | null | undefined) => {
if (value === undefined) return "—"
if (value === null) return "—"
return value.toLocaleString()
}
const percent = (value: number | null | undefined) => {
if (value === undefined) return "—"
if (value === null) return "—"
return value.toString() + "%"
}
const time = (value: number | undefined) => {
if (!value) return "—"
return DateTime.fromMillis(value).toLocaleString(DateTime.DATETIME_MED)
}
const providerLabel = createMemo(() => {
const c = ctx()
if (!c) return "—"
return c.provider?.name ?? c.message.providerID
})
const modelLabel = createMemo(() => {
const c = ctx()
if (!c) return "—"
if (c.model?.name) return c.model.name
return c.message.modelID
})
const breakdown = createMemo(
on(
() => [ctx()?.message.id, ctx()?.input, props.messages().length, systemPrompt()],
() => {
const c = ctx()
if (!c) return []
const input = c.input
if (!input) return []
const out = {
system: systemPrompt()?.length ?? 0,
user: 0,
assistant: 0,
tool: 0,
}
for (const msg of props.messages()) {
const parts = (sync.data.part[msg.id] ?? []) as Part[]
if (msg.role === "user") {
for (const part of parts) {
if (part.type === "text") out.user += part.text.length
if (part.type === "file") out.user += part.source?.text.value.length ?? 0
if (part.type === "agent") out.user += part.source?.value.length ?? 0
}
continue
}
if (msg.role === "assistant") {
for (const part of parts) {
if (part.type === "text") out.assistant += part.text.length
if (part.type === "reasoning") out.assistant += part.text.length
if (part.type === "tool") {
out.tool += Object.keys(part.state.input).length * 16
if (part.state.status === "pending") out.tool += part.state.raw.length
if (part.state.status === "completed") out.tool += part.state.output.length
if (part.state.status === "error") out.tool += part.state.error.length
}
}
}
}
const estimateTokens = (chars: number) => Math.ceil(chars / 4)
const system = estimateTokens(out.system)
const user = estimateTokens(out.user)
const assistant = estimateTokens(out.assistant)
const tool = estimateTokens(out.tool)
const estimated = system + user + assistant + tool
const pct = (tokens: number) => (tokens / input) * 100
const pctLabel = (tokens: number) => (Math.round(pct(tokens) * 10) / 10).toString() + "%"
const build = (tokens: { system: number; user: number; assistant: number; tool: number; other: number }) => {
return [
{
key: "system",
label: "System",
tokens: tokens.system,
width: pct(tokens.system),
percent: pctLabel(tokens.system),
color: "var(--syntax-info)",
},
{
key: "user",
label: "User",
tokens: tokens.user,
width: pct(tokens.user),
percent: pctLabel(tokens.user),
color: "var(--syntax-success)",
},
{
key: "assistant",
label: "Assistant",
tokens: tokens.assistant,
width: pct(tokens.assistant),
percent: pctLabel(tokens.assistant),
color: "var(--syntax-property)",
},
{
key: "tool",
label: "Tool Calls",
tokens: tokens.tool,
width: pct(tokens.tool),
percent: pctLabel(tokens.tool),
color: "var(--syntax-warning)",
},
{
key: "other",
label: "Other",
tokens: tokens.other,
width: pct(tokens.other),
percent: pctLabel(tokens.other),
color: "var(--syntax-comment)",
},
].filter((x) => x.tokens > 0)
}
if (estimated <= input) {
return build({ system, user, assistant, tool, other: input - estimated })
}
const scale = input / estimated
const scaled = {
system: Math.floor(system * scale),
user: Math.floor(user * scale),
assistant: Math.floor(assistant * scale),
tool: Math.floor(tool * scale),
}
const scaledTotal = scaled.system + scaled.user + scaled.assistant + scaled.tool
return build({ ...scaled, other: Math.max(0, input - scaledTotal) })
},
),
)
function Stat(statProps: { label: string; value: JSX.Element }) {
return (
<div class="flex flex-col gap-1">
<div class="text-12-regular text-text-weak">{statProps.label}</div>
<div class="text-12-medium text-text-strong">{statProps.value}</div>
</div>
)
}
const stats = createMemo(() => {
const c = ctx()
const count = counts()
return [
{ label: "Session", value: props.info()?.title ?? params.id ?? "—" },
{ label: "Messages", value: count.all.toLocaleString() },
{ label: "Provider", value: providerLabel() },
{ label: "Model", value: modelLabel() },
{ label: "Context Limit", value: number(c?.limit) },
{ label: "Total Tokens", value: number(c?.total) },
{ label: "Usage", value: percent(c?.usage) },
{ label: "Input Tokens", value: number(c?.input) },
{ label: "Output Tokens", value: number(c?.output) },
{ label: "Reasoning Tokens", value: number(c?.reasoning) },
{ label: "Cache Tokens (read/write)", value: `${number(c?.cacheRead)} / ${number(c?.cacheWrite)}` },
{ label: "User Messages", value: count.user.toLocaleString() },
{ label: "Assistant Messages", value: count.assistant.toLocaleString() },
{ label: "Total Cost", value: cost() },
{ label: "Session Created", value: time(props.info()?.time.created) },
{ label: "Last Activity", value: time(c?.message.time.created) },
] satisfies { label: string; value: JSX.Element }[]
})
function RawMessageContent(msgProps: { message: Message }) {
const file = createMemo(() => {
const parts = (sync.data.part[msgProps.message.id] ?? []) as Part[]
const contents = JSON.stringify({ message: msgProps.message, parts }, null, 2)
return {
name: `${msgProps.message.role}-${msgProps.message.id}.json`,
contents,
cacheKey: checksum(contents),
}
})
return <Code file={file()} overflow="wrap" class="select-text" />
}
function RawMessage(msgProps: { message: Message }) {
return (
<Accordion.Item value={msgProps.message.id}>
<StickyAccordionHeader>
<Accordion.Trigger>
<div class="flex items-center justify-between gap-2 w-full">
<div class="min-w-0 truncate">
{msgProps.message.role} <span class="text-text-base"> {msgProps.message.id}</span>
</div>
<div class="flex items-center gap-3">
<div class="shrink-0 text-12-regular text-text-weak">{time(msgProps.message.time.created)}</div>
<Icon name="chevron-grabber-vertical" size="small" class="shrink-0 text-text-weak" />
</div>
</div>
</Accordion.Trigger>
</StickyAccordionHeader>
<Accordion.Content class="bg-background-base">
<div class="p-3">
<RawMessageContent message={msgProps.message} />
</div>
</Accordion.Content>
</Accordion.Item>
)
}
let scroll: HTMLDivElement | undefined
let frame: number | undefined
let pending: { x: number; y: number } | undefined
const restoreScroll = (retries = 0) => {
const el = scroll
if (!el) return
const s = props.view()?.scroll("context")
if (!s) return
// Wait for content to be scrollable - content may not have rendered yet
if (el.scrollHeight <= el.clientHeight && retries < 10) {
requestAnimationFrame(() => restoreScroll(retries + 1))
return
}
if (el.scrollTop !== s.y) el.scrollTop = s.y
if (el.scrollLeft !== s.x) el.scrollLeft = s.x
}
const handleScroll = (event: Event & { currentTarget: HTMLDivElement }) => {
pending = {
x: event.currentTarget.scrollLeft,
y: event.currentTarget.scrollTop,
}
if (frame !== undefined) return
frame = requestAnimationFrame(() => {
frame = undefined
const next = pending
pending = undefined
if (!next) return
props.view().setScroll("context", next)
})
}
createEffect(
on(
() => props.messages().length,
() => {
requestAnimationFrame(restoreScroll)
},
{ defer: true },
),
)
onCleanup(() => {
if (frame === undefined) return
cancelAnimationFrame(frame)
})
return (
<div
class="@container h-full overflow-y-auto no-scrollbar pb-10"
ref={(el) => {
scroll = el
restoreScroll()
}}
onScroll={handleScroll}
>
<div class="px-6 pt-4 flex flex-col gap-10">
<div class="grid grid-cols-1 @[32rem]:grid-cols-2 gap-4">
<For each={stats()}>{(stat) => <Stat label={stat.label} value={stat.value} />}</For>
</div>
<Show when={breakdown().length > 0}>
<div class="flex flex-col gap-2">
<div class="text-12-regular text-text-weak">Context Breakdown</div>
<div class="h-2 w-full rounded-full bg-surface-base overflow-hidden flex">
<For each={breakdown()}>
{(segment) => (
<div
class="h-full"
style={{
width: `${segment.width}%`,
"background-color": segment.color,
}}
/>
)}
</For>
</div>
<div class="flex flex-wrap gap-x-3 gap-y-1">
<For each={breakdown()}>
{(segment) => (
<div class="flex items-center gap-1 text-11-regular text-text-weak">
<div class="size-2 rounded-sm" style={{ "background-color": segment.color }} />
<div>{segment.label}</div>
<div class="text-text-weaker">{segment.percent}</div>
</div>
)}
</For>
</div>
<div class="hidden text-11-regular text-text-weaker">
Approximate breakdown of input tokens. "Other" includes tool definitions and overhead.
</div>
</div>
</Show>
<Show when={systemPrompt()}>
{(prompt) => (
<div class="flex flex-col gap-2">
<div class="text-12-regular text-text-weak">System Prompt</div>
<div class="border border-border-base rounded-md bg-surface-base px-3 py-2">
<Markdown text={prompt()} class="text-12-regular" />
</div>
</div>
)}
</Show>
<div class="flex flex-col gap-2">
<div class="text-12-regular text-text-weak">Raw messages</div>
<Accordion multiple>
<For each={props.messages()}>{(message) => <RawMessage message={message} />}</For>
</Accordion>
</div>
</div>
</div>
)
}
@@ -0,0 +1,267 @@
import { createMemo, createResource, Show } from "solid-js"
import { A, useNavigate, useParams } from "@solidjs/router"
import { useLayout } from "@/context/layout"
import { useCommand } from "@/context/command"
import { useServer } from "@/context/server"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { useSync } from "@/context/sync"
import { useGlobalSDK } from "@/context/global-sdk"
import { getFilename } from "@opencode-ai/util/path"
import { base64Decode, base64Encode } from "@opencode-ai/util/encode"
import { iife } from "@opencode-ai/util/iife"
import { Icon } from "@opencode-ai/ui/icon"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { Button } from "@opencode-ai/ui/button"
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { Select } from "@opencode-ai/ui/select"
import { Popover } from "@opencode-ai/ui/popover"
import { TextField } from "@opencode-ai/ui/text-field"
import { DialogSelectServer } from "@/components/dialog-select-server"
import { SessionLspIndicator } from "@/components/session-lsp-indicator"
import { SessionMcpIndicator } from "@/components/session-mcp-indicator"
import type { Session } from "@opencode-ai/sdk/v2/client"
import { same } from "@/utils/same"
export function SessionHeader() {
const globalSDK = useGlobalSDK()
const layout = useLayout()
const params = useParams()
const navigate = useNavigate()
const command = useCommand()
const server = useServer()
const dialog = useDialog()
const sync = useSync()
const projectDirectory = createMemo(() => base64Decode(params.dir ?? ""))
const sessions = createMemo(() => (sync.data.session ?? []).filter((s) => !s.parentID))
const currentSession = createMemo(() => sync.data.session.find((s) => s.id === params.id))
const parentSession = createMemo(() => {
const current = currentSession()
if (!current?.parentID) return undefined
return sync.data.session.find((s) => s.id === current.parentID)
})
const shareEnabled = createMemo(() => sync.data.config.share !== "disabled")
const worktrees = createMemo(() => layout.projects.list().map((p) => p.worktree), [], { equals: same })
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
const view = createMemo(() => layout.view(sessionKey()))
function navigateToProject(directory: string) {
navigate(`/${base64Encode(directory)}`)
}
function navigateToSession(session: Session | undefined) {
if (!session) return
// Only navigate if we're actually changing to a different session
if (session.id === params.id) return
navigate(`/${params.dir}/session/${session.id}`)
}
return (
<header class="h-12 shrink-0 bg-background-base border-b border-border-weak-base flex">
<button
type="button"
class="xl:hidden w-12 shrink-0 flex items-center justify-center border-r border-border-weak-base hover:bg-surface-raised-base-hover active:bg-surface-raised-base-active transition-colors"
onClick={layout.mobileSidebar.toggle}
>
<Icon name="menu" size="small" />
</button>
<div class="px-4 flex items-center justify-between gap-4 w-full">
<div class="flex items-center gap-3 min-w-0">
<div class="flex items-center gap-2 min-w-0">
<div class="hidden xl:flex items-center gap-2">
<Select
options={worktrees()}
current={sync.project?.worktree ?? projectDirectory()}
label={(x) => getFilename(x)}
onSelect={(x) => (x ? navigateToProject(x) : undefined)}
class="text-14-regular text-text-base"
variant="ghost"
>
{/* @ts-ignore */}
{(i) => (
<div class="flex items-center gap-2">
<Icon name="folder" size="small" />
<div class="text-text-strong">{getFilename(i)}</div>
</div>
)}
</Select>
<div class="text-text-weaker">/</div>
</div>
<Show
when={parentSession()}
fallback={
<>
<Select
options={sessions()}
current={currentSession()}
placeholder="New session"
label={(x) => x.title}
value={(x) => x.id}
onSelect={navigateToSession}
class="text-14-regular text-text-base max-w-[calc(100vw-180px)] md:max-w-md"
variant="ghost"
/>
</>
}
>
<div class="flex items-center gap-2 min-w-0">
<Select
options={sessions()}
current={parentSession()}
placeholder="Back to parent session"
label={(x) => x.title}
value={(x) => x.id}
onSelect={(session) => {
// Only navigate if selecting a different session than current parent
const currentParent = parentSession()
if (session && currentParent && session.id !== currentParent.id) {
navigateToSession(session)
}
}}
class="text-14-regular text-text-base max-w-[calc(100vw-180px)] md:max-w-md"
variant="ghost"
/>
<div class="text-text-weaker">/</div>
<div class="flex items-center gap-1.5 min-w-0">
<Tooltip value="Back to parent session">
<button
type="button"
class="flex items-center justify-center gap-1 p-1 rounded hover:bg-surface-raised-base-hover active:bg-surface-raised-base-active transition-colors flex-shrink-0"
onClick={() => navigateToSession(parentSession())}
>
<Icon name="arrow-left" size="small" class="text-icon-base" />
</button>
</Tooltip>
</div>
</div>
</Show>
</div>
<Show when={currentSession() && !parentSession()}>
<TooltipKeybind class="hidden xl:block" title="New session" keybind={command.keybind("session.new")}>
<IconButton as={A} href={`/${params.dir}/session`} icon="edit-small-2" variant="ghost" />
</TooltipKeybind>
</Show>
</div>
<div class="flex items-center gap-3">
<div class="hidden md:flex items-center gap-1">
<Button
size="small"
variant="ghost"
onClick={() => {
dialog.show(() => <DialogSelectServer />)
}}
>
<div
classList={{
"size-1.5 rounded-full": true,
"bg-icon-success-base": server.healthy() === true,
"bg-icon-critical-base": server.healthy() === false,
"bg-border-weak-base": server.healthy() === undefined,
}}
/>
<Icon name="server" size="small" class="text-icon-weak" />
<span class="text-12-regular text-text-weak truncate max-w-[200px]">{server.name}</span>
</Button>
<SessionLspIndicator />
<SessionMcpIndicator />
</div>
<div class="flex items-center gap-1">
<Show when={currentSession()?.summary?.files}>
<TooltipKeybind
class="hidden md:block shrink-0"
title="Toggle review"
keybind={command.keybind("review.toggle")}
>
<Button
variant="ghost"
class="group/review-toggle size-6 p-0"
onClick={() => view().reviewPanel.toggle()}
>
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
<Icon
name={view().reviewPanel.opened() ? "layout-right" : "layout-left"}
size="small"
class="group-hover/review-toggle:hidden"
/>
<Icon
name={view().reviewPanel.opened() ? "layout-right-partial" : "layout-left-partial"}
size="small"
class="hidden group-hover/review-toggle:inline-block"
/>
<Icon
name={view().reviewPanel.opened() ? "layout-right-full" : "layout-left-full"}
size="small"
class="hidden group-active/review-toggle:inline-block"
/>
</div>
</Button>
</TooltipKeybind>
</Show>
<TooltipKeybind
class="hidden md:block shrink-0"
title="Toggle terminal"
keybind={command.keybind("terminal.toggle")}
>
<Button variant="ghost" class="group/terminal-toggle size-6 p-0" onClick={() => view().terminal.toggle()}>
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
<Icon
size="small"
name={view().terminal.opened() ? "layout-bottom-full" : "layout-bottom"}
class="group-hover/terminal-toggle:hidden"
/>
<Icon
size="small"
name="layout-bottom-partial"
class="hidden group-hover/terminal-toggle:inline-block"
/>
<Icon
size="small"
name={view().terminal.opened() ? "layout-bottom" : "layout-bottom-full"}
class="hidden group-active/terminal-toggle:inline-block"
/>
</div>
</Button>
</TooltipKeybind>
</div>
<Show when={shareEnabled() && currentSession()}>
<Popover
title="Share session"
trigger={
<Tooltip class="shrink-0" value="Share session">
<IconButton icon="share" variant="ghost" class="" />
</Tooltip>
}
>
{iife(() => {
const [url] = createResource(
() => currentSession(),
async (session) => {
if (!session) return
let shareURL = session.share?.url
if (!shareURL) {
shareURL = await globalSDK.client.session
.share({ sessionID: session.id, directory: projectDirectory() })
.then((r) => r.data?.share?.url)
.catch((e) => {
console.error("Failed to share session", e)
return undefined
})
}
return shareURL
},
{ initialValue: "" },
)
return (
<Show when={url.latest}>
{(shareUrl) => <TextField value={shareUrl()} readOnly copyable class="w-72" />}
</Show>
)
})}
</Popover>
</Show>
</div>
</div>
</header>
)
}
@@ -0,0 +1,89 @@
import { Show, createMemo } from "solid-js"
import { DateTime } from "luxon"
import { useSync } from "@/context/sync"
import { Icon } from "@opencode-ai/ui/icon"
import { getDirectory, getFilename } from "@opencode-ai/util/path"
import { Select } from "@opencode-ai/ui/select"
const MAIN_WORKTREE = "main"
const CREATE_WORKTREE = "create"
interface NewSessionViewProps {
worktree: string
onWorktreeChange: (value: string) => void
}
export function NewSessionView(props: NewSessionViewProps) {
const sync = useSync()
const sandboxes = createMemo(() => sync.project?.sandboxes ?? [])
const options = createMemo(() => [MAIN_WORKTREE, ...sandboxes(), CREATE_WORKTREE])
const current = createMemo(() => {
const selection = props.worktree
if (options().includes(selection)) return selection
return MAIN_WORKTREE
})
const projectRoot = createMemo(() => sync.project?.worktree ?? sync.data.path.directory)
const isWorktree = createMemo(() => {
const project = sync.project
if (!project) return false
return sync.data.path.directory !== project.worktree
})
const label = (value: string) => {
if (value === MAIN_WORKTREE) {
if (isWorktree()) return "Main branch"
const branch = sync.data.vcs?.branch
if (branch) return `Main branch (${branch})`
return "Main branch"
}
if (value === CREATE_WORKTREE) return "Create new worktree"
return getFilename(value)
}
return (
<div
class="size-full flex flex-col pb-45 justify-end items-start gap-4 flex-[1_0_0] self-stretch max-w-200 mx-auto px-6"
style={{ "padding-bottom": "calc(var(--prompt-height, 11.25rem) + 64px)" }}
>
<div class="text-20-medium text-text-weaker">New session</div>
<div class="flex justify-center items-center gap-3">
<Icon name="folder" size="small" />
<div class="text-12-medium text-text-weak">
{getDirectory(projectRoot())}
<span class="text-text-strong">{getFilename(projectRoot())}</span>
</div>
</div>
<div class="flex justify-center items-center gap-1">
<Icon name="branch" size="small" />
<Select
options={options()}
current={current()}
value={(x) => x}
label={label}
onSelect={(value) => {
props.onWorktreeChange(value ?? MAIN_WORKTREE)
}}
size="normal"
variant="ghost"
class="text-12-medium"
/>
</div>
<Show when={sync.project}>
{(project) => (
<div class="flex justify-center items-center gap-3">
<Icon name="pencil-line" size="small" />
<div class="text-12-medium text-text-weak">
Last modified&nbsp;
<span class="text-text-strong">
{DateTime.fromMillis(project().time.updated ?? project().time.created).toRelative()}
</span>
</div>
</div>
)}
</Show>
</div>
)
}
@@ -0,0 +1,49 @@
import { createMemo, Show } from "solid-js"
import type { JSX } from "solid-js"
import { createSortable } from "@thisbeyond/solid-dnd"
import { FileIcon } from "@opencode-ai/ui/file-icon"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tooltip } from "@opencode-ai/ui/tooltip"
import { Tabs } from "@opencode-ai/ui/tabs"
import { getFilename } from "@opencode-ai/util/path"
import { useFile } from "@/context/file"
export function FileVisual(props: { path: string; active?: boolean }): JSX.Element {
return (
<div class="flex items-center gap-x-1.5">
<FileIcon
node={{ path: props.path, type: "file" }}
classList={{
"grayscale-100 group-data-[selected]/tab:grayscale-0": !props.active,
"grayscale-0": props.active,
}}
/>
<span class="text-14-medium">{getFilename(props.path)}</span>
</div>
)
}
export function SortableTab(props: { tab: string; onTabClose: (tab: string) => void }): JSX.Element {
const file = useFile()
const sortable = createSortable(props.tab)
const path = createMemo(() => file.pathFromTab(props.tab))
return (
// @ts-ignore
<div use:sortable classList={{ "h-full": true, "opacity-0": sortable.isActiveDraggable }}>
<div class="relative h-full">
<Tabs.Trigger
value={props.tab}
closeButton={
<Tooltip value="Close tab" placement="bottom">
<IconButton icon="close" variant="ghost" onClick={() => props.onTabClose(props.tab)} />
</Tooltip>
}
hideCloseButton
onMiddleClick={() => props.onTabClose(props.tab)}
>
<Show when={path()}>{(p) => <FileVisual path={p()} />}</Show>
</Tabs.Trigger>
</div>
</div>
)
}
@@ -0,0 +1,27 @@
import type { JSX } from "solid-js"
import { createSortable } from "@thisbeyond/solid-dnd"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tabs } from "@opencode-ai/ui/tabs"
import { useTerminal, type LocalPTY } from "@/context/terminal"
export function SortableTerminalTab(props: { terminal: LocalPTY }): JSX.Element {
const terminal = useTerminal()
const sortable = createSortable(props.terminal.id)
return (
// @ts-ignore
<div use:sortable classList={{ "h-full": true, "opacity-0": sortable.isActiveDraggable }}>
<div class="relative h-full">
<Tabs.Trigger
value={props.terminal.id}
closeButton={
terminal.all().length > 1 && (
<IconButton icon="close" variant="ghost" onClick={() => terminal.close(props.terminal.id)} />
)
}
>
{props.terminal.title}
</Tabs.Trigger>
</div>
</div>
)
}
+296
View File
@@ -0,0 +1,296 @@
import type { Ghostty, Terminal as Term, FitAddon } from "ghostty-web"
import { ComponentProps, createEffect, createSignal, onCleanup, onMount, splitProps } from "solid-js"
import { useSDK } from "@/context/sdk"
import { SerializeAddon } from "@/addons/serialize"
import { LocalPTY } from "@/context/terminal"
import { resolveThemeVariant, useTheme, withAlpha, type HexColor } from "@opencode-ai/ui/theme"
export interface TerminalProps extends ComponentProps<"div"> {
pty: LocalPTY
onSubmit?: () => void
onCleanup?: (pty: LocalPTY) => void
onConnectError?: (error: unknown) => void
}
type TerminalColors = {
background: string
foreground: string
cursor: string
selectionBackground: string
}
const DEFAULT_TERMINAL_COLORS: Record<"light" | "dark", TerminalColors> = {
light: {
background: "#fcfcfc",
foreground: "#211e1e",
cursor: "#211e1e",
selectionBackground: withAlpha("#211e1e", 0.2),
},
dark: {
background: "#191515",
foreground: "#d4d4d4",
cursor: "#d4d4d4",
selectionBackground: withAlpha("#d4d4d4", 0.25),
},
}
export const Terminal = (props: TerminalProps) => {
const sdk = useSDK()
const theme = useTheme()
let container!: HTMLDivElement
const [local, others] = splitProps(props, ["pty", "class", "classList", "onConnectError"])
let ws: WebSocket | undefined
let term: Term | undefined
let ghostty: Ghostty
let serializeAddon: SerializeAddon
let fitAddon: FitAddon
let handleResize: () => void
let handleTextareaFocus: () => void
let handleTextareaBlur: () => void
let reconnect: number | undefined
let disposed = false
const getTerminalColors = (): TerminalColors => {
const mode = theme.mode()
const fallback = DEFAULT_TERMINAL_COLORS[mode]
const currentTheme = theme.themes()[theme.themeId()]
if (!currentTheme) return fallback
const variant = mode === "dark" ? currentTheme.dark : currentTheme.light
if (!variant?.seeds) return fallback
const resolved = resolveThemeVariant(variant, mode === "dark")
const text = resolved["text-stronger"] ?? fallback.foreground
const background = resolved["background-stronger"] ?? fallback.background
const alpha = mode === "dark" ? 0.25 : 0.2
const base = text.startsWith("#") ? (text as HexColor) : (fallback.foreground as HexColor)
const selectionBackground = withAlpha(base, alpha)
return {
background,
foreground: text,
cursor: text,
selectionBackground,
}
}
const [terminalColors, setTerminalColors] = createSignal<TerminalColors>(getTerminalColors())
createEffect(() => {
const colors = getTerminalColors()
setTerminalColors(colors)
if (!term) return
const setOption = (term as unknown as { setOption?: (key: string, value: TerminalColors) => void }).setOption
if (!setOption) return
setOption("theme", colors)
})
const focusTerminal = () => {
const t = term
if (!t) return
t.focus()
setTimeout(() => t.textarea?.focus(), 0)
}
const handlePointerDown = () => {
const activeElement = document.activeElement
if (activeElement instanceof HTMLElement && activeElement !== container) {
activeElement.blur()
}
focusTerminal()
}
onMount(async () => {
const mod = await import("ghostty-web")
ghostty = await mod.Ghostty.load()
const url = new URL(sdk.url + `/pty/${local.pty.id}/connect?directory=${encodeURIComponent(sdk.directory)}`)
if (window.__OPENCODE__?.serverPassword) {
url.username = "opencode"
url.password = window.__OPENCODE__?.serverPassword
}
const socket = new WebSocket(url)
ws = socket
const t = new mod.Terminal({
cursorBlink: true,
cursorStyle: "bar",
fontSize: 14,
fontFamily: "IBM Plex Mono, monospace",
allowTransparency: true,
theme: terminalColors(),
scrollback: 10_000,
ghostty,
})
term = t
const copy = () => {
const selection = t.getSelection()
if (!selection) return false
const body = document.body
if (body) {
const textarea = document.createElement("textarea")
textarea.value = selection
textarea.setAttribute("readonly", "")
textarea.style.position = "fixed"
textarea.style.opacity = "0"
body.appendChild(textarea)
textarea.select()
const copied = document.execCommand("copy")
body.removeChild(textarea)
if (copied) return true
}
const clipboard = navigator.clipboard
if (clipboard?.writeText) {
clipboard.writeText(selection).catch(() => {})
return true
}
return false
}
t.attachCustomKeyEventHandler((event) => {
const key = event.key.toLowerCase()
if (event.ctrlKey && event.shiftKey && !event.metaKey && key === "c") {
copy()
return true
}
if (event.metaKey && !event.ctrlKey && !event.altKey && key === "c") {
if (!t.hasSelection()) return true
copy()
return true
}
// allow for ctrl-` to toggle terminal in parent
if (event.ctrlKey && key === "`") {
return true
}
return false
})
fitAddon = new mod.FitAddon()
serializeAddon = new SerializeAddon()
t.loadAddon(serializeAddon)
t.loadAddon(fitAddon)
t.open(container)
container.addEventListener("pointerdown", handlePointerDown)
handleTextareaFocus = () => {
t.options.cursorBlink = true
}
handleTextareaBlur = () => {
t.options.cursorBlink = false
}
t.textarea?.addEventListener("focus", handleTextareaFocus)
t.textarea?.addEventListener("blur", handleTextareaBlur)
focusTerminal()
if (local.pty.buffer) {
if (local.pty.rows && local.pty.cols) {
t.resize(local.pty.cols, local.pty.rows)
}
t.write(local.pty.buffer, () => {
if (local.pty.scrollY) {
t.scrollToLine(local.pty.scrollY)
}
fitAddon.fit()
})
}
fitAddon.observeResize()
handleResize = () => fitAddon.fit()
window.addEventListener("resize", handleResize)
t.onResize(async (size) => {
if (socket.readyState === WebSocket.OPEN) {
await sdk.client.pty
.update({
ptyID: local.pty.id,
size: {
cols: size.cols,
rows: size.rows,
},
})
.catch(() => {})
}
})
t.onData((data) => {
if (socket.readyState === WebSocket.OPEN) {
socket.send(data)
}
})
t.onKey((key) => {
if (key.key == "Enter") {
props.onSubmit?.()
}
})
// t.onScroll((ydisp) => {
// console.log("Scroll position:", ydisp)
// })
socket.addEventListener("open", () => {
console.log("WebSocket connected")
sdk.client.pty
.update({
ptyID: local.pty.id,
size: {
cols: t.cols,
rows: t.rows,
},
})
.catch(() => {})
})
socket.addEventListener("message", (event) => {
t.write(event.data)
})
socket.addEventListener("error", (error) => {
console.error("WebSocket error:", error)
props.onConnectError?.(error)
})
socket.addEventListener("close", () => {
console.log("WebSocket disconnected")
})
})
onCleanup(() => {
if (handleResize) {
window.removeEventListener("resize", handleResize)
}
container.removeEventListener("pointerdown", handlePointerDown)
term?.textarea?.removeEventListener("focus", handleTextareaFocus)
term?.textarea?.removeEventListener("blur", handleTextareaBlur)
const t = term
if (serializeAddon && props.onCleanup && t) {
const buffer = serializeAddon.serialize()
props.onCleanup({
...local.pty,
buffer,
rows: t.rows,
cols: t.cols,
scrollY: t.getViewportY(),
})
}
ws?.close()
t?.dispose()
})
return (
<div
ref={container}
data-component="terminal"
data-prevent-autofocus
style={{ "background-color": terminalColors().background }}
classList={{
...(local.classList ?? {}),
"select-text": true,
"size-full px-6 py-3 font-mono": true,
[local.class ?? ""]: !!local.class,
}}
{...others}
/>
)
}

Some files were not shown because too many files have changed in this diff Show More