Compare commits

...

1613 Commits

Author SHA1 Message Date
opencode ddf4897eaa release: v1.0.205
Update Nix Hashes / update (push) Has been cancelled
2025-12-28 18:37:58 +00:00
Adam 040939fb72 chore: cleanup theme stuff 2025-12-28 10:21:32 -06:00
Adam f89b83a6d7 chore: cleanup theme stuff 2025-12-28 10:14:30 -06:00
Adam 82a876da4d chore: cleanup 2025-12-28 06:41:59 -06:00
GitHub Action 69a15ae9c1 ignore: update download stats 2025-12-28 2025-12-28 12:04:31 +00:00
Adam 18c8e5f451 chore: cleanup 2025-12-28 05:47:22 -06:00
Adam ba3a1cfa0b chore: cleanup 2025-12-28 05:47:21 -06:00
Github Action d8563160f7 Update Nix flake.lock and hashes 2025-12-28 11:13:54 +00:00
Adam 4a9ff9412e feat(desktop): themes 2025-12-28 05:12:36 -06:00
Matt Silverlock d6db6ff198 fix: handle non-text response parts in GitHub action (#6173) 2025-12-27 21:24:10 -06:00
Aiden Cline 79c263494f tweak: inform agent if no skills are available 2025-12-27 21:20:00 -06:00
Adam 1b5bf32ce5 chore: permissions ux 2025-12-27 20:40:25 -06:00
Adam 2e972b3fdc fix(desktop): copy/paste in terminal 2025-12-27 20:18:59 -06:00
Adam d70e9fb01e chore(desktop): cleanup 2025-12-27 19:59:16 -06:00
Adam fc082a0f14 fix(desktop): drag file over entire body to attach 2025-12-27 19:49:35 -06:00
Adam 953e4e9446 chore(desktop): vertical tabs 2025-12-27 19:43:52 -06:00
rektide 7ea0d37ee3 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 e35d97f9d7 feat: add bash shell completions (#6239) 2025-12-27 19:14:56 -06:00
GitHub Action 2c0d9a46cb chore: generate 2025-12-28 01:12:02 +00:00
Nindaleth 2fe7a7f2d3 docs: document attach command (#6254)
Co-authored-by: Black_Fox <radekliska@gmail.com>
2025-12-27 19:11:30 -06:00
Connor Adams 8a2f4ddf70 chore: update INVALID_DIRS to include plural 'skills' directory (#6255) 2025-12-27 19:10:51 -06:00
processtrader 7a94d7a2c5 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 de28fafb47 fix: search all recent models instead of only top 5 in TUI /models command 2025-12-27 19:07:38 -06:00
Ivan Pantic 9d485dd307 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 613813ac12 chore: generate 2025-12-28 00:53:48 +00:00
ewired 7617f59441 Allow line numbers and ranges in autocomplete (#4238) 2025-12-27 18:53:17 -06:00
opencode 7aecb43e84 release: v1.0.204
Update Nix Hashes / update (push) Has been cancelled
2025-12-27 20:51:09 +00:00
Adam 21eba5f987 feat(desktop): permissions 2025-12-27 14:43:42 -06:00
Adam c523ca4127 wip(desktop): handle more errors 2025-12-27 14:33:22 -06:00
GitHub Action 685f3ea324 ignore: update download stats 2025-12-27 2025-12-27 12:04:27 +00:00
Aiden Cline 4667d57e3c ci: stale issues 2025-12-27 00:51:05 -06:00
Didier Durand e6b9988fa4 doc: fix typos in various files (#6238) 2025-12-27 00:46:06 -06:00
rari404 3c02d5d338 feat: add path traversal protection to File.read and File.list (#5985) 2025-12-26 23:20:07 -06:00
Christopher Ochsenreither bfb9787361 fix: compact command after revert now properly cleans up revert state (#6235) 2025-12-26 22:57:59 -06:00
ja 1bcc72c477 feat: add ability to disable spinner animation (#6084) 2025-12-26 22:12:35 -06:00
Adam 4385fa4dd7 fix(desktop): prompt input fixes, directory and branch in status bar 2025-12-26 20:47:13 -06:00
Dax Raad 2b054bec95 core: fix compaction config checks to properly respect user settings 2025-12-26 19:48:56 -05:00
Dax Raad 2cdc88d295 core: add compaction config tests to verify auto and prune settings work correctly 2025-12-26 19:44:32 -05:00
GitHub Action f8fb08b3b4 chore: generate 2025-12-27 00:32:34 +00:00
Dax Raad ed06de5e30 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 52b99622ad zen: add context for login errors 2025-12-26 17:32:39 -05:00
Github Action a15397cd89 Update Nix flake.lock and hashes 2025-12-26 20:49:06 +00:00
GitHub Action da394439a1 chore: generate 2025-12-26 20:48:30 +00:00
Adam 390b0a79b3 fix(core): mdns global config 2025-12-26 14:47:53 -06:00
Adam b2f45d574f Reapply "feat(core): optional mdns service (#6192)"
This reverts commit 505068d5a6.
2025-12-26 14:47:53 -06:00
Aiden Cline 1e2ef07c97 chore: kill some unused tools 2025-12-26 14:31:22 -06:00
Aiden Cline 664e6bf2d0 test: add more tests to make sure that cwd is locked for read tool 2025-12-26 14:30:05 -06:00
Aiden Cline 160c8ab7cc tweak: bash tool description to avoid unnecessary 'cd &&' usage 2025-12-26 13:44:52 -06:00
Matt Silverlock 1626341a4a github: support issues and workflow_dispatch events (#6157) 2025-12-26 13:34:03 -06:00
Aiden Cline 61ddd1716d ci: re-enable sync zed 2025-12-26 12:24:14 -06:00
Aiden Cline 053a10e515 ci: fix token for gh 2025-12-26 12:22:56 -06:00
Aiden Cline e1c1b1340b ci: fix var 2025-12-26 12:08:16 -06:00
Aiden Cline 7a5fbdf67c ci: update zed extension sync 2025-12-26 12:06:36 -06:00
Github Action 9afc451020 Update Nix flake.lock and hashes 2025-12-26 17:45:58 +00:00
GitHub Action f4fdf0eb03 chore: generate 2025-12-26 17:45:03 +00:00
Aiden Cline 505068d5a6 Revert "feat(core): optional mdns service (#6192)"
This reverts commit 26e7043718.
2025-12-26 11:43:52 -06:00
Aiden Cline 2e10ffac6b chore: rm comments 2025-12-26 11:43:13 -06:00
Aiden Cline 4abaa052db 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 1bcf8d8806 fix: opencode web baseURL error (#6181) 2025-12-26 11:36:31 -06:00
Ariane Emory 25c68c8061 chore: kill the dead Polaris Alpha code (#6193) 2025-12-26 11:32:31 -06:00
ja b0e4408ecf feat: add shfmt formatter for shell scripts (#6204) 2025-12-26 11:31:51 -06:00
Aiden Cline 8416db03ef tweak: make install script handle 404s better 2025-12-26 11:28:18 -06:00
Github Action d5b47d9128 Update Nix flake.lock and hashes 2025-12-26 17:09:54 +00:00
GitHub Action 634559760a chore: generate 2025-12-26 17:09:31 +00:00
Ayush Walekar 155ba794cf chore: createOpencodeServer expose logLevel (#6202) 2025-12-26 11:09:06 -06:00
Roberto Carvajal f1ab427f0e 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 2333af6ed3 Desktop: MCP UI (#6162)
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
2025-12-26 10:49:05 -06:00
GitHub Action 54588b4570 chore: generate 2025-12-26 16:30:20 +00:00
Adam 26e7043718 feat(core): optional mdns service (#6192)
Co-authored-by: Github Action <action@github.com>
2025-12-26 10:29:48 -06:00
GitHub Action dd569c927a chore: generate 2025-12-26 16:22:05 +00:00
Didier Durand cf38884778 doc: fix typos in various files (#6196) 2025-12-26 10:21:33 -06:00
GitHub Action 2946a6d9a7 ignore: update download stats 2025-12-26 2025-12-26 12:10:30 +00:00
Aiden Cline 3522c460e3 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 b6a264819e chore: generate 2025-12-26 04:25:19 +00:00
JackNorris 46c7a41d5f fix: only show diagnostics block when errors exist (#6175) 2025-12-25 22:24:48 -06:00
opencode 7cc4b24ac2 release: v1.0.203
Update Nix Hashes / update (push) Has been cancelled
2025-12-26 04:10:11 +00:00
Dax Raad 281ce4c0c3 prompt update to prevent searching via bash tool 2025-12-25 23:07:39 -05:00
Donghyun Shin f59d274d0f fix(lsp): make JDTLS use the correct config directory on Windows (#6121) 2025-12-25 21:17:54 -06:00
GitHub Action 8886c78dce chore: generate 2025-12-26 03:05:15 +00:00
Marco d9f0f58277 feat: haskell lsp support (#6141) 2025-12-25 21:04:43 -06:00
opencode effa7b45cf release: v1.0.202
Update Nix Hashes / update (push) Has been cancelled
2025-12-26 02:11:47 +00:00
Adam b307075063 chore: brain icon 2025-12-25 20:06:41 -06:00
Adam aaf9a5d434 fix(desktop): user message display 2025-12-25 19:45:20 -06:00
Adam e9c2f1f3f3 fix(desktop): padding 2025-12-25 19:22:16 -06:00
Adam 7469cba7cf fix(desktop): move session context to top-right 2025-12-25 19:21:04 -06:00
Adam 5420702f69 fix(desktop): missing keybinds in tooltips 2025-12-25 19:07:42 -06:00
Adam 583751ecae fix(desktop): markdown rendering perf 2025-12-25 19:07:42 -06:00
GitHub Action d0a1b5ef96 chore: generate 2025-12-26 01:03:22 +00:00
Adam 42f2bc7199 fix(desktop): can't collapse project with active session 2025-12-25 19:02:43 -06:00
Adam 603dae562a chore(ui): radio group primitive 2025-12-25 18:46:57 -06:00
Adam 650bd76370 feat(desktop): better indicator that session is busy 2025-12-25 14:31:10 -06:00
opencode 8aa3520683 release: v1.0.201
Update Nix Hashes / update (push) Has been cancelled
2025-12-25 14:07:19 +00:00
Adam 5b5b8c57d9 fix(desktop): so many prompt input fixes, merry christmas 2025-12-25 08:04:42 -06:00
GitHub Action f057b22e20 ignore: update download stats 2025-12-25 2025-12-25 12:04:54 +00:00
GitHub Action 388d40e41f chore: generate 2025-12-25 05:59:27 +00:00
Dax Raad f397c92ddf remove list tool 2025-12-25 00:58:47 -05:00
opencode 6f9bea4e1f release: v1.0.200
Update Nix Hashes / update (push) Has been cancelled
2025-12-25 04:39:32 +00:00
Adam 5c49b4cbfc fix(desktop): scroll jank in session turn and review 2025-12-24 22:36:45 -06:00
GitHub Action b746e831e2 chore: generate 2025-12-25 04:17:39 +00:00
Adam 2178deef91 fix(desktop): override agent model 2025-12-24 22:16:58 -06:00
Adam b1d2fb5319 fix(desktop): reconcile session diff updates 2025-12-24 22:16:44 -06:00
opencode 2284a4e6df release: v1.0.199
Update Nix Hashes / update (push) Has been cancelled
2025-12-25 02:40:19 +00:00
Adam ad852d9186 chore: toast on file load error 2025-12-24 20:37:12 -06:00
Adam 8a9b4245b4 chore: cleanup dead code 2025-12-24 20:30:52 -06:00
Adam 76ac1ccb6b chore: show version on error page 2025-12-24 20:25:36 -06:00
Adam e71bc8c0b0 fix(desktop): show server connection failure 2025-12-24 20:11:37 -06:00
Ahmed Mansour a5301e2ab7 fix: correct Content-Type headers for static assets on app.opencode.ai (#6113) 2025-12-24 20:00:22 -06:00
Connor Adams 8eac72341f docs: update skills to use canonical ~/.config/opencode location (#6132) 2025-12-24 18:18:33 -06:00
Robb Tolliver bd139b4bd6 docs: Corrected the number of built-in subagents in documentation (#6133) 2025-12-24 18:02:35 -06:00
GitHub Action 508578bf17 chore: generate 2025-12-24 19:21:10 +00:00
Dax Raad 607d8aafb7 tui: disable tips display in home route 2025-12-24 14:20:29 -05:00
Dax Raad 5843eca7d6 CI 2025-12-24 18:16:46 +00:00
opencode ff3b68bd36 release: v1.0.198
Update Nix Hashes / update (push) Has been cancelled
2025-12-24 18:16:46 +00:00
Dax Raad 474b6fd3d1 ci 2025-12-24 13:12:29 -05:00
GitHub Action 6145b197f3 chore: generate 2025-12-24 18:08:42 +00:00
Dax Raad 918eff9233 ci 2025-12-24 13:07:56 -05:00
opencode 987e444828 release: v1.0.197
Update Nix Hashes / update (push) Has been cancelled
2025-12-24 17:47:07 +00:00
Dax Raad 99633cb299 Revert "feat: better styling for small screens (short and/or not wide) (#5968)"
This reverts commit ac371d2987.
2025-12-24 12:38:10 -05:00
GitHub Action f822331eb8 chore: generate 2025-12-24 17:07:43 +00:00
Patrick Schiel 0f053769db docs: add infos about server debugging (#6085) 2025-12-24 11:07:12 -06:00
opencode ceeaf494c4 release: v1.0.196
Update Nix Hashes / update (push) Has been cancelled
2025-12-24 16:40:16 +00:00
Adam 126d887e57 fix(desktop): last text part streaming 2025-12-24 10:35:52 -06:00
Adam e5cfc24d6b fix(desktop): render perf 2025-12-24 10:26:49 -06:00
Jay V 7f8d659737 docs: edits 2025-12-24 11:23:51 -05:00
Jay V 4b061653f2 docs: add comprehensive CLI command documentation for agent, mcp, session, stats, and web commands 2025-12-24 11:12:09 -05:00
Jay V eeed89f985 docs: make MCP server documentation more scannable and add Sentry example 2025-12-24 10:49:48 -05:00
Adam 8ab533b616 chore: cleanup 2025-12-24 09:07:31 -06:00
Adam 09a399d8d6 fix(desktop): summary flicker 2025-12-24 09:07:31 -06:00
Adam b75575884a feat(desktop): show read tool args 2025-12-24 09:07:31 -06:00
GitHub Action 5688c9fd61 chore: generate 2025-12-24 14:56:15 +00:00
Adam 08a075df61 fix(desktop): better session navigation, hide child sessions 2025-12-24 08:55:32 -06:00
opencode a2e8737114 release: v1.0.195
Update Nix Hashes / update (push) Has been cancelled
2025-12-24 14:50:40 +00:00
Adam 776a394b02 chore: cleanup 2025-12-24 08:46:11 -06:00
GitHub Action 5788b33fdf chore: generate 2025-12-24 14:38:25 +00:00
Adam 0f270c3da4 refactor(ui): rewrite createAutoScroll with robust event tracking to fix sticky behavior 2025-12-24 08:37:49 -06:00
opencode 376019e347 release: v1.0.194
Update Nix Hashes / update (push) Has been cancelled
2025-12-24 12:20:02 +00:00
Adam 44b773a6f6 chore: cleanup 2025-12-24 06:16:17 -06:00
Adam df97774f7f fix(desktop): session sort when multiple active 2025-12-24 06:16:17 -06:00
Adam eeff62a912 fix(share): page title should be session title 2025-12-24 06:16:17 -06:00
GitHub Action 3fc6c42f5f ignore: update download stats 2025-12-24 2025-12-24 12:04:46 +00:00
Adam 967d8238be fix(desktop): exclude deprecated models 2025-12-24 06:01:27 -06:00
Adam bff7518a24 fix(desktop): auto-scroll 2025-12-24 05:57:48 -06:00
Adam 8eab677094 fix: don't disable text selection 2025-12-24 05:57:48 -06:00
Github Action db57e7023a Update Nix flake.lock and hashes 2025-12-24 11:56:43 +00:00
Adam ede4e467db deps: update marked and marked-shiki 2025-12-24 05:55:28 -06:00
Adam aa1c560e5e fix(desktop): hang on backtracing-prone regex 2025-12-24 05:49:35 -06:00
Adam 3aca9e5fa5 fix(desktop): conditionally show review pane toggle 2025-12-24 05:22:25 -06:00
Ryan Vogel 9e96d83164 fix: remove SVG favicon to improve SEO (#5755) 2025-12-24 05:17:13 -06:00
Aiden Cline 4275907df6 docs: tweak lsp.mdx 2025-12-23 22:38:17 -06:00
opencode-agent[bot] 6097d6af86 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] 09d2febe27 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 2c5c1ecb5e docs: add Neovim to the list of editors (#6081) 2025-12-23 22:17:34 -06:00
Aiden Cline 99e2112807 tweak: retry err 2025-12-23 22:10:28 -06:00
GitHub Action 4b6575999d chore: generate 2025-12-24 01:37:35 +00:00
Frank 1a9ee3080c zen: sync 2025-12-23 20:36:55 -05:00
Abdelkader Boudih f4d61be8bd 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 8b40e38cd7 test: add test for retry 2025-12-23 19:34:40 -06:00
Aiden Cline 7396d495ee chore: regen sdk 2025-12-23 19:34:38 -06:00
GitHub Action f9b5ce180a chore: generate 2025-12-24 01:21:10 +00:00
Aiden Cline 12ee9d51c3 make 'The socket connection was closed unexpectedly' errors retryable 2025-12-23 19:20:31 -06:00
Rohan Mukherjee 2730e0c9cd chore: update AGENTS.md to ~150 lines (#5955) 2025-12-23 19:04:44 -06:00
David Hill d6c81d6e14 style: update current todo style (#6077) 2025-12-23 18:57:02 -06:00
rari404 e8ac0b663b 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 2806f240ea fix: resize textarea when pasting prompt less than 150 chars (#6070) 2025-12-23 18:11:48 -06:00
Matt Silverlock 9898fbe8ef providers: add Cloudflare AI Gateway (#5174) 2025-12-23 17:31:58 -06:00
Frank 1bd8e61719 ci: adam is not a full stack engineer 2025-12-23 17:03:00 -05:00
Adam b6c07cb1b8 fix: remove desktop dup 2025-12-23 15:31:18 -06:00
Viktor Nagy 83f23817ce Update gitlab.mdx to use the 2.x component version (#6062) 2025-12-23 14:56:46 -06:00
Github Action 23b1d7c755 Update Nix flake.lock and hashes 2025-12-23 20:42:17 +00:00
Aiden Cline ef033db9c2 Revert "Add animated braille spinner to terminal title when agent is running (#5984)"
This reverts commit 59b87f60f7.
2025-12-23 14:40:55 -06:00
Aiden Cline e30d5d8e34 tweak: update import & pr commands to use new share link ur 2025-12-23 14:04:33 -06:00
GitHub Action 698cfb57a1 chore: generate 2025-12-23 19:48:40 +00:00
Jon Redeker 27e72f2652 Add opencode-shell-strategy plugin to ecosystem (#5995) 2025-12-23 13:48:08 -06:00
ja 10eed6ee7e feat(install): add standard CLI flags (--help, --version, --no-modify-path) (#5885) 2025-12-23 13:47:32 -06:00
David Hill 59b87f60f7 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 d10089a0bf chore: generate 2025-12-23 19:29:05 +00:00
David Hill ae7286c031 "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 52048c327d fix: favorites and recents stay visible when filtering models (#6053) 2025-12-23 12:55:47 -06:00
Matt Silverlock 4e1a9b6216 docs: add MCP OAuth debugging section (#6047) 2025-12-23 12:46:59 -06:00
Aiden Cline 1995be3599 ci: update zed sync script 2025-12-23 12:45:34 -06:00
ja 86b9b7b15a fix(tui): prevent keybinds from executing when dialog is open (#6017) 2025-12-23 12:37:28 -06:00
Frank a90f2b9723 ci: fix 2025-12-23 12:58:28 -05:00
GitHub Action c73a17f8af chore: generate 2025-12-23 15:58:33 +00:00
Rhys Sullivan 48898fda07 [feat]: prompt stashing (#6021) 2025-12-23 09:58:00 -06:00
GitHub Action c573732ddb chore: generate 2025-12-23 15:39:47 +00:00
Daniel Polito ab2a6c45a3 Fix Github Pull Request Event (#6037) 2025-12-23 09:39:14 -06:00
opencode 66563fb974 release: v1.0.193
Update Nix Hashes / update (push) Has been cancelled
2025-12-23 15:16:20 +00:00
GitHub Action fbece0dc4d chore: generate 2025-12-23 14:45:27 +00:00
Sebastian Herrlinger 1d9e181da0 indent wrapped todo items properly 2025-12-23 15:44:45 +01:00
opencode c81721e9fc release: v1.0.192
Update Nix Hashes / update (push) Has been cancelled
2025-12-23 14:43:07 +00:00
Frank a94899ed36 zen: glm 4.7 2025-12-23 07:56:37 -05:00
Frank b18d22498c ci: fix 2025-12-23 07:53:33 -05:00
Frank c75584a31b ci: fix 2025-12-23 07:38:42 -05:00
GitHub Action b474f65547 ignore: update download stats 2025-12-23 2025-12-23 12:04:42 +00:00
Sebastian Herrlinger c352999b41 no intermediate autocomplete result to avoid flickering 2025-12-23 12:22:34 +01:00
GitHub Action f4cd708ca0 chore: generate 2025-12-23 10:15:49 +00:00
Brendan Allan c20f2731ab desktop: kill_sidecar before update install on windows 2025-12-23 18:14:44 +08:00
Github Action 01ca1a384a Update Nix flake.lock and hashes 2025-12-23 10:12:37 +00:00
GitHub Action f330dadd89 chore: generate 2025-12-23 10:11:53 +00:00
Adam 43e92b4932 deps: diffs, shiki updates 2025-12-23 04:08:42 -06:00
opencode 83397ebde2 release: v1.0.191
Update Nix Hashes / update (push) Has been cancelled
2025-12-23 05:57:23 +00:00
GitHub Action fde74a72bb chore: generate 2025-12-23 05:53:02 +00:00
Brendan Allan 10ee8e5b3d console: add AppImage download link 2025-12-23 13:52:23 +08:00
GitHub Action 96d3f1fe7c chore: generate 2025-12-23 04:28:11 +00:00
Matt Silverlock 1a2b656c4d improve mcp CLI + ability to debug MCP oauth (#5980) 2025-12-22 22:27:38 -06:00
Aiden Cline 161e9287a8 ci: docs sync 2025-12-22 22:27:21 -06:00
opencode-agent[bot] 968543af39 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 5af35117db 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 eab177f5e7 feat(plugin): allow compaction hook to replace prompt entirely (#5907) 2025-12-22 22:19:14 -06:00
Brendan Allan 279dc04b3c ci: rename tauri -> desktop 2025-12-23 11:15:19 +08:00
Github Action cbc5903aa1 Update Nix flake.lock and hashes 2025-12-23 02:03:30 +00:00
Adam 81c3c63895 chore: rename packages/tauri -> packages/desktop 2025-12-22 20:01:25 -06:00
Github Action b76bd4141d Update Nix flake.lock and hashes 2025-12-23 01:40:34 +00:00
Adam 794fe8f381 chore: rename packages/desktop -> packages/app 2025-12-22 19:39:00 -06:00
GitHub Action a4eebf9f08 chore: generate 2025-12-23 01:17:33 +00:00
Adam 680a63e3de fix(desktop): better error messages on connection failure 2025-12-22 19:16:54 -06:00
Mohammad Alhashemi 3a54ab68d1 feat(skill): add per-agent filtering to skill tool description (#6000) 2025-12-22 20:14:33 -05:00
Frank 44fd0eee64 zen: glm 4.7 2025-12-22 19:36:07 -05:00
Aiden Cline ac371d2987 feat: better styling for small screens (short and/or not wide) (#5968) 2025-12-22 18:00:26 -06:00
GitHub Action a7baa5ce18 chore: generate 2025-12-22 23:40:52 +00:00
Dax Raad b129f809b9 tui: change task tool container to block layout for better subagent session display 2025-12-22 18:40:15 -05:00
opencode 92c0ab51e2 release: v1.0.190
Update Nix Hashes / update (push) Has been cancelled
2025-12-22 23:31:20 +00:00
GitHub Action b25418e68b chore: generate 2025-12-22 23:24:39 +00:00
Mohammad Alhashemi 046e351140 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 b9029afa22 release: v1.0.189
Update Nix Hashes / update (push) Has been cancelled
2025-12-22 23:15:23 +00:00
GitHub Action b229aeec0b chore: generate 2025-12-22 22:47:22 +00:00
Jay V c9140c6bab docs: edit gitlab 2025-12-22 22:47:22 +00:00
opencode 38551bda38 release: v1.0.188
Update Nix Hashes / update (push) Has been cancelled
2025-12-22 22:47:21 +00:00
Github Action cd16d31510 Update Nix flake.lock and hashes 2025-12-22 22:37:48 +00:00
Frank 54ba1af5d6 remove sharp 2025-12-22 17:36:23 -05:00
Josh Thomas fe3144ce5b fix(tui): resize textarea if text inserted via appendPrompt TUI API (#5983) 2025-12-22 16:29:18 -06:00
Frank a1c0bae3af zen: add glm 4.7 2025-12-22 17:23:33 -05:00
Aiden Cline 85f8655dfd ignore: agents.md 2025-12-22 16:21:56 -06:00
Adam 9b6c9f64f7 feat(desktop): review pane toggle 2025-12-22 16:20:17 -06:00
Viktor Nagy 1aae1c795d 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 526c723e62 support glm 4.7 2025-12-22 17:11:02 -05:00
GitHub Action 6011200128 chore: generate 2025-12-22 22:01:27 +00:00
Jay V 740fcd243c ignore: update GitHub stars to 41K and project stats to reflect current growth 2025-12-22 17:00:27 -05:00
opencode e4d8a117c4 release: v1.0.187
Update Nix Hashes / update (push) Has been cancelled
2025-12-22 21:58:41 +00:00
Aiden Cline 8c4a816cf6 ci: add failure case for changelog 2025-12-22 15:53:41 -06:00
Aiden Cline 5605fc3f38 test: rm claude skills test 2025-12-22 15:45:31 -06:00
Aiden Cline 009b096004 fix: disable claude skill loading for now 2025-12-22 15:40:08 -06:00
Shpetim 64f898601b 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 224e5466c1 docs: add opencode-morph-fast-apply plugin to ecosystem (#5992) 2025-12-22 15:21:14 -06:00
Blake North 87b5b34280 fix(providers.opencode): check config for api key in addition to auth (#5906) 2025-12-22 15:20:40 -06:00
Github Action 855fd07d22 Update Nix flake.lock and hashes 2025-12-22 21:13:50 +00:00
Aiden Cline f9be2bab3a fix: bundle more providers to fix breaking ai sdk issue 2025-12-22 15:12:18 -06:00
Rohan Godha 25f1643e8e feat(tui): go to parent keybind for subagents (#5762) 2025-12-22 14:50:45 -06:00
GitHub Action e015bea462 chore: generate 2025-12-22 20:34:21 +00:00
wienans 7dc55ac3ca Add OpenChamber to ecosystem documentation (#5978) 2025-12-22 14:33:45 -06:00
ja cd8ecf9722 feat(lsp): add Tinymist LSP support for Typst (#5933) 2025-12-22 14:31:47 -06:00
Github Action eb021a5f92 Update Nix flake.lock and hashes 2025-12-22 20:27:29 +00:00
Sebastian Herrlinger 7f5e30834f upgrade opentui to v0.1.63, enabling kitty alternate keys by default 2025-12-22 21:26:03 +01:00
Tim Kleinschmidt 750a936ae1 support clojure projects with built-in lsp (#5975) 2025-12-22 14:20:15 -06:00
Shpetim 8dfef670b3 [FEATURE]: Show context usage in OpenCode Desktop Context usage (#5979) 2025-12-22 13:56:36 -06:00
Adam 1b1b73b5b3 fix(prompt): better summary prompt 2025-12-22 13:09:12 -06:00
Daniel Polito 6baee0791f docs: Github Auto Pull Request Docs (#5974) 2025-12-22 11:53:58 -06:00
Adam 291b65977c chore(desktop): auto scroll utility 2025-12-22 11:27:27 -06:00
GitHub Action 90f232d7f1 chore: generate 2025-12-22 17:06:35 +00:00
Will Marella af214d35cb 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 3f0afd7cf6 ci: tweak docs prompt 2025-12-22 11:00:32 -06:00
Daniel Polito 0545c5da2d GitHub pull request event (#5335) 2025-12-22 10:59:02 -06:00
Adam 4a32fa6f02 fix(share): expanded state and responsiveness 2025-12-22 10:13:57 -06:00
Aiden Cline 29c99ed4ab ci: limit to opencode repo 2025-12-22 09:56:51 -06:00
Lekë Dobruna 753abbe164 fix: duplicate words in dialog options (#5944) 2025-12-22 09:56:32 -06:00
Adam 8e01f6cc13 fix(desktop): diff readability (colors) 2025-12-22 09:51:21 -06:00
Dax Raad 33c0b125cb fix url for web 2025-12-22 10:45:51 -05:00
GitHub Action dab2e54df8 chore: generate 2025-12-22 14:38:20 +00:00
Buck Evan 60db171b44 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 c6e9a5c800 release: v1.0.186
Update Nix Hashes / update (push) Has been cancelled
2025-12-22 12:14:06 +00:00
Adam 2c16b9fa61 Revert "server: ensure frontend has correct port for PTY websocket connections (#5898)"
This reverts commit a05915ddc8.
2025-12-22 06:05:46 -06:00
Adam 240ad31edd Revert "fix: server"
This reverts commit dbaac79039.
2025-12-22 06:05:46 -06:00
GitHub Action a97631f769 ignore: update download stats 2025-12-22 2025-12-22 12:05:15 +00:00
Adam dbaac79039 fix: server 2025-12-22 06:02:16 -06:00
Ashutosh Kumar a05915ddc8 server: ensure frontend has correct port for PTY websocket connections (#5898) 2025-12-22 05:56:23 -06:00
Adam eebbd73346 Revert "fix: use current page port instead of hardcoded 4096 (#5949)"
This reverts commit d04a72a4ad.
2025-12-22 05:55:15 -06:00
Adam d4c981495a fix(desktop): cleanup auto scroll 2025-12-22 05:46:07 -06:00
Adam 653c206688 feat(desktop): mobile responsiveness 2025-12-22 05:46:07 -06:00
Adam 580f46b589 fix(desktop): filter child sessions from header 2025-12-22 05:46:07 -06:00
Adam 986d12fd20 feat(desktop): better task tool rendering 2025-12-22 05:46:06 -06:00
lif d04a72a4ad 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 5fd873a35a 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 a9fbd786b3 ci: fix tauri build args 2025-12-22 18:55:01 +08:00
Brendan Allan abde984b3e ci: verbose build and re-enable appimage 2025-12-22 18:48:58 +08:00
GitHub Action a95aa037a3 chore: generate 2025-12-22 10:06:29 +00:00
Brendan Allan 11a92b24c2 ci: run prepare step for tauri build 2025-12-22 18:05:52 +08:00
Brendan Allan f9c10c62d8 ci: try downloading artifact in desktop prepare 2025-12-22 17:45:57 +08:00
NN708 6339f39871 feat(desktop): arm64 build for linux (#5935) 2025-12-22 03:45:30 -06:00
Brendan Allan 68b09b30a1 ci: replace with just upload-artifact whole dir 2025-12-22 17:17:17 +08:00
Brendan Allan 92ade2a320 ci: import bun shell 2025-12-22 17:10:43 +08:00
Brendan Allan cb1a1fb26c try uploading artifacts in workflow 2025-12-22 17:08:44 +08:00
Brendan Allan af5ebabd03 remove actions artifact uploading 2025-12-22 17:01:52 +08:00
Github Action fe2626a4ea Update Nix flake.lock and hashes 2025-12-22 08:58:57 +00:00
GitHub Action 45447e3336 chore: generate 2025-12-22 08:58:16 +00:00
Brendan Allan 7a3e82ec5d ci: try to upload cli artifacts 2025-12-22 16:57:28 +08:00
Aiden Cline 345f4801e8 feat: add experimental lsp tool (#5886) 2025-12-22 00:34:21 -06:00
Luo Chen ac4b8d62e3 feat: add nixd as lsp for nix language (#5929) 2025-12-22 00:30:08 -06:00
opencode-agent[bot] 236ce7a8c0 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 8bdc0c8f79 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 04650f01fe docs: add ai-sdk-provider-opencode-sdk to ecosystem (#5772) 2025-12-21 22:59:36 -06:00
Aiden Cline 02d4594abf ci: update docs prompt 2025-12-21 22:32:04 -06:00
Aiden Cline c1894b4e3d ci: add automatic doc update workflow 2025-12-21 21:52:44 -06:00
Neil Daquioag 2062247e72 fix: support clipboard image paste (Ctrl+V) on Windows (#5919) 2025-12-21 21:18:47 -06:00
Aiden Cline 8785bec29c tweak: adjust minimax m2 topK and add minimax m2.1 topP 2025-12-21 21:17:58 -06:00
Noam Bressler d4b7f75ce3 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 4f73d58031 prompts: improve built-in /review prompt (#5918) 2025-12-21 21:11:48 -06:00
YeonGyu-Kim b906f2de88 feat(server): expose auto param in session.summarize for plugins (#5924) 2025-12-21 21:05:30 -06:00
GitHub Action 4035afe5c8 chore: generate 2025-12-22 00:45:30 +00:00
Dax 8fe0715928 feat: add Agent Skills support (#5921) 2025-12-21 19:44:56 -05:00
opencode cb8af962cd release: v1.0.185
Update Nix Hashes / update (push) Has been cancelled
2025-12-21 23:38:40 +00:00
Dax Raad c333ffa38b 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 3456f4ed80 tweak: update kimi-k2 and kimi-k2-thinking to use recommended temperature values 2025-12-21 15:54:57 -06:00
Dax Raad 2536e9f45b 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 9188bc542c Update Nix flake.lock and hashes 2025-12-21 17:46:30 +00:00
GitHub Action cbaba10994 chore: generate 2025-12-21 17:45:51 +00:00
Sherlock Holmes 85d3604309 fix(deps): add missing @opencode-ai/plugin to dependencies (#5797) 2025-12-21 11:45:20 -06:00
Nalin Singh 507ba644cf feat: add syntax highlighting for .ets files (#5889) 2025-12-21 11:42:47 -06:00
Adam Hosker 3d6f62746a fix: prevent stats workflow from running on forks (#5897) 2025-12-21 11:32:00 -06:00
Abdelkader Boudih 2f48c8c05f fix: use official MCP SDK for better tool schema handling (#5463) 2025-12-21 11:31:07 -06:00
GitHub Action 4828fd1eac chore: generate 2025-12-21 14:47:27 +00:00
Matt Silverlock 10375263ef github: support schedule events (#5810)
publish-github-action / publish (push) Has been cancelled
Update Nix Hashes / update (push) Has been cancelled
2025-12-21 08:46:54 -06:00
GitHub Action ae00001aa0 ignore: update download stats 2025-12-21 2025-12-21 12:04:17 +00:00
opencode f53ebafbab release: v1.0.184
Update Nix Hashes / update (push) Has been cancelled
2025-12-21 11:04:32 +00:00
Adam 23ebc50da9 fix(desktop): layout regression 2025-12-21 05:01:30 -06:00
Adam 673c6f97b7 fix(desktop): better keybind tooltips 2025-12-21 04:56:20 -06:00
Adam ec46f71258 fix(desktop): todo tool title 2025-12-21 04:41:54 -06:00
Adam 8865e524cb fix(desktop): allow text selection 2025-12-21 04:39:54 -06:00
GitHub Action 36bb02ae45 chore: generate 2025-12-21 10:36:43 +00:00
Adam 5072331f04 fix(desktop): incorrect state dir on macos 2025-12-21 04:36:02 -06:00
opencode 9d48fd4bbd release: v1.0.183
Update Nix Hashes / update (push) Has been cancelled
2025-12-21 10:14:41 +00:00
Adam bf66390557 fix(desktop): better error reporting 2025-12-21 04:11:09 -06:00
Adam 184643f0db fix(desktop): non-latin file paths failed 2025-12-21 04:06:10 -06:00
Adam 1bce898ca7 fix(desktop): file loading errors 2025-12-21 04:02:00 -06:00
Github Action 8c895570c6 Update Nix flake.lock and hashes 2025-12-21 06:11:08 +00:00
Christopher Tso 6dc4e5ac93 Make CLI build script Windows-friendly (#5835) 2025-12-21 00:09:58 -06:00
Aiden Cline d3922f0965 core: add verification that at least 1 primary agent is enabled, add regression tests (#5881) 2025-12-20 21:36:22 -06:00
GitHub Action cfaac9f2e1 chore: generate 2025-12-21 03:07:26 +00:00
opencode 0b046d6cf0 release: v1.0.182
Update Nix Hashes / update (push) Has been cancelled
2025-12-21 03:07:26 +00:00
Aiden Cline 3d822e5f79 fix: regression where config would error despite valid agents 2025-12-20 21:04:37 -06:00
opencode f9cef22a53 release: v1.0.181
Update Nix Hashes / update (push) Has been cancelled
2025-12-21 02:06:25 +00:00
Adam b5d7d3dec1 fix(desktop): layout 2025-12-20 20:02:40 -06:00
Adam 182630e0d7 feat(desktop): new layout 2025-12-20 19:52:12 -06:00
YuY801103 c81506b28d docs: add Traditional Chinese (Taiwan) README translation (#5861)
Co-authored-by: Yu <YuY801103@users.noreply.github.com>
2025-12-20 15:51:42 -06:00
Ryan Vogel 6c40bfe043 docs: clarify model ID format for OpenCode provider (#5854) 2025-12-20 13:51:13 -06:00
Aiden Cline 9caaae6a18 tweak: better error message if no primary agents are enabled 2025-12-20 13:47:28 -06:00
Ryan Vogel ad6a5e6157 feat(docs): adding .md to docs pages shows raw markdown (#5823) 2025-12-20 12:05:06 -06:00
shamil2 7dd8ea58c2 feat: add Catppuccin Frappé theme (#5821)
Co-authored-by: shamil2 <shamil2@users.noreply.github.com>
2025-12-20 12:04:35 -06:00
ja 3b261e0125 docs: add name property to model configuration example (#5853) 2025-12-20 11:54:49 -06:00
Shpetim 426791f68a fix: system theme flicker (#5842)
Co-authored-by: Shpetim <shpetim.alimi@ndbit.net>
2025-12-20 11:53:46 -06:00
Frank c7cade2494 zen: sync 2025-12-20 12:52:55 -05:00
Matt Silverlock 8f6c8844d7 feat: support configuring a default_agent across all API/user surfaces (#5843)
publish-github-action / publish (push) Has been cancelled
Update Nix Hashes / update (push) Has been cancelled
Co-authored-by: observerw <observerw@users.noreply.github.com>
2025-12-20 11:49:23 -06:00
Aiden Cline da6e0e60c0 ci: adjust review agent prompt to discourage bad diffs 2025-12-20 11:43:59 -06:00
lif d89b567b47 fix: add transform case for gemini if mcp tool has missing array items (#5846) 2025-12-20 11:41:52 -06:00
ja 34eb03f5b8 fix: prioritize session list loading when resuming with -c (#5816)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-20 11:39:26 -06:00
Ryan Cassidy 2f6d15a51e feat: add cursor theme (#5850) 2025-12-20 10:56:27 -06:00
GitHub Action 8ffea80980 chore: generate 2025-12-20 16:00:36 +00:00
opencode c87d61b561 release: v1.0.180
Update Nix Hashes / update (push) Has been cancelled
2025-12-20 16:00:36 +00:00
Dax Raad 35c12e2053 ci 2025-12-20 10:54:02 -05:00
opencode 33d8bfc937 release: v1.0.179
Update Nix Hashes / update (push) Has been cancelled
2025-12-20 15:38:38 +00:00
Dax Raad f2343a6794 ci 2025-12-20 10:35:01 -05:00
GitHub Action bab000eeb5 chore: generate 2025-12-20 15:29:23 +00:00
Dax Raad 8e674ae053 ci 2025-12-20 10:28:46 -05:00
opencode 6a4f4009d5 release: v1.0.178
Update Nix Hashes / update (push) Has been cancelled
2025-12-20 15:08:27 +00:00
Dax Raad 5e79b95927 ci 2025-12-20 10:05:03 -05:00
Tommy D. Rossi a7a2bbb497 feat: add endpoints to delete and update message parts (#5433) 2025-12-20 15:00:41 +00:00
GitHub Action 6e93d14bdb chore: generate 2025-12-20 15:00:41 +00:00
opencode f29f284b3e release: v1.0.177
Update Nix Hashes / update (push) Has been cancelled
2025-12-20 15:00:40 +00:00
Dax Raad b1b8f6cf71 ci 2025-12-20 09:57:19 -05:00
opencode 4c3336bbe7 release: v1.0.176
Update Nix Hashes / update (push) Has been cancelled
2025-12-20 12:28:27 +00:00
Adam 354ac0b493 fix(desktop): sidebar UX issues 2025-12-20 06:25:39 -06:00
Adam 1d159c6858 fix(desktop): task rendering perf 2025-12-20 06:15:59 -06:00
GitHub Action d70639b256 chore: generate 2025-12-20 12:13:52 +00:00
Adam e4a92f0084 fix(desktop): show last text part when summarized 2025-12-20 06:12:58 -06:00
Adam fdf5a70a27 fix(desktop): performance with lots of session changes 2025-12-20 06:12:58 -06:00
Adam f71da42520 fix(desktop): event reconnect gaps 2025-12-20 06:12:58 -06:00
GitHub Action f6bdeb9e3a ignore: update download stats 2025-12-20 2025-12-20 12:04:14 +00:00
opencode 2400354bab release: v1.0.175
Update Nix Hashes / update (push) Has been cancelled
2025-12-20 11:10:23 +00:00
Adam db348c46cc fix(desktop): perf tweaks 2025-12-20 05:06:57 -06:00
Adam 49567fe61a fix(desktop): add retries to init promises 2025-12-20 04:57:39 -06:00
Adam e5b3f796e4 fix: types 2025-12-20 04:33:42 -06:00
Adam a9700c8773 fix: shouldEncode 2025-12-20 04:25:17 -06:00
Adam 26cf5e003e fix(desktop): perf stuff 2025-12-20 04:25:17 -06:00
Adam 742cf10dee fix(desktop): removed projects 2025-12-20 04:25:16 -06:00
Frank 7664453f94 zen: add minimax m2.1 2025-12-20 01:55:10 -05:00
GitHub Action 460672aa93 chore: generate 2025-12-20 06:27:02 +00:00
Frank b4e4fd9807 zen: add minimax m2.1 2025-12-20 01:26:27 -05:00
opencode 34bdfd0937 release: v1.0.174
Update Nix Hashes / update (push) Has been cancelled
2025-12-20 04:06:28 +00:00
GitHub Action 84591ca8ad chore: generate 2025-12-20 03:58:36 +00:00
Aiden Cline fd4d0c5c0b fix: file permissions 2025-12-19 21:57:55 -06:00
opencode 9f5db46911 release: v1.0.173
Update Nix Hashes / update (push) Has been cancelled
2025-12-20 03:48:15 +00:00
Ariane Emory 755ddbb223 feat(tui): reinsert forked message text in prompt text input box when forking session (resolves #5495) (#5545)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-19 21:45:33 -06:00
GitHub Action 701d470d01 chore: generate 2025-12-20 03:42:00 +00:00
Aiden Cline 1d9058d26b ci: fix archive 2025-12-19 21:41:18 -06:00
opencode 39e2a5f595 release: v1.0.172
Update Nix Hashes / update (push) Has been cancelled
2025-12-20 03:29:07 +00:00
Aiden Cline f862ab6722 ci: fix undefined 2025-12-19 21:22:49 -06:00
Aiden Cline 129d4f0b1b ci: fix release notes
Update Nix Hashes / update (push) Has been cancelled
2025-12-19 17:49:07 -06:00
Aiden Cline 3a1e50d1f8 Revert "tweak: better release notes (grouped changelog) (#5768)"
This reverts commit b99afdad91.
2025-12-19 17:46:38 -06:00
Aiden Cline e2fb690d8e Revert "tweak: DevEx to run changelog independently (#5774)"
This reverts commit 7f8e799392.
2025-12-19 17:46:36 -06:00
Aiden Cline 0a7f58a811 Revert "ci: gemini 3 flash doesnt exist in pinned cicd version (#5776)"
This reverts commit 382905602c.
2025-12-19 17:46:35 -06:00
Aiden Cline dae0168ed8 test: fixture cleanup 2025-12-19 17:00:23 -06:00
Aiden Cline edfe2e4f1c test: fix test case 2025-12-19 16:37:36 -06:00
Kaspar 1bc1ea8b47 tweak: Make LSP message more accurate when LSPs disabled (#5814) 2025-12-19 16:34:04 -06:00
Aiden Cline dacbbe3184 chore: rm dead code 2025-12-19 16:20:17 -06:00
Aiden Cline 89285d8f5f ci: fix publish auth failure 2025-12-19 16:00:41 -06:00
1XD 2e853911c3 docs: replace deprecated mise ubi backend with github backend (#5811) 2025-12-19 15:51:09 -06:00
Github Action 695fdecf23 Update Nix flake.lock and hashes 2025-12-19 20:40:07 +00:00
Frank 054d22791d zen: sync 2025-12-19 15:38:31 -05:00
Frank 4a57cc69d8 zen: sync 2025-12-19 15:25:35 -05:00
Frank 7e0c8db029 zen: sync 2025-12-19 15:21:57 -05:00
Cameron ba4cc3bf86 Desktop file encoding issue (#5490) 2025-12-19 14:18:37 -06:00
Adam b19a424c85 chore: cleanup 2025-12-19 13:11:08 -06:00
Adam 1689281c35 fix(desktop): auto-scroll and session perf 2025-12-19 13:07:53 -06:00
Adam cdbb59fae8 fix(desktop): don't use tauri http for sse events 2025-12-19 13:07:53 -06:00
Adam 4eb311e98f fix(desktop): error height 2025-12-19 13:07:52 -06:00
Github Action 80eac96258 Update Nix flake.lock and hashes 2025-12-19 18:29:37 +00:00
Aiden Cline 4bad6f9f1b tweak: use fetch instead of octokit for now 2025-12-19 12:28:11 -06:00
Steven T. Cramer d7db57e8e1 docs: add Windows Terminal Shift+Enter configuration guide (#5788) 2025-12-19 12:27:21 -06:00
Brendan Allan 943fbf39a3 ci: separate standalone publishing from dependent publishing (#5634)
Co-authored-by: GitHub Action <action@github.com>
2025-12-19 13:22:17 -05:00
Dax Raad d8a34c2fcc core: prevent file system scanning when in root directory to avoid unnecessary operations 2025-12-19 13:15:11 -05:00
Aiden Cline 5720ed1f44 ci: change token for gh release to allow discord job to actually trigger see: https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow 2025-12-19 10:45:26 -06:00
GitHub Action bb20a359e4 chore: generate 2025-12-19 14:56:47 +00:00
José Valim 0d472a49a0 Do not include hidden agents in ACP (#5791) 2025-12-19 08:56:12 -06:00
Github Action 203581e82f Update Nix flake.lock and hashes 2025-12-19 13:42:49 +00:00
Sebastian Herrlinger 677631916c upgrade opentui to v0.1.62, enabling textarea mouse scroll and cursor set 2025-12-19 13:42:49 +00:00
Github Action 1aa1e8c904 Update Nix flake.lock and hashes 2025-12-19 13:42:49 +00:00
opencode 55d62fbd9f release: v1.0.170
Update Nix Hashes / update (push) Has been cancelled
2025-12-19 13:42:48 +00:00
Adam e1ad2a355c fix(desktop): error handling 2025-12-19 07:38:38 -06:00
Adam 4f318f913e chore: logging 2025-12-19 07:38:38 -06:00
Adam 2d814b6db2 fix(desktop): separate prompt history for shell 2025-12-19 07:38:38 -06:00
Adam e561f1ad68 fix(desktop): don't navigate prompt history if dirty 2025-12-19 07:38:37 -06:00
Sebastian Herrlinger ebfb985215 user messages as markdown with toggle 2025-12-19 13:51:26 +01:00
GitHub Action 2646da50df ignore: update download stats 2025-12-19 2025-12-19 12:04:33 +00:00
Github Action 50a5f6e53b Update Nix flake.lock and hashes 2025-12-19 11:49:40 +00:00
Brendan Allan d03fac52e7 Update SolidStart and bring back HttpHeader usage (#5355)
Co-authored-by: Github Action <action@github.com>
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
2025-12-19 05:46:33 -06:00
Sherlock Holmes 6a802c01cd feat(tui): implement smooth scrolling for autocomplete dropdown navigation (#5559)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-19 00:40:16 -06:00
Eric Shirley 14146428dd lsp: add oxlint server (#5570) 2025-12-19 00:17:20 -06:00
Aiden Cline 26d0280f70 docs: contributing 2025-12-18 22:18:34 -06:00
Aiden Cline 3274a5813e ci: only run generate for dev 2025-12-18 22:17:36 -06:00
Luke Parker 382905602c ci: gemini 3 flash doesnt exist in pinned cicd version (#5776) 2025-12-18 21:59:46 -06:00
GitHub Action 8b5cea7899 chore: generate 2025-12-19 03:59:14 +00:00
Matt Silverlock 100c31cbb1 fix: use correct octokit API for PR review comment reactions (#5778) 2025-12-18 21:58:41 -06:00
GitHub Action 0b286f1b84 chore: generate 2025-12-19 02:12:35 +00:00
Brendan Allan 2f6ca958fe tauri: remove pinch-to-zoom on window 2025-12-19 02:12:35 +00:00
Basit Mustafa 5218e7a546 docs(ecosystem): add opencode-zellij-namer plugin (#5771) 2025-12-19 02:12:35 +00:00
Luke Parker 7f8e799392 tweak: DevEx to run changelog independently (#5774) 2025-12-19 02:12:35 +00:00
opencode 289f4abaaa release: v1.0.169
Update Nix Hashes / update (push) Has been cancelled
2025-12-19 02:12:34 +00:00
Adam 7ce898ce43 fix(desktop): shell mode 2025-12-18 20:06:53 -06:00
Adam 0dd716a75e fix(desktop): extra reqs 2025-12-18 19:53:38 -06:00
Aiden Cline 87171467fa ci: better err msg for generate workflow 2025-12-18 19:03:16 -06:00
Luke Parker b99afdad91 tweak: better release notes (grouped changelog) (#5768) 2025-12-18 18:49:37 -06:00
Aiden Cline 4fd576f3af fix: better api call error msgs in some cases 2025-12-18 18:46:25 -06:00
GitHub Action 2f41d0bedd chore: generate 2025-12-19 00:18:07 +00:00
Rohan Godha 5f03290534 feat(tui): click on subagents to open them (#5761)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-18 18:17:34 -06:00
opencode 427157c683 release: v1.0.168
Update Nix Hashes / update (push) Has been cancelled
2025-12-18 21:55:29 +00:00
Github Action a0ab3d98b7 Update Nix flake.lock and hashes 2025-12-18 21:48:53 +00:00
GitHub Action c8de766913 chore: generate 2025-12-18 21:47:59 +00:00
Adam d57b963141 fix: id 2025-12-18 15:47:21 -06:00
Adam 0ebcaff927 fix(desktop): expanded states 2025-12-18 15:47:20 -06:00
Adam 15931fa170 chore: cleanup 2025-12-18 15:47:20 -06:00
Adam af4087d7b5 fix(desktop): smaller max-width when review open 2025-12-18 15:47:20 -06:00
Aiden Cline 323ea1040c ci: fix generate 2025-12-18 15:23:23 -06:00
Aiden Cline 1fe87b0233 ci: fix file perm 2025-12-18 14:39:44 -06:00
Aiden Cline 8d11df1b3b ci: handle case where generate.yml fails better 2025-12-18 14:33:40 -06:00
Aiden Cline ecc5050838 tweak: more retry cases 2025-12-18 13:59:37 -06:00
Aiden Cline 606cf3b6f2 chore: rm dead code 2025-12-18 13:59:37 -06:00
GitHub Action 67cfd7f06b chore: format code 2025-12-18 19:38:25 +00:00
OpeOginni ab9ac7c87a feat: add experimental support for Ty language server (#5575) 2025-12-18 13:37:48 -06:00
Adam ee9f979613 fix(desktop): markdown styles 2025-12-18 13:03:14 -06:00
Adam 228b6444f8 fix(desktop): don't show image button in shell mode 2025-12-18 13:03:14 -06:00
Frank 9998efdae2 zen: cleanup headers 2025-12-18 13:47:31 -05:00
Aiden Cline 9427f56e1a rm interleaved thinking filter for certain kimi k2 thinking model providers that were bugged 2025-12-18 12:26:27 -06:00
Adam a6dd35d73d fix(desktop): submit prompt 2025-12-18 12:03:21 -06:00
GitHub Action faeaafa5f5 chore: format code 2025-12-18 17:31:49 +00:00
Matt Silverlock 8b298a233e github: add OIDC_BASE_URL for custom GitHub App installs (#5756)
publish-github-action / publish (push) Has been cancelled
Update Nix Hashes / update (push) Has been cancelled
2025-12-18 11:31:13 -06:00
Adam 6f43d03043 fix(desktop): checkbox render in safari fml 2025-12-18 11:16:33 -06:00
Adam c868a4088d fix(desktop): rendering shell mode messages 2025-12-18 11:16:33 -06:00
Adam 83d8a88c90 fix(desktop): error styles 2025-12-18 11:16:33 -06:00
Adam 268f37f8c9 fix(desktop): prompt history nav, optimistic prompt dup 2025-12-18 11:16:33 -06:00
Adam b0aaf04957 fix(desktop): session ordered by most recent 2025-12-18 11:16:32 -06:00
Adam b7875256f3 feat(desktop): shell mode 2025-12-18 11:16:32 -06:00
Adam 7bc47fb904 chore: cleanup 2025-12-18 11:16:32 -06:00
GitHub Action 5cf8e54372 chore: format code 2025-12-18 16:39:21 +00:00
Ariane Emory 7437ccd6f4 feat(tui): fork slash command for keyboard-friendly session forking (resolves #5599) (#5610)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-18 10:38:19 -06:00
Jeon Suyeol 4bf882ba81 fix(command): validate model before executing slash command (#5740) 2025-12-18 10:35:40 -06:00
Frank d5dcc55a47 Revert "add client header"
This reverts commit 2fb89161c8.
2025-12-18 11:21:22 -05:00
barış e1925f4fe8 docs: fix typos (#5753)
publish-github-action / publish (push) Has been cancelled
Update Nix Hashes / update (push) Has been cancelled
2025-12-18 09:56:37 -06:00
Aiden Cline ee3d034e16 ci: fix discord 2025-12-18 09:56:08 -06:00
Aiden Cline 257a4d5b86 bump bun version 2025-12-18 09:47:42 -06:00
Daniel Polito 1fc5836f64 Improve Github Action Hallucinations (#5751) 2025-12-18 09:40:04 -06:00
Frank 2fb89161c8 add client header 2025-12-18 10:33:25 -05:00
GitHub Action 251fbc0a99 ignore: update download stats 2025-12-18 2025-12-18 12:16:54 +00:00
Brendan Allan 0da901a188 tauri: disable pinch zoom on linux (#5735) 2025-12-18 05:13:09 -06:00
Brendan Allan 17221e6ffe tauri: configure display backends more correctly on linux (#5730) 2025-12-18 18:34:12 +08:00
GitHub Action cc9f88ac8f chore: format code 2025-12-18 10:28:55 +00:00
Adam fe65ed6a61 fix(desktop): disable pinch to zoom 2025-12-18 04:28:03 -06:00
Adam e37a75a411 feat(desktop): custom update toast 2025-12-18 04:26:21 -06:00
opencode 194ff4919c release: v1.0.167
Update Nix Hashes / update (push) Has been cancelled
2025-12-18 09:45:33 +00:00
shuv 83843a794f fix: handle empty directory query parameter in server middleware (#5732) 2025-12-18 03:27:50 -06:00
Brendan Allan 235a60d3c2 tauri: say OpenCode Server instead of OpenCode CLI 2025-12-18 17:18:46 +08:00
Brendan Allan b70d186bd1 tauri: server spawn fail dialog w/ copy logs button (#5729) 2025-12-18 03:17:31 -06:00
Frank 647331de28 zen: error handling for stream requests 2025-12-18 00:47:37 -05:00
GitHub Action 57ef115375 chore: format code 2025-12-18 04:30:54 +00:00
Jeon Suyeol 942498211f docs: add OPENCODE_DISABLE_TERMINAL_TITLE to environment variables (#5725) 2025-12-17 22:30:21 -06:00
Jake Nelson e789fcf5e5 feat(tui): add option to disable terminal title (#5713) 2025-12-17 22:30:01 -06:00
Frank b9fb180bc6 zen: error handling for stream requests 2025-12-17 22:55:44 -05:00
Rohan Mukherjee 7427b887f9 MCP improvements (#5699) 2025-12-17 20:49:45 -06:00
Jay V 289b2b6a51 docs: add legal pages with privacy policy and terms of service links 2025-12-17 20:17:33 -05:00
GitHub Action 49b4b5907e chore: format code 2025-12-18 01:09:02 +00:00
Ryan Vogel f82442c123 docs: add opencode.cafe to ecosystem page (#5714) 2025-12-17 19:08:29 -06:00
opencode e682cc9daf release: v1.0.166
Update Nix Hashes / update (push) Has been cancelled
2025-12-17 22:08:15 +00:00
Adam d359e086a4 chore: cleanup 2025-12-17 16:04:41 -06:00
Adam f949755367 fix: better init error messages 2025-12-17 16:04:40 -06:00
Adam a168d854f4 fix: auto-scroll 2025-12-17 16:04:40 -06:00
GitHub Action 31645f5578 chore: format code 2025-12-17 22:03:43 +00:00
Sercan Sagman a1b68daa9a fix(tui): exclude reverted assistant reply when copying last message (#5705)
Signed-off-by: assagman <ahmetsercansagman@gmail.com>
2025-12-17 16:03:06 -06:00
opencode ca65da2d9e release: v1.0.165
Update Nix Hashes / update (push) Has been cancelled
2025-12-17 21:46:21 +00:00
Adam e48d804d84 feat(desktop): startup errors shown 2025-12-17 15:42:55 -06:00
Adam b4209582fb feat(desktop): optimistic prompt submit 2025-12-17 15:42:55 -06:00
Aiden Cline dbdea2f659 fix: better error messages 2025-12-17 15:32:44 -06:00
Aiden Cline a50ab4b5b5 fix: prevent 1 from showing when preparing write 2025-12-17 15:25:04 -06:00
Nalin Singh 4d7c3f56fa feat: add viewportOptions to scrollbox for padding adjustments to avoid scrollbar overlap (#5703) 2025-12-17 15:09:41 -06:00
Spoon 16b41d2bea UI: show plugins in /status (#4515)
Co-authored-by: GitHub Action <action@github.com>
2025-12-17 15:01:52 -06:00
Nalin Singh a8c499ae8f fix: prevent session list selection from jumping to active session when confirming delete (#5666) 2025-12-17 14:35:46 -06:00
Joel Hooks 24430287c5 feat(plugin): add experimental.session.compacting hook for pre-compaction context injection (#5698)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-17 13:57:09 -06:00
Github Action 1f52731255 Update Nix flake.lock and hashes 2025-12-17 19:13:06 +00:00
Adam 4a3ba58f65 chore: localStorage -> tauri store 2025-12-17 13:11:02 -06:00
Brendan Allan 2a3a8a1ec2 console: use download proxy to rename mac and windows installers (#5697)
Co-authored-by: GitHub Action <action@github.com>
2025-12-18 01:59:23 +08:00
Ravi Kumar 69e562125d fix(tui): resolve session_status TypeError (#5520) 2025-12-17 11:38:05 -06:00
Aiden Cline b5e97eb338 fix: keep session dialog open if deleting session 2025-12-17 11:29:55 -06:00
Aiden Cline 16e6941495 fix: remove needless tui event publish on session delete 2025-12-17 11:29:55 -06:00
GitHub Action f033e0317e chore: format code 2025-12-17 17:02:36 +00:00
Adam ddd88f92cc fix: sticky visual issues 2025-12-17 11:02:00 -06:00
Aiden Cline 99101edc13 ci: add windows label to triage bot 2025-12-17 11:01:53 -06:00
GitHub Action 6e85a07977 chore: format code 2025-12-17 16:59:57 +00:00
Brendan Allan be1a3536ae console: add /download/[platform] endpoint 2025-12-18 00:59:16 +08:00
Qio 1e4bfbcf6f add OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX to override 32k default (#5679)
Co-authored-by: qio <handsomehust@gmail.com>
2025-12-17 10:35:43 -06:00
Adam 204e3bf382 feat(desktop): inter and ibm plex mono 2025-12-17 10:30:35 -06:00
Adam 8fb014a48d feat(desktop): inter and ibm plex mono 2025-12-17 10:30:03 -06:00
Paolo Ricciuti 57c3cf1f8b fix: send mcpName as state if authUrl doesn't have state (#5681) 2025-12-17 10:26:23 -06:00
Aiden Cline f9d0850c5e test: add regression test for setCacheKey option 2025-12-17 10:24:53 -06:00
Spoon 8864da7a77 batch: enable edit, todoread, clarify error message, minor tool description change (#5659) 2025-12-17 10:23:35 -06:00
Rhys Sullivan 1b39199083 fix: change subagent navigation order to newest-to-oldest (#5680) 2025-12-17 10:22:57 -06:00
Shantur Rathore b8204c0bb7 fix: config option setCacheKey not being respected (#5686) 2025-12-17 10:20:10 -06:00
Aiden Cline fe8c5c143e docs: update share link 2025-12-17 10:18:30 -06:00
Frank d6f86e9bb7 zen: add gemini 3 flash 2025-12-17 11:10:58 -05:00
Brendan Allan bf00b2bfc9 tauri: nsis header and sidebar 2025-12-18 00:02:16 +08:00
Brendan Allan 382ec8fb2c tauri: update nsis icon 2025-12-17 23:40:52 +08:00
Stoufiler 6454adcd69 docs: Sort LSP Server list (#5688) 2025-12-17 09:14:26 -06:00
Rohan Mukherjee 99548554d7 feat: added lucent-orng theme (#5678)
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-17 09:10:35 -06:00
Sachnun 751899eeec fix: remove unsupported parameter from bash tool description (#5676) 2025-12-17 08:59:42 -06:00
Brendan Allan f8df1d3185 tauri: return after update failures 2025-12-17 22:54:54 +08:00
GitHub Action b07a47fc89 chore: format code 2025-12-17 14:52:05 +00:00
Brendan Allan c6f84f32d7 tauri: only alert on update failure when triggered manually 2025-12-17 22:51:14 +08:00
Brendan Allan ebe25c3e9a tauri: dev icons + separate prod config (#5691)
Co-authored-by: GitHub Action <action@github.com>
2025-12-17 22:23:03 +08:00
Adam 65d7fc3ccd fix: command shortcuts 2025-12-17 07:36:53 -06:00
Github Action 4f3037d803 Update Nix flake.lock and hashes 2025-12-17 13:35:05 +00:00
Amadeus Demarzi 5c490c51ed Diffs Performance Improvements (#5653)
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
2025-12-17 07:33:46 -06:00
GitHub Action 5da1c0087b ignore: update download stats 2025-12-17 2025-12-17 12:04:41 +00:00
David Hill 4375149e63 wip: auto-detect OS and show desktop download button 2025-12-17 11:43:04 +00:00
David Hill b695d3b6bb fix: website cta button 2025-12-17 11:21:57 +00:00
Adam d7e133732c chore: cleanup 2025-12-17 03:58:16 -06:00
Adam 494e6fff01 feat(desktop): share sessions 2025-12-17 03:47:49 -06:00
Adam 0c7a297b1d feat(desktop): lsp diagnostics displayed 2025-12-17 03:47:48 -06:00
Github Action 9b1f9007c3 Update Nix flake.lock and hashes 2025-12-17 08:41:07 +00:00
GitHub Action 34ef5f4ece chore: format code 2025-12-17 08:40:22 +00:00
Sebastian Herrlinger 73ad20b90c upgrade opentui to v0.1.61 2025-12-17 09:38:04 +01:00
Jeon Suyeol 340e80257a Add availability to disable terminal title using OPENCODE_DISABLE_TERMINAL_TITLE env (#5661) 2025-12-16 23:57:03 -06:00
Dax Raad c23ea2a211 ci: update publish workflow configuration 2025-12-16 21:01:44 -05:00
GitHub Action a5f964aec6 chore: format code 2025-12-17 01:28:42 +00:00
Spoon b8a8fb0de6 plugin(hook): add task tool execution hooks and command context tracking (#5642) 2025-12-16 19:28:09 -06:00
Aiden Cline a6a8f41fd3 ci: tweak triage 2025-12-16 19:27:44 -06:00
Matt Silverlock c137babea3 github: add configurable mentions input (#5655) 2025-12-16 19:14:50 -06:00
Adam db2abc1b2c tui: increase session width to accommodate longer code blocks and improve readability 2025-12-16 17:31:26 -06:00
David Hill a0f9f8dabb fix: load more button 2025-12-16 23:23:18 +00:00
Aiden Cline 8a185aa678 ci: fix missing pkg issue 2025-12-16 16:31:46 -06:00
Dax Raad 29aaf4f000 ci: fix release draft configuration to prevent automatic draft flag 2025-12-16 17:17:06 -05:00
GitHub Action fc940dfcfb chore: format code 2025-12-16 22:07:10 +00:00
Adam 2f2ea98937 fix(share): content wasn't centered 2025-12-16 16:06:28 -06:00
opencode ef0fa2007b release: v1.0.164
Update Nix Hashes / update (push) Has been cancelled
2025-12-16 21:47:41 +00:00
Adam f07d4b933c fix(desktop): prompt history nav 2025-12-16 15:42:35 -06:00
Aiden Cline 5f57cee8e4 fix: user invoked subtasks causing tool_use or missing thinking signa… (#5650) 2025-12-16 15:42:21 -06:00
Adam 1755a3fe07 fix(desktop): auto-scroll 2025-12-16 15:32:14 -06:00
Adam 99680baf83 fix(desktop): focus prompt input after dialog close 2025-12-16 15:25:00 -06:00
Adam 9aa5460a0e fix(desktop): prompt history navigation 2025-12-16 15:10:44 -06:00
Adam b4014e5baa fix: auto-scroll 2025-12-16 15:10:43 -06:00
Adam 96e4dcb521 fix: working logic 2025-12-16 15:10:43 -06:00
Adam 7e682a95c4 fix: prompt input multi line input 2025-12-16 15:10:43 -06:00
Adam 5eeba76bc5 fix: defensive audio init 2025-12-16 15:10:43 -06:00
Eric Guo a2c91ebc32 feat(desktop): Loading more session number per project by button (#5616)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-16 14:50:33 -06:00
matvey 1aee8b49e1 feat: add experimental oxfmt formatter (#5620) 2025-12-16 14:43:14 -06:00
Aiden Cline 984f17ddd7 ci: include desktop & tauri in release notes 2025-12-16 14:37:33 -06:00
Aiden Cline d556143e3b ci: fix branch name 2025-12-16 14:35:42 -06:00
Aiden Cline 7e3ad770ac fix: git branch filewatcher, add flag to completely disable watcher
publish-github-action / publish (push) Has been cancelled
Update Nix Hashes / update (push) Has been cancelled
2025-12-16 14:31:09 -06:00
Aiden Cline 87524de265 ci: fix triage 2025-12-16 14:15:50 -06:00
Aiden Cline ee10d9b898 ci: auto tag github action once a change is shipped for it 2025-12-16 14:13:51 -06:00
Dax Raad bbd36e8441 core: update plugin dependency and config loading for .opencode directory support 2025-12-16 15:06:50 -05:00
Dax Raad 4e2d1acf7d core: fix Tauri desktop app SSE connection timeout
- Add heartbeat events to /global/event and /event SSE endpoints
- Send server.heartbeat event every 30s to prevent WKWebView 60s timeout
- Fixes desktop app disconnecting from global events after 1 minute
2025-12-16 14:45:03 -05:00
Dax Raad 40d63cd1e3 fix 2025-12-16 14:25:12 -05:00
Dax Raad 77b2331428 ignore: update opencode plugin dependency 2025-12-16 14:21:19 -05:00
Dax Raad 2b7e2edee5 core: ensure desktop app loads user shell environment variables
Changes shell spawn flags from -l to -il so that ~/.zshrc and
~/.bashrc are sourced when starting the desktop app on macOS
and Linux. This fixes missing PATH and other environment
variables that users expect to be available.
2025-12-16 14:21:19 -05:00
Adam 28aba35ff9 feat(desktop): show retries 2025-12-16 13:19:32 -06:00
Adam 89219a77f7 fix: layout badness 2025-12-16 12:53:12 -06:00
Adam 20e3a74bad fix: defensive audio init 2025-12-16 12:53:12 -06:00
Adam ff690350b1 feat(desktop): show write tool output 2025-12-16 12:53:11 -06:00
Adam ebefb26e8f chore: cleanup 2025-12-16 12:53:11 -06:00
Dax Raad 0b1ee9ddd9 Merge remote-tracking branch 'origin/dev' into dev 2025-12-16 13:52:50 -05:00
Dax Raad 79599f351e chore: update opencode plugin dependencies and fix tauri sidecar path 2025-12-16 13:52:40 -05:00
Github Action 8c9f6b1d3e Update Nix flake.lock and hashes 2025-12-16 18:38:59 +00:00
Dax Raad 83bcb9e95b tui: fix autocomplete file loading and update dependencies 2025-12-16 13:37:22 -05:00
David Hill 96b9ff8d0e fix: remove the selected state from button when select deselected 2025-12-16 17:41:56 +00:00
David Hill 0af2254856 wip: add active state to open select 2025-12-16 17:35:04 +00:00
Fran Zekan c2944024a8 fix: enable shell alias expansion in ! command (#5621) 2025-12-16 11:32:31 -06:00
GitHub Action 5be4bda90f chore: format code 2025-12-16 17:30:08 +00:00
shekohex b78e2db013 docs: fix typo in Google Antigravity github link (#5625) 2025-12-16 11:29:31 -06:00
jinzhongjia 3f4d1121a4 docs: Add new project entry for opencode.nvim frontend (#5626) 2025-12-16 11:29:06 -06:00
Connor Adams def910021d docs: add homebrew command for opencode-desktop (#5631)
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-16 11:28:40 -06:00
Aiden Cline 3ac42e9632 fix: github install cmd if repo has . in it 2025-12-16 11:19:18 -06:00
David Hill 9c26bb7c6c fix: breadcrumb dropdown position left aligned 2025-12-16 17:16:51 +00:00
David Hill 53f20f7612 Revert "wip: make the default container wider"
This reverts commit 1f18f389c0.
2025-12-16 17:16:51 +00:00
Tommy D. Rossi 11b3927dc2 fix: use system prompt field from prompt input (#5633)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-16 10:52:22 -06:00
David Hill a190eda2c8 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-16 16:36:15 +00:00
David Hill 1f18f389c0 wip: make the default container wider 2025-12-16 16:35:57 +00:00
GitHub Action 84e56ee614 chore: format code 2025-12-16 16:10:36 +00:00
Aiden Cline 59329a414d ci: tweak triage 2025-12-16 10:09:51 -06:00
Brendan Allan 452c991f58 Keep release a draft until all builds are finished (#5632)
Co-authored-by: opencode <opencode@sst.dev>
Co-authored-by: GitHub Action <action@github.com>
2025-12-16 23:34:44 +08:00
Simon D'Morias be8116e2ea fix: preserve argument boundaries in run command (#4979) 2025-12-16 06:21:51 -06:00
David Hill f0ed1e38c9 Revert "fix: strip parentheses from file paths generated by llm"
This reverts commit 6c1a1a77b7.
2025-12-16 12:12:01 +00:00
GitHub Action ac0f1dbbdd ignore: update download stats 2025-12-16 2025-12-16 12:04:54 +00:00
GitHub Action 275a352e81 chore: format code 2025-12-16 11:51:08 +00:00
David Hill 9f3bc0e352 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-16 11:50:25 +00:00
David Hill 6c1a1a77b7 fix: strip parentheses from file paths generated by llm 2025-12-16 11:50:23 +00:00
David Hill 2e21c62320 fix: font size updates 2025-12-16 11:48:52 +00:00
David Hill 19c6fec4d1 wip: font-size updates 2025-12-16 11:12:08 +00:00
Brendan Allan 4779d99a13 tauri: explicitly kill sidecar before updater relaunch 2025-12-16 19:01:37 +08:00
David Hill 05e0759878 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-16 10:54:51 +00:00
David Hill 2330ec6dc3 fix: font size updates 2025-12-16 10:54:50 +00:00
GitHub Action 75e5130cf8 chore: format code 2025-12-16 10:33:49 +00:00
Brendan Allan 87efd27459 tauri: macos-only app menu 2025-12-16 18:33:04 +08:00
Aiden Cline 62f080b0e4 fix: small bug w/ install script 2025-12-16 00:20:05 -06:00
Aiden Cline ae3990a557 chore: centralize dep to catalog & fix typos 2025-12-15 23:07:55 -06:00
Aiden Cline d7b5b431d6 ci: Update issue assignment and labeling guidelines
Clarified assignment responsibilities and labeling for issues related to OpenCode tools.
2025-12-15 22:11:30 -06:00
Dax Raad e2fbd098d2 tui: fix dialog select items taking up 2 lines when truncated
Prevents text wrapping in dialog select options by removing wrapMode,
ensuring truncated text stays on single line and maintains proper timestamp visibility
2025-12-15 22:57:52 -05:00
Luke Parker ef78fd8bae fix: debounce LSP diagnostics to get complete results (#5600) 2025-12-15 21:26:59 -06:00
DS 72ebaeb8f7 fix: rejoin system prompt if experimental plugin hook triggers to preserve caching (#5550)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-15 20:00:26 -06:00
Lucas Duailibe 0dc62d5dad make install script use tmp dir (#5601) 2025-12-15 19:58:07 -06:00
Aiden Cline d118782a10 ci: cheaper model 2025-12-15 19:24:30 -06:00
Aiden Cline ff05647350 ci: ignore 2025-12-15 19:18:39 -06:00
GitHub Action 0e1c711c4e chore: format code 2025-12-16 01:16:20 +00:00
Aiden Cline bfb254dac6 ci: auto triage issues 2025-12-15 19:15:40 -06:00
opencode 92fe927785 release: v1.0.162
Update Nix Hashes / update (push) Has been cancelled
2025-12-16 00:40:27 +00:00
GitHub Action 2e25fe9d5d chore: format code 2025-12-16 00:36:01 +00:00
Dax Raad 38c5f23f4a tui: update dialog context and server to use new single dialog system 2025-12-15 19:35:19 -05:00
Dax Raad 112c58abf5 tui: refactor dialog system to use single active dialog instead of stack 2025-12-15 19:35:18 -05:00
opencode 0dce5173cc release: v1.0.161
Update Nix Hashes / update (push) Has been cancelled
2025-12-16 00:17:15 +00:00
Adam 2c70c0b00f fix: undefined events 2025-12-15 18:13:11 -06:00
opencode-agent[bot] 34024c2504 docs: models --refresh flag in cli.mdx (#5596)
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-15 18:04:47 -06:00
Luke Parker 27e826eba6 fix(win32): Normalise LSP paths on windows (fixes lua) (#5597) 2025-12-15 18:01:03 -06:00
Aiden Cline 89a4f1c1ae tweak: add .catch for extractZip calls 2025-12-15 17:41:28 -06:00
GitHub Action c0c61b25ff chore: format code 2025-12-15 23:30:06 +00:00
Luke Parker 0d1c6e0ca9 fix(win32): Missing LSP can now unzip on windows (#5594) 2025-12-15 17:29:30 -06:00
opencode 002db3abf4 release: v1.0.160
Update Nix Hashes / update (push) Has been cancelled
2025-12-15 23:26:00 +00:00
Dax Raad 416a919c6d tui: fix dialog replacement to prevent nested dialogs from showing simultaneously 2025-12-15 18:21:54 -05:00
Dax Raad dbbcf0b8d0 tui: fix model selection dialog to properly replace current dialog instead of creating nested dialogs 2025-12-15 18:14:33 -05:00
Luke Parker efac8cebb3 fix(win32): correct ElixirLS extension typo (#5590) 2025-12-15 16:47:27 -06:00
GitHub Action 4f2baf1a72 chore: format code 2025-12-15 22:40:18 +00:00
Luke Parker 48b2bde6e5 fix(win32): use path.delimiter for PATH separator in LSP server lookups (#5589) 2025-12-15 16:39:42 -06:00
opencode 88314148e6 release: v1.0.159
Update Nix Hashes / update (push) Has been cancelled
2025-12-15 22:19:08 +00:00
Dax Raad 56452d886d fix dialog root complexity 2025-12-15 17:13:20 -05:00
Luke Parker f3e64cfb19 fix(windows): opencode github install (#5587) 2025-12-15 16:12:54 -06:00
Ariane Emory 8fcc80bc20 fix: restore ability to bind keys for model_cycle_favorite model_cycle_favorite_reverse (resolves #5198) (#5202) 2025-12-15 16:03:50 -06:00
opencode-agent[bot] 0beccc406e docs: enabled_providers docs section (#5586)
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-15 16:01:35 -06:00
Adam b82ea693db fix: multiline user input 2025-12-15 15:22:39 -06:00
Adam 4fd9a19fbb fix: keybinds for agent and model selection 2025-12-15 15:19:19 -06:00
Adam e16487b804 fix: landing page CLS hero jump down 2025-12-15 15:10:48 -06:00
Adam 5388192aac fix: session nav on homepage 2025-12-15 15:10:17 -06:00
Adam 8010448ba1 fix: default steps expanded unless done 2025-12-15 15:03:00 -06:00
Adam 66f3e69867 fix: landing page CLS hero jump down 2025-12-15 14:59:24 -06:00
Aiden Cline ca599ab8fc tweak: add model flag support for agent create command 2025-12-15 14:55:59 -06:00
Jay V c3b3b133b0 docs: make project names clickable links in ecosystem documentation 2025-12-15 15:44:46 -05:00
Adam 300ec0e0af fix: missing event type (global) 2025-12-15 14:43:48 -06:00
Ariane Emory 6632987827 fix: record shell mode in history (resolves #5454) (#5551) 2025-12-15 14:42:17 -06:00
GitHub Action e555e893c4 chore: format code 2025-12-15 20:35:39 +00:00
Aiden Cline 81134cf61e add ability to set topK 2025-12-15 14:34:56 -06:00
opencode 37e4c1e619 release: v1.0.158
Update Nix Hashes / update (push) Has been cancelled
2025-12-15 20:28:44 +00:00
Adam 02b5e7d72c fix: modal search 2025-12-15 14:25:19 -06:00
justfortheloveof 7abc2a947e tweak: prioritize fuzzysort results that start with user input (#5571) 2025-12-15 14:22:37 -06:00
Adam 337a7e9646 fix: allow for non-vcs projects in desktop 2025-12-15 14:16:08 -06:00
GitHub Action 62cc532ecc chore: format code 2025-12-15 20:09:08 +00:00
Dax Raad d5a506d4ae core: fix server response handling to prevent connection timeouts 2025-12-15 15:07:20 -05:00
opencode 9c5f94bd66 release: v1.0.157
Update Nix Hashes / update (push) Has been cancelled
2025-12-15 19:46:23 +00:00
Dax Raad 83390314d6 ci: fix tauri updater version mismatch by checking out the release tag 2025-12-15 14:42:34 -05:00
Dax Raad 8b08e9cda5 ci: add app bundle target to fix macOS updater by generating .app.tar.gz files 2025-12-15 14:41:38 -05:00
GitHub Action b1b1df824c chore: format code 2025-12-15 19:33:56 +00:00
Dax Raad 7d1733c752 core: fix message caching for Anthropic models to improve response consistency 2025-12-15 14:33:14 -05:00
opencode cf05e6e02b release: v1.0.156
Update Nix Hashes / update (push) Has been cancelled
2025-12-15 19:28:02 +00:00
Adam 7e49d0fb15 fix: connect provider on homepage 2025-12-15 13:24:30 -06:00
Adam c4f63824df fix: terminal in desktop 2025-12-15 13:09:13 -06:00
Adam 4236744fb5 fix: image attachments in desktop 2025-12-15 12:01:11 -06:00
Github Action 284c045795 Update Nix flake.lock and hashes 2025-12-15 16:57:28 +00:00
Dax Raad 2c53abd70c docs: update header navigation to include desktop download 2025-12-15 11:56:53 -05:00
Adam b7a9cbfc68 fix: share page 2025-12-15 10:56:07 -06:00
Dax Raad 46a35dfc1b docs: restore desktop beta banner to homepage 2025-12-15 11:54:09 -05:00
GitHub Action b7597c12dd chore: format code 2025-12-15 16:52:45 +00:00
Nalin Singh 6830590183 feat: add F# language server support (#5549) 2025-12-15 10:52:16 -06:00
opencode b9b4349039 release: v1.0.155
Update Nix Hashes / update (push) Has been cancelled
2025-12-15 16:44:08 +00:00
Dax Raad 4107918909 20min 2025-12-15 11:31:38 -05:00
Adam 6347ee9988 chore: update stats 2025-12-15 10:31:11 -06:00
GitHub Action 9daa4e04ea chore: format code 2025-12-15 16:22:49 +00:00
Adam ed96ae9d45 chore: cleanup 2025-12-15 10:22:07 -06:00
Adam 8ce0966987 wip(desktop): progress 2025-12-15 10:22:06 -06:00
Adam 8cb26b6066 wip(desktop): progress 2025-12-15 10:22:06 -06:00
Adam 5cf6a1343c wip(desktop): progress 2025-12-15 10:22:04 -06:00
Adam 44d6c5780d wip(desktop): progress 2025-12-15 10:20:20 -06:00
Adam 5eaa8e1bf4 chore: cleanup 2025-12-15 10:20:20 -06:00
Adam df2713a6c2 chore: cleanup 2025-12-15 10:20:19 -06:00
Adam ff6864a7ca feat(desktop): custom commands 2025-12-15 10:20:19 -06:00
Adam 5e37a902ce wip(desktop): progress 2025-12-15 10:20:19 -06:00
Adam df2ebfac7d wip(desktop): progress 2025-12-15 10:20:19 -06:00
Adam 5fbcb203f5 wip(desktop): progress 2025-12-15 10:20:19 -06:00
Adam 34db739442 wip(desktop): progress 2025-12-15 10:20:18 -06:00
Adam ae8c4154aa wip(desktop): progress 2025-12-15 10:20:18 -06:00
Adam 315836c0b7 wip(desktop): progress 2025-12-15 10:20:18 -06:00
Adam c0d009d5f3 wip(desktop): progress 2025-12-15 10:20:18 -06:00
Adam c36f3b9dbe wip(desktop): progress 2025-12-15 10:20:17 -06:00
Adam d31824320e Revert "wip(desktop): session turn state consolidation"
This reverts commit 453f862616dc4d3ac90680581cde279e118b0da1.
2025-12-15 10:20:17 -06:00
Adam 88c0675148 wip(desktop): progress 2025-12-15 10:20:17 -06:00
Adam 82c4755fb0 wip(desktop): progress 2025-12-15 10:20:17 -06:00
Adam 40572eeba4 wip(desktop): progress 2025-12-15 10:20:17 -06:00
Adam d81d63045a wip(desktop): session turn state consolidation 2025-12-15 10:20:16 -06:00
Adam ece3bfd93d wip(desktop): progress 2025-12-15 10:20:16 -06:00
Adam acd91bddf7 wip(desktop): progress 2025-12-15 10:20:16 -06:00
Adam 3a14ca044c wip(desktop): progress 2025-12-15 10:20:16 -06:00
Adam d66d806700 wip(desktop): progress 2025-12-15 10:20:16 -06:00
Adam e9b95b2e91 wip(desktop): progress 2025-12-15 10:20:15 -06:00
opencode 56dde2cc83 release: v1.0.154
Update Nix Hashes / update (push) Has been cancelled
2025-12-15 16:01:15 +00:00
Dax Raad d2ce368a3f ci: update publish workflow concurrency to include version inputs and upgrade ubuntu runner to 24.04 2025-12-15 10:14:20 -05:00
GitHub Action f492122d59 chore: format code 2025-12-15 14:59:05 +00:00
Dax Raad b0f77da56c core: reorganize agent configuration to separate primary agents (build, plan) from subagents 2025-12-15 09:58:23 -05:00
Dax Raad 274b86b19b ci: fix AppImage build hanging by using portable appimage format
- Add appimage target back to tauri.conf.json
- Force reinstall tauri-cli from feat/truly-portable-appimage branch
- Add 10 minute timeout to prevent indefinite hangs
- Add logging to verify correct tauri-cli version is installed

This fixes the issue where AppImage builds would hang at 'Running input plugin: gtk' by using the new portable appimage format that bypasses linuxdeploy entirely.
2025-12-15 09:47:19 -05:00
René 2ca118db59 docs: Fix Wakatime repository link in ecosystem.mdx (#5552)
Co-authored-by: Github Action <action@github.com>
2025-12-15 08:32:06 -06:00
David Hill a0c0e2b5c3 fix: add tooltip to close review tab 2025-12-15 14:22:57 +00:00
David Hill d43fbec12d fix: prompt input using border shadow 2025-12-15 14:14:10 +00:00
David Hill bb426112ed fix: replace agents dropdown with shadow border 2025-12-15 14:09:47 +00:00
David Hill d2217bb825 fix: fix width and padding on agent select
- conditionally hide the role=presentation element because it was adding extra gap before the first agent
2025-12-15 14:05:24 +00:00
David Hill ac495bd351 fix: hide prompt input send tooltip when the send button is disabled 2025-12-15 13:47:50 +00:00
David Hill b913eb7acc fix: avatar and icon size in sidebar 2025-12-15 13:21:59 +00:00
David Hill ea65a91b2e fix: remove blue border from prompt input 2025-12-15 13:11:09 +00:00
GitHub Action ed6d749104 ignore: update download stats 2025-12-15 2025-12-15 12:05:05 +00:00
René 9eefcd1b41 Provider fix, anthropic Errorhandling if empty image file is read (#5521) 2025-12-14 23:56:47 -06:00
Nalin Singh 7c1124199e fix: input lip visibility for transparent themes (#5544) 2025-12-14 23:10:33 -06:00
Spoon 5cf126d489 fix(edit): add per-file lock to prevent read-before-write race (#4388) 2025-12-14 23:01:50 -06:00
Aiden Cline 509f7d9617 ignore: fix debug var in last commit 2025-12-14 22:59:30 -06:00
opencode-agent[bot] ae1bf92c81 Add dismiss button to Getting Started box (#5543)
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-14 22:58:26 -06:00
DS b021b26e77 feat: restore experimental.chat.messages.transform and add experimental.chat.system.transform hooks (#5542) 2025-12-14 22:51:11 -06:00
Aiden Cline 9555d348de ci: switch model 2025-12-14 22:41:54 -06:00
Brendan Allan 220c564047 tauri: use correct sidecar name 2025-12-15 12:40:49 +08:00
Brendan Allan cf5c0129ac tauri: rename sidecar to opencode-cli 2025-12-15 12:40:14 +08:00
Aiden Cline 543dbe71d2 ci: smart oc 2025-12-14 22:36:46 -06:00
Ravi Kumar 54569b5552 fix(session): fix unshare command not clearing share state (#5523) 2025-12-14 22:05:06 -06:00
GitHub Action 6a09861806 chore: format code 2025-12-15 03:42:56 +00:00
Adam 79a4c65313 fix: test 2025-12-14 21:42:17 -06:00
Adam 654534ac71 fix: update sdk 2025-12-14 21:41:20 -06:00
Adam ba16bfdf3d wip(desktop): progress 2025-12-14 21:38:59 -06:00
Adam ad5614bbb9 wip(desktop): progress 2025-12-14 21:38:59 -06:00
Adam dda579c8ad wip(desktop): progress 2025-12-14 21:38:59 -06:00
Adam 4246cdb069 wip(desktop): progress 2025-12-14 21:38:58 -06:00
Adam 7ade6d386d wip(desktop): progress 2025-12-14 21:38:58 -06:00
Adam 2613f44961 wip(desktop): progress 2025-12-14 21:38:58 -06:00
Adam 62ffeb3987 fix(desktop): auto scroll 2025-12-14 21:38:58 -06:00
Adam 4a8e8f537c wip(desktop): progress 2025-12-14 21:38:58 -06:00
Adam a68bee7878 fix(desktop): layout fixes 2025-12-14 21:38:57 -06:00
Mark Jaquith ed33d82535 feat(cli): auto-submit prompt when using --prompt flag (#4510) 2025-12-14 21:06:04 -06:00
Dax Raad 2d63c22d1a fix share link 2025-12-14 22:05:53 -05:00
Dax Raad e22af25076 ci: fix test failures in CI by pre-populating models cache
Tests were failing in CI because the models.json cache file doesn't exist
and the data() macro fallback only works at build time, not runtime.
The preload now pre-fetches models.json and disables the background
refresh to prevent race conditions during test execution.
2025-12-14 21:23:45 -05:00
Dax Raad 622caae9c9 fix desktop updater 2025-12-14 21:12:38 -05:00
Dax fed4776451 LLM cleanup (#5462)
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-14 21:11:30 -05:00
Ravi Kumar fdf560c343 fix(tui): --continue selects wrong session (#5513) 2025-12-14 19:50:54 -06:00
GitHub Action fc3ffb2bf9 chore: format code 2025-12-14 23:14:05 +00:00
Martijn Baay 7368342bab feat: add experimental.continue_loop_on_deny config option (#4729)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-14 17:13:32 -06:00
Dax Raad c8fc910533 ignore: simplify download page to use GitHub latest redirect URLs 2025-12-14 17:10:59 -05:00
GitHub Action 0f9ef84d55 chore: format code 2025-12-14 22:05:28 +00:00
Dax Raad 74b5c285cf disable app image 2025-12-14 17:04:46 -05:00
opencode a34e67b518 release: v1.0.153
Update Nix Hashes / update (push) Has been cancelled
2025-12-14 19:04:01 +00:00
Aiden Cline 0c7f0cfa2e tweak: fallback to provider default for temperature 2025-12-14 11:57:12 -06:00
GitHub Action 10ee6d345b chore: format code 2025-12-14 17:48:34 +00:00
Nalin Singh 48ec68730f fix: ensure input borders are drawn in transparent themes (#5524) 2025-12-14 11:48:02 -06:00
GitHub Action 70e4efe429 chore: format code 2025-12-14 17:46:46 +00:00
Sellers Crisp 92948ed8a4 feat: add server_error, rate_limit, and no_kv_space retry logic to accommodate Foundry API issues (#5527)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-14 11:46:14 -06:00
shekohex 6d412d8872 docs: add opencode-pty and opencode-google-antigravity-auth plugins to the echosystem (#5530) 2025-12-14 11:38:53 -06:00
Lawrence Sarpong e6a0a005d6 Add Gleam LSP and formatter (#5514) 2025-12-14 10:51:08 -06:00
Aymvn 90d44751e7 docs: Fix Wakatime link in ecosystem documentation (#5528) 2025-12-14 10:49:35 -06:00
GitHub Action 4d062ba1b2 ignore: update download stats 2025-12-14 2025-12-14 12:04:18 +00:00
Aiden Cline f8bca50f00 rm unnecessary code 2025-12-13 23:45:15 -06:00
Aiden Cline 3d2ef28fa8 add topK function to transform, add temp defaults for glm and minimax 2025-12-13 23:27:11 -06:00
YeonGyu-Kim 210b3e905b fix(ui): guard Node reference for SSR compatibility in isTriggerTitle (#5509) 2025-12-13 22:28:14 -06:00
Brendan Allan 96975ef8d6 tauri: change mainBinaryName to just OpenCode 2025-12-14 12:08:53 +08:00
Brendan Allan b8b998be56 tauri: bring back appimage 2025-12-14 12:00:14 +08:00
Sachnun d8ac35f6e5 fix(tui): open parent session instead of subagent on continue flag (#5503) 2025-12-13 21:09:42 -06:00
Zhou Rui ed1eacfce0 docs: Add opencode-websearch-cited to plugin list (#5501) 2025-12-13 20:28:39 -06:00
Adam 629f475f63 fix: sort models 2025-12-13 20:25:24 -06:00
Adam 43a7c1dd8c fix: use opencode icon 2025-12-13 20:25:24 -06:00
Adam e288ce0fca chore: cleanup 2025-12-13 20:25:24 -06:00
Adam 67b3fcb31a chore: cleanup 2025-12-13 20:25:24 -06:00
Tommy D. Rossi aedb5550a8 fix: limit LSP diagnostics to prevent context window waste (#5480)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-13 19:56:26 -06:00
Aiden Cline 1638ffde69 docs: networking 2025-12-13 18:20:44 -06:00
Aiden Cline d4cfbd8219 chore: reduce duplication of field in transform 2025-12-13 18:07:22 -06:00
Adam c7bac83212 chore: cleanup 2025-12-13 16:17:32 -06:00
Adam fc9789d7a7 fix(desktop): archive button 2025-12-13 16:14:31 -06:00
Adam a8957d8d16 fix(desktop): auto scroll 2025-12-13 15:56:12 -06:00
Adam 0660433921 feat(desktop): show richer status when thinking 2025-12-13 15:47:50 -06:00
Adam 1a6f4f1c0d fix: css scroll jitter 2025-12-13 15:36:28 -06:00
Adam 974a24ba02 fix: don't rotate placeholders in session 2025-12-13 15:25:56 -06:00
Adam 5ebe29de1e fix: don't open shell by default 2025-12-13 15:17:22 -06:00
Github Action 6bdf8b1fe1 Update Nix flake.lock and hashes 2025-12-13 21:13:53 +00:00
Adam 5bcc93851c chore: cleanup 2025-12-13 15:12:41 -06:00
Adam d0789632b4 fix(desktop): terminal light mode 2025-12-13 15:12:32 -06:00
Adam a6e297baad feat(desktop): message history 2025-12-13 14:57:24 -06:00
Adam 307af10c8b fix: session turn scroll 2025-12-13 14:57:23 -06:00
Felipe Oduardo Sierra f254cf76d9 add ARM64 Docker image support (#5483) 2025-12-13 13:01:59 -06:00
Github Action b4ffaa21ec Update Nix flake.lock and hashes 2025-12-13 19:01:20 +00:00
Aiden Cline 7bf6f264e4 bump bun version & set flags this time 2025-12-13 13:00:03 -06:00
GitHub Action 7434fbba8e chore: format code 2025-12-13 17:34:07 +00:00
Jan-Niklas W. b7581e01ea docs: fix title for JetBrains ACP config file (#5479) 2025-12-13 11:33:31 -06:00
YeonGyu-Kim b46d4789fc docs: add oh-my-opencode to plugins list (#5481) 2025-12-13 11:33:10 -06:00
GitHub Action 199bd8a9a2 chore: format code 2025-12-13 17:30:48 +00:00
rari404 decf2452c4 feat: add dockerfile language server (#5252)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-13 11:30:15 -06:00
GitHub Action d8663a44c2 ignore: update download stats 2025-12-13 2025-12-13 12:04:19 +00:00
rari404 8917a4c609 feat: add texlab language server and latexindent formatter (#5251) 2025-12-12 23:50:09 -06:00
GitHub Action 5d7a52f8b8 chore: format code 2025-12-13 02:09:41 +00:00
Jan-Niklas W. b7b827c5bd docs: JetBrains IDEs to ACP config docs page (#5465) 2025-12-12 20:09:08 -06:00
Matt Silverlock 613e082358 github: support GITHUB_TOKEN + skip OIDC (#5459) 2025-12-12 19:55:46 -06:00
Charles Cooper b6856bd593 fix: add --session flag to attach command (#5460)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-12 19:45:28 -06:00
David Hill 7cb5a77ba6 fix: mute the project path in the sidebar that proceeds the final directory 2025-12-12 23:45:39 +00:00
David Hill cd9898a565 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-12 23:25:11 +00:00
Dax Raad a4ffa869cc fix 2025-12-12 18:15:31 -05:00
David Hill dbc84ff4c3 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-12 22:50:50 +00:00
David Hill c11ea3fd92 fix: mute the whole prompt area when leader key is active 2025-12-12 22:50:48 +00:00
GitHub Action 3c3a0f8afb chore: format code 2025-12-12 22:48:43 +00:00
Aiden Cline b93614cb81 docs: add env vars sections 2025-12-12 16:47:50 -06:00
opencode b84d513bd7 release: v1.0.152
Update Nix Hashes / update (push) Has been cancelled
2025-12-12 22:29:21 +00:00
Adam 0554d03162 Revert "fix: archive button"
This reverts commit bc3286de46.
2025-12-12 16:16:52 -06:00
Aiden Cline 15caecdb45 shell tweaks, better handling for windows (#5455)
Co-authored-by: GitHub Action <action@github.com>
2025-12-12 16:11:07 -06:00
Adam 91ab966921 fix: max height on bash tool 2025-12-12 16:10:13 -06:00
Adam bc3286de46 fix: archive button 2025-12-12 16:03:07 -06:00
Dax Raad af45444496 desktop: fix build on Linux and Windows by making macOS title bar styling conditional 2025-12-12 16:47:48 -05:00
Sebastian Herrlinger 43202f2820 only exit app when prompt is empty, otherwise fallthrough, fix #5457 2025-12-12 22:45:28 +01:00
GitHub Action ce37e11bfe chore: format code 2025-12-12 21:44:09 +00:00
Dan Brown 6e9833acce Shell: No -l in fallback, for max compatibility (#5452) 2025-12-12 15:43:35 -06:00
opencode 379c4ecab3 release: v1.0.151
Update Nix Hashes / update (push) Has been cancelled
2025-12-12 21:34:32 +00:00
Github Action f1db4b60c4 Update Nix flake.lock and hashes 2025-12-12 21:28:16 +00:00
Adam 9846b26be7 fix: desktop layout 2025-12-12 15:26:53 -06:00
Adam d6ba6af6f3 fix: desktop layout 2025-12-12 15:24:43 -06:00
Adam d463ade028 fix: desktop layout 2025-12-12 15:24:43 -06:00
Adam 6c3495a75a fix: desktop layout 2025-12-12 15:24:43 -06:00
Adam a16edb4ea0 fix: desktop layout 2025-12-12 15:24:43 -06:00
Adam 9efe09564b fix: desktop layout 2025-12-12 15:24:42 -06:00
Adam ccdd77032a fix: desktop layout 2025-12-12 15:24:42 -06:00
Adam 41e234c6d0 fix: desktop layout 2025-12-12 15:24:42 -06:00
Adam 3e03646e42 fix: desktop layout 2025-12-12 15:24:42 -06:00
Adam f7acc34327 fix: desktop layout 2025-12-12 15:24:41 -06:00
Adam bf420e7df6 chore: cleanup 2025-12-12 15:24:41 -06:00
Adam 78484f545c chore: cleanup 2025-12-12 15:24:41 -06:00
Adam ad008d2151 wip: desktop timeline changes 2025-12-12 15:24:41 -06:00
Dax Raad 651a10d6db only gen summary if diffs 2025-12-12 16:24:26 -05:00
Aiden Cline f9674793af tweak: 5.1 -> 5. in transform 2025-12-12 15:17:11 -06:00
GitHub Action f3a33d41f1 chore: format code 2025-12-12 20:26:02 +00:00
David Hill 642eec3dfd Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-12 20:25:21 +00:00
Dax Raad 73513612d4 reuse existing server query 2025-12-12 15:23:38 -05:00
David Hill 9b77246246 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-12 20:23:06 +00:00
David Hill cf3bc1e0a6 fix: increase font-size-small to 13px 2025-12-12 20:22:51 +00:00
David Hill 4550ad049e fix: make syntax colors have more contrast 2025-12-12 20:22:08 +00:00
Dax Raad d51c6ca39f sync 2025-12-12 15:21:29 -05:00
Dax Raad 47c6a2430c sync 2025-12-12 15:18:07 -05:00
Aiden Cline 909013320b tweak: 5.1 -> 5. reasoning effort match 2025-12-12 13:21:57 -06:00
Frank 770a4d87db Zen: add gpt5.2 2025-12-12 13:48:54 -05:00
Luke Parker 2e417c4d8c fix: osascript for clipboard typo (#5430) 2025-12-12 11:37:43 -06:00
Adam 2da527aaa6 fix: desktop layout 2025-12-12 11:11:17 -06:00
Jeremy Osih 0303eb0cc1 Change tooltip text from 'Open file' to 'New Terminal' (#5435) 2025-12-12 10:57:52 -06:00
Adam 3f4a792c8a fix: tweak missing colors 2025-12-12 10:56:30 -06:00
Adam 9629f7464b chore: cleanup 2025-12-12 10:45:34 -06:00
xu0o0 9017d10303 acp: fix internal error on /compact (#5424) 2025-12-12 10:43:18 -06:00
Brendan Allan 59d4041aa4 tauri: add basic custom titlebar (#5438) 2025-12-12 09:37:17 -06:00
Adam feb8c4f3c6 feat(desktop): archive sessions 2025-12-12 07:07:54 -06:00
Adam 3f5cd2c4a8 fix(desktop): audio stuff 2025-12-12 06:21:31 -06:00
Adam a160eb76df fix(desktop): homedir aware path on home 2025-12-12 06:21:31 -06:00
GitHub Action e4b2028f91 ignore: update download stats 2025-12-12 2025-12-12 12:04:34 +00:00
Github Action ffc48e115b Update Nix flake.lock and hashes 2025-12-12 11:16:22 +00:00
Adam 04b4dacee3 feat(desktop): basic alerting 2025-12-12 05:14:51 -06:00
GitHub Action c0e30f48c6 chore: format code 2025-12-12 09:44:46 +00:00
David Hill 99158e736b Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-12 09:44:06 +00:00
David Hill 4c02d515a1 wip: desktop sidebar icon updates 2025-12-12 09:44:04 +00:00
David Hill b803a9732d fix: make the logo on the home screen non-selectable 2025-12-12 09:43:41 +00:00
Brendan Allan f9d5e18790 tauri: create window with full screen size 2025-12-12 17:04:00 +08:00
Github Action 147111c9c6 Update Nix flake.lock and hashes 2025-12-12 09:00:19 +00:00
GitHub Action 9a70eb538b chore: format code 2025-12-12 08:59:18 +00:00
Brendan Allan 0b1731142e tauri: initialise store and window-state plugins 2025-12-12 16:58:37 +08:00
Viktor Forsman 7ec48dfd15 fix: debug lsp diagnostics cmd for certain lsps (#5420) 2025-12-11 23:20:38 -06:00
Frank 57120e69ed Zen: sync 2025-12-11 23:41:04 -05:00
Rhys Sullivan 11efda3f5c [feat]: show indicator for in progress chats in the sessions list (#5417) 2025-12-11 21:57:03 -05:00
Sachnun a5cb4e41f5 fix(tui): restore input on timeline revert and show newest first (#5366) 2025-12-11 20:07:31 -06:00
GitHub Action 88b2382b97 chore: format code 2025-12-12 02:06:49 +00:00
Sachnun 237c0253c2 fix(server): make time field optional in session update validator (#5372) 2025-12-11 20:06:15 -06:00
xu0o0 a9f27371cf acp: replay conversation history in session/load (#5385) 2025-12-11 20:02:06 -06:00
opencode-agent[bot] 9c126c5b64 Removed cache mention from webfetch prompt. (#5412)
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-11 19:53:39 -06:00
Zeno Jiricek e241aa21b9 docs: Add opencode-md-table-formatter and plugin template (#5405) 2025-12-11 17:02:49 -06:00
Koichi Nakayamada 1fd005838c fix(tui): ensure fatal error UI is readable in light mode (#5387) 2025-12-11 16:39:27 -06:00
Koichi Nakayamada 61ba844234 fix: add fg color to some TUI text elements for light mode visibility (#5378) 2025-12-11 16:29:34 -06:00
Adán ffec52a17c fix: Windows LSP URIs using backslashes (Biome initialization failure) (#5317) 2025-12-11 16:28:39 -06:00
Aiden Cline 342595e0f7 tweak: setting chat_template_args in transform 2025-12-11 16:22:10 -06:00
Aiden Cline b41051b4ee tweak(tui): better error msg rendering 2025-12-11 16:22:10 -06:00
Dax Raad 6f8746ab94 console: fix download route async handling 2025-12-11 16:55:23 -05:00
Dax Raad 9d4ed5b04a console: fix download links to use dynamic GitHub release version 2025-12-11 16:49:15 -05:00
Adam e149b7c1e2 fix: avatar colors 2025-12-11 15:39:41 -06:00
GitHub Action 55957b2ac7 chore: format code 2025-12-11 21:34:17 +00:00
Jay V 14291bff71 docs: lander copy 2025-12-11 16:26:42 -05:00
Adam a0472c0312 fix: free model logic 2025-12-11 15:24:32 -06:00
Dax Raad 0400024d02 core: fix missing Flag import in session prompt 2025-12-11 16:18:04 -05:00
Dax Raad d4dc142cc2 core: add client identification to user agent and request headers for better tracking 2025-12-11 16:18:04 -05:00
Adam bfdb236581 fix: toast colors 2025-12-11 15:16:44 -06:00
Aiden Cline 4e92f54415 tweak: bash tool, try to prevent the cd spam 2025-12-11 14:58:46 -06:00
Adam f8dc740c61 fix: remove settings button 2025-12-11 14:55:41 -06:00
Adam dea5111a5a fix: message nav popover placement 2025-12-11 14:52:06 -06:00
Aiden Cline 4f5abe387d tweak: bash tool deny msg 2025-12-11 14:46:58 -06:00
Adam 7d55aeee0a fix: no loading state in message nav 2025-12-11 14:46:32 -06:00
Adam b34f434332 fix: message order ascending 2025-12-11 14:46:32 -06:00
Jorgen Henriksen 2a9269c347 add experimental.chat.messages.transform hook (#5207)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-11 14:38:45 -06:00
Sachnun 0efdc3a8a0 feat: add caching for opencode binary in GitHub Actions (#5384) 2025-12-11 14:14:37 -06:00
GitHub Action fc9dfd054a chore: format code 2025-12-11 20:14:20 +00:00
Dax Raad 013bf079cc ignore: added debug logging for share data loading performance 2025-12-11 15:13:36 -05:00
opencode 5ffcde9dba release: v1.0.150
Update Nix Hashes / update (push) Has been cancelled
2025-12-11 20:09:20 +00:00
Dax Raad e0f885ffc8 ci 2025-12-11 14:57:54 -05:00
Aiden Cline 5b21334fdd fix 2025-12-11 13:55:08 -06:00
Shantur Rathore 755a79cd8e fix: Writing to Bun.stderr causes CPU pinning to 100%. (#5396) 2025-12-11 13:47:59 -06:00
Adam 16b7370d8c wip(desktop): progress 2025-12-11 13:42:47 -06:00
Adam 634fd62b25 wip(desktop): progress 2025-12-11 13:42:47 -06:00
Adam e845eedbc3 wip(desktop): progress 2025-12-11 13:42:47 -06:00
Adam 4ae7e1b19c wip(desktop): progress 2025-12-11 13:42:46 -06:00
Adam 0ca758e135 wip(desktop): progress 2025-12-11 13:42:45 -06:00
Dax Raad ea8508ee44 ci 2025-12-11 14:38:45 -05:00
GitHub Action 78d4f32d79 chore: format code 2025-12-11 19:35:36 +00:00
David Hill afcd547a16 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-11 19:34:34 +00:00
David Hill 521fbb93cd wip: lander updates 2025-12-11 19:33:55 +00:00
Dax Raad 9d73096db0 ci: fix Ubuntu host detection in Tauri build workflow 2025-12-11 13:27:21 -05:00
GitHub Action c11bb440e6 chore: format code 2025-12-11 18:21:10 +00:00
Sebastian Herrlinger 80e04be84f fix super modifier parsing 2025-12-11 19:20:23 +01:00
Dax Raad 639320b3e1 ci: install libglib2.0-dev on Blacksmith Linux runners to fix missing glib-2.0 system library error 2025-12-11 13:16:44 -05:00
GitHub Action 55ea20de84 chore: format code 2025-12-11 18:12:25 +00:00
Dax Raad 21cf3a7c1b ci 2025-12-11 13:11:36 -05:00
David Hill 4f855072f2 wip: avatar colors 2025-12-11 18:07:14 +00:00
David Hill 11e6a181ad fix: free cta 2025-12-11 18:07:14 +00:00
GitHub Action 4c9208fbf1 chore: format code 2025-12-11 18:05:35 +00:00
Dax Raad 3a9bbe2371 ci: use Blacksmith runners for Linux and Windows Tauri builds to improve build performance 2025-12-11 13:04:55 -05:00
opencode 6382bda7d6 release: v1.0.149
Update Nix Hashes / update (push) Has been cancelled
2025-12-11 18:00:30 +00:00
Dax Raad 885a142ae3 temporarily remove undo/redo keybinds 2025-12-11 12:55:08 -05:00
GitHub Action 4387602f9d chore: format code 2025-12-11 17:49:43 +00:00
opencode 71fc8b2115 release: v1.0.148
Update Nix Hashes / update (push) Has been cancelled
2025-12-11 17:49:42 +00:00
Dax Raad 137716e0dc ci: fix tauri release asset naming to use opencode-desktop-[platform]-[arch][ext] format 2025-12-11 12:45:48 -05:00
Dax Raad 95526fb9ed fix share image 2025-12-11 12:20:11 -05:00
GitHub Action a5cc19068b chore: format code 2025-12-11 16:46:43 +00:00
opencode 48c7913431 release: v1.0.147
Update Nix Hashes / update (push) Has been cancelled
2025-12-11 16:46:43 +00:00
Dax Raad 89d9856ed2 ci: enable tauri desktop app to be published with GitHub release ID and tag name 2025-12-11 11:41:54 -05:00
Sebastian Herrlinger c12be73bf7 align default input keybinds 2025-12-11 17:39:29 +01:00
Github Action de6fb3126c Update Nix flake.lock and hashes 2025-12-11 16:25:22 +00:00
GitHub Action fad5cbe6c7 chore: format code 2025-12-11 16:24:30 +00:00
Sebastian Herrlinger e271852bc3 allow custom mappings for all textarea actions via config 2025-12-11 17:23:47 +01:00
David Hill 4e02704f17 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-11 16:21:32 +00:00
David Hill b17fdc7f4e fix: inverted text colors 2025-12-11 16:21:31 +00:00
Dax Raad df4d9236a6 fix bug causing windows builds to sometimes fail 2025-12-11 11:01:43 -05:00
GDR! 0ee626ba9f Add ripgrep to Docker image (#5379) 2025-12-11 09:47:56 -06:00
Daniel Polito dce4585d40 Fix / Improve Github Eyes Reaction Removal (#5383) 2025-12-11 09:42:10 -06:00
David Hill 5ffc1617fe Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-11 14:37:05 +00:00
David Hill 93dd01947d wip: update lander video 2025-12-11 14:36:49 +00:00
Brendan Allan 5e7d908dc9 tauri: always build cli in predev 2025-12-11 22:20:22 +08:00
David Hill eb6596cb97 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-11 13:10:46 +00:00
David Hill 8fcd31f353 wip: social share image 2025-12-11 13:03:08 +00:00
Adam dfdd1c9b20 fix: load fonts the right way 2025-12-11 06:51:39 -06:00
Adam d7b8dce6a7 fix: missing font 2025-12-11 06:49:00 -06:00
Adam f9f78122d0 wip(desktop): progress 2025-12-11 06:48:59 -06:00
Adam 1980113ee4 wip(desktop): progress 2025-12-11 06:48:59 -06:00
Adam 85c0311d38 wip(desktop): progress 2025-12-11 06:48:59 -06:00
Adam 740aa8e541 wip(desktop): progress 2025-12-11 06:48:59 -06:00
Adam 1b331548ba wip(desktop): progress 2025-12-11 06:48:59 -06:00
Adam 3bb546c94d wip(desktop): progress 2025-12-11 06:48:58 -06:00
David Hill 8e15bcb68e Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-11 12:40:24 +00:00
David Hill ca08dc87dd wip: add faq to download 2025-12-11 12:38:40 +00:00
David Hill 4287552991 wip: add more items to download 2025-12-11 12:26:47 +00:00
GitHub Action f1af54ada1 chore: format code 2025-12-11 12:12:13 +00:00
David Hill 577cacb7db Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-11 12:11:36 +00:00
David Hill 8346550d26 fix: change terminal title to OpenCode 2025-12-11 12:10:04 +00:00
David Hill a8e3caca3f wip: lander updates 2025-12-11 12:09:31 +00:00
GitHub Action fd83578442 ignore: update download stats 2025-12-11 2025-12-11 12:04:43 +00:00
David Hill e91d121ef8 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-11 10:47:24 +00:00
David Hill 1b4975ba54 wip: hero and nav updates 2025-12-11 10:47:22 +00:00
David Hill 380d2c466e wip: lander hero updates 2025-12-11 10:36:31 +00:00
Brendan Allan 53d2edc0f2 write typescript artifacts to node_modules/.ts-dist 2025-12-11 18:35:07 +08:00
David Hill 02705e460f wip: downloads 2025-12-11 10:19:41 +00:00
GitHub Action 44cd384e3c chore: format code 2025-12-11 06:33:51 +00:00
Aiden Cline 8ac5204009 ignore: Comment out plugin entry in opencode.jsonc
Comment out the plugin entry in opencode.jsonc
2025-12-11 00:33:17 -06:00
opencode ddd5464081 release: v1.0.146
Update Nix Hashes / update (push) Has been cancelled
2025-12-11 06:23:53 +00:00
opencode fbad378966 release: v1.0.145
Update Nix Hashes / update (push) Has been cancelled
2025-12-11 06:19:42 +00:00
Dax Raad c211b22a45 update 2025-12-11 01:15:34 -05:00
Dax Raad 5d57d0385c opencode config 2025-12-11 01:13:23 -05:00
Aiden Cline f7e29a1acf downgrade bun 2025-12-11 00:10:58 -06:00
Aiden Cline a8f83cdcb5 ignore: comment out item in project cfg 2025-12-11 00:09:43 -06:00
Github Action 9e0a2bc7d0 Update Nix flake.lock and hashes 2025-12-11 06:03:16 +00:00
Dax Raad 2d9c76baae enterprise: add default social card images to HTML head
- Add og:image and twitter:image meta tags to entry-server
- Provide fallback social card image for pages without specific social cards
2025-12-11 01:02:31 -05:00
Dax Raad 85c01e8694 enterprise: add social card meta tags to share pages
- Add og:image and twitter:image meta tags for better social sharing
- Generate dynamic social card URLs with session title, models, and version
- Include description meta tag for search engines
2025-12-11 01:01:32 -05:00
GitHub Action 65a6b3d585 chore: format code 2025-12-11 05:54:58 +00:00
Dax Raad 40f121c3e8 prevent indexing of share page 2025-12-11 00:54:15 -05:00
opencode 6251231e41 release: v1.0.144
Update Nix Hashes / update (push) Has been cancelled
2025-12-11 05:48:13 +00:00
Dax Raad 578072bb8e use new share url 2025-12-11 00:43:00 -05:00
Dax Raad 231390cb7b ci 2025-12-11 00:23:06 -05:00
Dax Raad 5955d20539 remove 2025-12-11 00:21:40 -05:00
Dax Raad 4309c078fb domain 2025-12-11 00:11:40 -05:00
Dax Raad d14462f7a7 fix 2025-12-10 23:19:28 -05:00
Dax Raad a02223a310 sync 2025-12-10 23:19:28 -05:00
Ayush Walekar d93c8c7604 docs: update doc sdk.mdx (#5315) 2025-12-10 21:56:23 -06:00
Aiden Cline 7eb509db14 ci: rm bash tool from opencode ci workflow, reduce risks 2025-12-10 21:45:46 -06:00
Dax Raad f1b8707286 ignore 2025-12-10 22:36:57 -05:00
Dax Raad 9b05217471 ignore 2025-12-10 22:34:16 -05:00
Dax Raad d88912abf0 global bus 2025-12-10 22:28:56 -05:00
GitHub Action 28c6320cd6 chore: format code 2025-12-11 03:22:51 +00:00
Dax Raad 13a77005f1 global.dispose 2025-12-10 22:22:16 -05:00
Dax Raad 530b75a92a ignore 2025-12-10 22:22:16 -05:00
Aiden Cline 7b4f852f33 ignore: tmp transform exclusion 2025-12-10 21:18:19 -06:00
Aiden Cline 439aebb4e9 tweak: correct thinkingLevel 2025-12-10 21:13:10 -06:00
Brendan Allan 6f5f73a74a desktop: exclude ts-dist from tsconfig sources 2025-12-11 09:51:35 +08:00
Brendan Allan bd1f5f884e tauri: update icons 2025-12-11 09:49:00 +08:00
Koichi Nakayamada 499ad4f84b fix: light mode visibility for filepath in /undo diff files (#5352) 2025-12-10 19:38:14 -06:00
Jinhyeok Lee 01fd0d8209 docs(bash): clarify description parameter is required (#5353) 2025-12-10 19:37:38 -06:00
Seb Duerr df55ad89ab Add Cerebras integration header with opencode identifier (#5354) 2025-12-10 19:36:58 -06:00
David Hill a5a3060208 wip: lander updates 2025-12-11 00:56:05 +00:00
David Hill a468044c9f Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-11 00:46:07 +00:00
David Hill f0274fd29f wip: lander updates 2025-12-11 00:46:02 +00:00
Dax Raad fadeed1fa4 desktop: enable zoom hotkeys in Tauri app 2025-12-10 19:27:05 -05:00
David Hill e9d3e240c2 wip: lander updates 2025-12-11 00:18:22 +00:00
Dax Raad 13611176b0 fix deploy 2025-12-10 19:11:49 -05:00
Jay V 92fa66d76f core: reposition OpenCode as open source multi-platform coding agent
docs: update main intro page to reflect open source positioning and multi-platform availability
2025-12-10 19:05:33 -05:00
David Hill fba0aad2f8 wip: lander updates 2025-12-10 23:57:11 +00:00
Jay V 1a1874d8b3 docs: desktop 2025-12-10 18:43:19 -05:00
Adam 56540f8312 wip(desktop): progress 2025-12-10 17:31:13 -06:00
Shantur Rathore 89d51ad596 compaction: improve compaction prompt (#5348) 2025-12-10 17:21:38 -06:00
Adam 15b8c14542 fix: tauri 2025-12-10 17:19:50 -06:00
Adam 85cfa226c3 wip(desktop): progress 2025-12-10 17:17:37 -06:00
Christian Stewart cbb591eb7d fix: more descriptive tool or subtask execution failed error (#5337)
Signed-off-by: Christian Stewart <christian@aperture.us>
2025-12-10 17:12:49 -06:00
Aiden Cline e36c349222 tweak: oc -> OC 2025-12-10 17:06:16 -06:00
Christian Stewart b274371dbb feat: use |- for intermediate sub-agent steps (#5336)
Signed-off-by: Christian Stewart <christian@aperture.us>
2025-12-10 16:36:11 -06:00
Hammad Shami 72eb004057 feat: add helicone docs + helicone session tracking (#5265) 2025-12-10 16:23:52 -06:00
Yukai Huang e46080aa8c fix(auth): add plugin lookup for custom provider in 'Other' flow (#5324) 2025-12-10 16:23:12 -06:00
Aiden Cline 7d82f1769c tweak: small fix 2025-12-10 16:01:10 -06:00
OpeOginni 7435d94f85 fix(cli): obtain directory data from server (#5320) 2025-12-10 15:55:15 -06:00
Github Action e060f968f5 Update Nix flake.lock and hashes 2025-12-10 21:18:57 +00:00
Dax Raad 86f7cc17ae tui: pass dynamic port to frontend
Frontend now receives the server port via window.__OPENCODE__.port,
allowing it to connect when using a random free port instead of hardcoded 4096
2025-12-10 16:17:36 -05:00
Adam 58e66dd3d1 wip(desktop): progress 2025-12-10 15:17:04 -06:00
Adam 190fa4c87a wip(desktop): progress 2025-12-10 15:17:03 -06:00
Adam 91d743ef9a wip(desktop): progress 2025-12-10 15:17:03 -06:00
Adam 804ad5897f wip(desktop): progress 2025-12-10 15:17:03 -06:00
Adam f20d6e8555 wip(desktop): progress 2025-12-10 15:17:03 -06:00
Adam e694d4d880 wip(desktop): progress 2025-12-10 15:17:02 -06:00
Adam ada40decd1 wip(desktop): progress 2025-12-10 15:17:02 -06:00
Adam 6866a060bc wip(desktop): progress 2025-12-10 15:17:02 -06:00
Adam a4ec619c74 wip(desktop): progress 2025-12-10 15:17:02 -06:00
Adam 67a95c3cc8 wip(desktop): progress 2025-12-10 15:17:01 -06:00
Dax Raad 8d3eac2347 fix type 2025-12-10 16:14:32 -05:00
Dax Raad 9ad828dcd0 tui: use random free port and enable icon discovery by default
- Tauri app now automatically finds an available port instead of defaulting to 4096
- Icon discovery feature is now enabled by default in the Tauri app
- Prevents port conflicts when multiple OpenCode instances are running
2025-12-10 16:13:11 -05:00
Aiden Cline 59fb3ae606 ignore: add bash tests 2025-12-10 15:07:40 -06:00
GitHub Action 0ab3b88250 chore: format code 2025-12-10 21:01:06 +00:00
Dax Raad a1175bddcd gen types 2025-12-10 15:59:42 -05:00
Dax Raad 936a6be5d6 stuff adam needs 2025-12-10 15:59:42 -05:00
igordertigor 03c6c3f4cb docs: document accept always behavior (#5340)
Co-authored-by: Ingo Fruend <ingo@oudyo.com>
2025-12-10 14:59:12 -06:00
Aiden Cline 6288a032fd bump bun to 1.3.4 2025-12-10 14:48:52 -06:00
Daniel Polito 31e6ed6806 Add Eyes Reaction instead of Comment Working on Github Action (#5072)
publish-github-action / publish (push) Has been cancelled
Update Nix Hashes / update (push) Has been cancelled
2025-12-10 13:35:55 -06:00
Aiden Cline da56319af4 ignore: fix test 2025-12-10 13:31:45 -06:00
GitHub Action 2198f9400f chore: format code 2025-12-10 19:31:11 +00:00
Dax Raad ffc4d53923 add models.dev family 2025-12-10 14:30:28 -05:00
Aiden Cline 18d3c054a3 more interleaved thinking fixes (#5334) 2025-12-10 13:29:26 -06:00
Aiden Cline 59c5da9b6c tweak: set gemini thinkingLevel to high by default 2025-12-10 13:09:27 -06:00
Github Action 15880195a2 Update Nix flake.lock and hashes 2025-12-10 19:04:03 +00:00
Tom Aylott 117de64f39 fix: upgrade OpenRouter sdk pkg (#5302)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-10 13:02:26 -06:00
Dax Raad 388156704a fix /provider endpoint to return loaded providers 2025-12-10 13:49:45 -05:00
GitHub Action faf443132f chore: format code 2025-12-10 18:32:21 +00:00
Nick 36a9be040b docs: add opencode-type-inject to ecosystem plugins (#5331) 2025-12-10 12:31:44 -06:00
GitHub Action 1835d7526f chore: format code
publish-github-action / publish (push) Has been cancelled
Update Nix Hashes / update (push) Has been cancelled
2025-12-10 16:55:19 +00:00
Stanislas 946e4f0a61 docs: add wakatime plugin to ecosystem page (#5326)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-10 10:54:45 -06:00
Matt Silverlock ae60f41adf themes: update orng theme (#5329) 2025-12-10 10:52:58 -06:00
GitHub Action 6b93d23642 chore: format code 2025-12-10 16:50:49 +00:00
Noè cfa13df346 docs: Added opencode-antigravity-auth to ecosystem documentation (#5303) 2025-12-10 10:50:12 -06:00
Frank 744a7159e4 zen: sync 2025-12-10 11:44:37 -05:00
arc-source-coder 80d1c62818 tui: only show active MCP server count (#5327) 2025-12-10 10:27:59 -06:00
Connor Adams 83aa42f510 docs: configure mise to use latest version instead of pinned version (#5316) 2025-12-10 10:11:48 -06:00
Aiden Cline 183a1a181c ci: stop zed sync temporarily 2025-12-10 09:57:34 -06:00
Github Action bc7e7c2c4d Update Nix flake.lock and hashes 2025-12-10 14:04:52 +00:00
GitHub Action 7b5bd89570 chore: format code 2025-12-10 14:04:08 +00:00
Sebastian Herrlinger ba1c6122b9 bump opentui to v0.1.60, fixing doubled key events on some older terminal emulators and add_buffer leaks for prompt input 2025-12-10 15:03:14 +01:00
Sebastian Herrlinger baed581a7c remove input_forward_delete special handling 2025-12-10 14:55:56 +01:00
GitHub Action 4a23052778 chore: format code 2025-12-10 13:53:21 +00:00
opencode ee4190aa41 release: v1.0.143
Update Nix Hashes / update (push) Has been cancelled
2025-12-10 13:53:21 +00:00
Dax Raad de8460cb99 docs: improve bash and grep tool documentation with clearer usage guidelines 2025-12-10 08:48:41 -05:00
opencode f7b2beaaf1 release: v1.0.142
Update Nix Hashes / update (push) Has been cancelled
2025-12-10 13:25:55 +00:00
GitHub Action 9b0933187e ignore: update download stats 2025-12-10 2025-12-10 12:04:47 +00:00
Adam 862141e8b2 fix: exit aliases 2025-12-10 02:49:54 -06:00
Aiden Cline 070ced0b3f fix: revert hook try/catch that surpressed errors 2025-12-10 00:14:24 -06:00
GitHub Action cc3b699823 chore: format code 2025-12-10 06:02:31 +00:00
spoj 301f1a191b fix: add Windows support for shell mode (! command) (#5311) 2025-12-10 00:01:56 -06:00
Adam d149c25aab fix: types 2025-12-09 21:44:34 -06:00
Adam 18d24b8f5f wip(desktop): progress 2025-12-09 21:39:13 -06:00
Adam cf34981e8f wip(desktop): progress 2025-12-09 21:39:13 -06:00
Adam e2ebe560ea feat: provider icon component 2025-12-09 21:39:12 -06:00
GitHub Action 6db822fd92 chore: format code 2025-12-10 03:32:26 +00:00
Brendan Allan 661122bab8 tauri: don't ask to restart separately in updater 2025-12-10 11:31:50 +08:00
Brendan Allan 4a96836d11 tauri: update macos icon 2025-12-10 11:28:40 +08:00
Brendan Allan e072f9605c tauri: comment out restart server dialog 2025-12-10 11:18:17 +08:00
Brendan Allan 9986031481 fix: use project references for desktop typecheck 2025-12-10 11:15:12 +08:00
Dax Raad 3d95848607 ci 2025-12-09 22:14:18 -05:00
GitHub Action 221c9028af chore: format code 2025-12-10 03:10:18 +00:00
Timor b2057791aa feat: add CLI arguments to agent create command for scripting (#5157) 2025-12-09 21:09:45 -06:00
Dax Raad c1ee6d6c41 ci 2025-12-10 02:57:39 +00:00
opencode a3fbbece9a release: v1.0.141
Update Nix Hashes / update (push) Has been cancelled
2025-12-10 02:57:38 +00:00
Dax Raad e72c974c4c ci 2025-12-09 21:49:38 -05:00
Dax Raad a762da7cab ci 2025-12-09 21:49:05 -05:00
Dax Raad fa6c060324 ci 2025-12-09 21:44:13 -05:00
Dax Raad 8e33ac052b ci: publish with multiple tags instead of using dist-tag
npm dist-tag add command is broken, so publish package multiple times
with different tags directly instead
2025-12-09 21:39:05 -05:00
Dax Raad 0759696ec0 core: enable project discovery for experimental builds 2025-12-09 21:23:55 -05:00
Hosenur Rahaman 59dce63471 docs: Add portal project to ecosystem documentation (#5300)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-09 20:23:14 -06:00
Dax Raad 1ae28090e3 ci 2025-12-09 21:16:36 -05:00
Dax Raad 0decdf6a55 ci 2025-12-09 21:16:23 -05:00
Dax Raad 09b402a274 ci 2025-12-09 21:16:00 -05:00
Dax Raad 150baf3e96 ci 2025-12-09 21:10:58 -05:00
Dax Raad 78c51371af sync 2025-12-09 21:06:57 -05:00
Dax Raad 6dbcacf3ea ci 2025-12-09 21:04:06 -05:00
Dax Raad 4ecebc2c83 ci 2025-12-09 21:03:19 -05:00
Dax Raad 38a79fa449 ci 2025-12-09 21:02:04 -05:00
Dax Raad bafad6b8a8 ci 2025-12-09 21:01:48 -05:00
Dax Raad 5682dddd45 ci 2025-12-09 21:01:39 -05:00
Dax Raad a9aacdb94a ci 2025-12-09 20:59:31 -05:00
Dax Raad e7e32c946b ci 2025-12-09 20:58:01 -05:00
Dax Raad fc9bc26d86 ci 2025-12-09 20:56:13 -05:00
Dax Raad ee00b4e0ce ci 2025-12-09 20:54:56 -05:00
Dax Raad f82156f0b1 ci 2025-12-09 20:51:54 -05:00
Dax Raad 2ed6298584 ci 2025-12-09 20:51:25 -05:00
Dax Raad 52ef8dea3e ci 2025-12-09 20:50:46 -05:00
Adam ebe6015db0 fix: re-enable tauri typecheck 2025-12-09 19:34:07 -06:00
Dax Raad 56526114e4 ci 2025-12-09 20:26:46 -05:00
Dax Raad bb1c225027 ci 2025-12-09 20:24:31 -05:00
Dax Raad e5af0dde08 ci 2025-12-09 20:22:00 -05:00
Dax Raad 3cf17bc24f ci 2025-12-09 20:20:47 -05:00
Dax Raad 4aa1b8de0e ci 2025-12-09 20:19:43 -05:00
Dax Raad 73e9534d08 ci 2025-12-09 20:17:07 -05:00
Dax Raad cb188f907f ci 2025-12-09 19:53:50 -05:00
Dax Raad 63d9656ad8 ci 2025-12-09 19:46:59 -05:00
Dax Raad 3512d02e9e ci 2025-12-09 19:46:38 -05:00
Dax Raad 1efdceaf10 ci: combine sdk and format workflows into single generate workflow 2025-12-09 19:41:23 -05:00
GitHub Action 632a0fe009 chore: regen sdk 2025-12-10 00:33:05 +00:00
Dax Raad 6fb32cebec ci 2025-12-09 19:32:30 -05:00
GitHub Action 8b8b17d755 chore: format code 2025-12-10 00:28:05 +00:00
GitHub Action 2c27afaaf5 chore: regen sdk 2025-12-10 00:27:29 +00:00
Dax Raad 4bdc7c1426 ci fix 2025-12-09 19:26:54 -05:00
Github Action 3c1e6c2c8f Update Nix flake.lock and hashes 2025-12-09 23:56:13 +00:00
Aiden Cline b8f5809f95 ignore: rm chalk 2025-12-09 17:54:53 -06:00
Aiden Cline 552ee81455 tweak: add OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS 2025-12-09 17:28:34 -06:00
David Hill 9fdbe193cd fix: add spacer before queued badge 2025-12-09 22:56:44 +00:00
Aiden Cline df64612d54 better interleaved thinking support (#5298) 2025-12-09 16:32:12 -06:00
Adam 0aa3e6c270 wip(desktop): progress 2025-12-09 16:23:05 -06:00
Adam 44c17c1435 wip(desktop): progress 2025-12-09 16:23:05 -06:00
Dax Raad 132e772c26 core: fix project icon update handling to preserve existing icon properties 2025-12-09 16:55:26 -05:00
Adam 62cbed57cc wip(desktop): progress 2025-12-09 15:55:08 -06:00
Adam ebab7e176e wip(desktop): progress 2025-12-09 15:53:08 -06:00
Adam 9c93853e22 wip(desktop): progress 2025-12-09 15:46:23 -06:00
Aiden Cline 8a9c7a4ef3 add OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT 2025-12-09 15:44:56 -06:00
Adam 2dad56c9a2 wip(desktop): progress 2025-12-09 15:39:44 -06:00
Adam 41d78c1ecc wip(desktop): progress 2025-12-09 15:32:36 -06:00
ry2009 16c4b02b69 feat: add Biome LSP support (#5290) 2025-12-09 15:31:13 -06:00
Adam 35c04d9283 wip(desktop): progress 2025-12-09 15:24:11 -06:00
Adam 1fbd7a7f9a wip(desktop): progress 2025-12-09 15:21:47 -06:00
Adam d7563d1694 wip(desktop): progress 2025-12-09 15:21:47 -06:00
Adam b9fa7d9163 wip(desktop): progress 2025-12-09 15:21:47 -06:00
Adam f736751ab2 wip(desktop): progress 2025-12-09 15:21:45 -06:00
Aiden Cline dbcc779f0b ci: fix sdk workflow 2025-12-09 15:08:30 -06:00
Nick c33a90320c fix: resolve 'latest' to actual version when caching plugins (#5292) 2025-12-09 15:07:59 -06:00
Dax Raad 802b862aae format on write 2025-12-09 16:06:57 -05:00
GitHub Action b0cd171c1b chore: format code 2025-12-09 21:05:09 +00:00
GitHub Action 13755f4680 chore: regen sdk 2025-12-09 21:04:37 +00:00
Dax Raad b242659cc3 fix types 2025-12-09 16:03:58 -05:00
Dax Raad 5f6b2fdc6f fix tests 2025-12-09 16:03:33 -05:00
GitHub Action e34f18991e chore: format code 2025-12-09 20:53:06 +00:00
GitHub Action 209b0a06f7 chore: regen sdk 2025-12-09 20:52:31 +00:00
Dax Raad a2e460bc4b discover logic 2025-12-09 15:51:55 -05:00
GitHub Action fc9081afe4 chore: format code 2025-12-09 20:49:25 +00:00
GitHub Action 1a3f7c3d84 chore: regen sdk 2025-12-09 20:48:49 +00:00
Dax Raad 06aa1f49b8 sync 2025-12-09 15:48:22 -05:00
GitHub Action dfd67cd922 chore: format code 2025-12-09 20:44:09 +00:00
GitHub Action 70f7287ca1 chore: regen sdk 2025-12-09 20:43:35 +00:00
Dax Raad f1955b4d05 core: fix project event emission to include updated worktree data 2025-12-09 15:41:38 -05:00
Dax Raad c5e5627cbd test fixes 2025-12-09 15:41:38 -05:00
GitHub Action 93378526b9 chore: format code 2025-12-09 20:26:02 +00:00
GitHub Action abf176a335 chore: regen sdk 2025-12-09 20:25:21 +00:00
Aiden Cline 84a0868e66 fix: read when file is svg 2025-12-09 14:24:30 -06:00
GitHub Action 75a9c42789 chore: format code 2025-12-09 20:20:03 +00:00
GitHub Action 204fa54625 chore: regen sdk 2025-12-09 20:19:27 +00:00
Dax Raad 365584048f core: fix project creation to include updated timestamp 2025-12-09 15:18:55 -05:00
Dax Raad edffcc32cf core: make project updated timestamp optional to support legacy project data 2025-12-09 15:18:55 -05:00
GitHub Action 238f441bcb chore: format code 2025-12-09 20:17:37 +00:00
GitHub Action 0571a8302c chore: regen sdk 2025-12-09 20:16:59 +00:00
Aiden Cline 8c07382382 ci: fix sdk gen 2025-12-09 14:16:22 -06:00
GitHub Action fa32fbd187 chore: format code 2025-12-09 20:12:07 +00:00
GitHub Action 0fd2ecd0ba chore: regen sdk 2025-12-09 20:11:32 +00:00
Dax Raad 7439a40b00 core: fix project icon update to preserve existing icon properties 2025-12-09 15:11:00 -05:00
GitHub Action 2ad99713f3 chore: format code 2025-12-09 20:07:32 +00:00
GitHub Action 19ec970701 chore: regen sdk 2025-12-09 20:06:57 +00:00
Dax Raad b48caec218 core: add automatic project icon discovery from favicon/logo files 2025-12-09 15:06:24 -05:00
GitHub Action 380c34af53 chore: format code 2025-12-09 19:54:54 +00:00
GitHub Action 553d9013eb chore: regen sdk 2025-12-09 19:54:19 +00:00
Dax Raad 8bff3cdae8 fix ci 2025-12-09 14:53:47 -05:00
Dax Raad 0b40c3d37d rework project loading 2025-12-09 14:41:14 -05:00
Dax Raad 1e3bdcc71c rename bus 2025-12-09 14:32:09 -05:00
GitHub Action de577e17da chore: format code 2025-12-09 19:31:07 +00:00
GitHub Action 8a9e258ad7 chore: regen sdk 2025-12-09 19:30:32 +00:00
Adam 9a34965432 feat: add color to project 2025-12-09 13:29:59 -06:00
Adam c944d19c3b wip(desktop): progress 2025-12-09 13:24:37 -06:00
Dax Raad fb1b6c5e6b add project.name/icon 2025-12-09 13:57:18 -05:00
GitHub Action ad0c4c5d89 chore: format code 2025-12-09 18:37:14 +00:00
GitHub Action a54b663a39 chore: regen sdk 2025-12-09 18:36:38 +00:00
Adam ae4993f39a wip(desktop): progress 2025-12-09 12:36:06 -06:00
GitHub Action aa638cec48 chore: format code 2025-12-09 18:10:31 +00:00
GitHub Action 4db4a90559 chore: regen sdk 2025-12-09 18:07:43 +00:00
Aiden Cline e23a81097c core: add test to prevent MCP headers regression when OAuth is enabled
Custom headers configured for remote MCP servers were being silently
dropped when OAuth was enabled (the default). This test ensures headers
are always sent to MCP servers regardless of OAuth configuration.
2025-12-09 12:06:40 -06:00
opencode 76f4803d8d release: v1.0.138
Update Nix Hashes / update (push) Has been cancelled
2025-12-09 18:05:21 +00:00
GitHub Action 22e4649318 chore: format code 2025-12-09 17:53:53 +00:00
GitHub Action 0ac70ff261 chore: regen sdk 2025-12-09 17:53:14 +00:00
Adam 1bc1e56da3 wip(desktop): progress 2025-12-09 11:52:43 -06:00
GitHub Action 0d0c20e673 chore: format code 2025-12-09 17:45:35 +00:00
Jay a964824b22 docs: Modify documentation for SDK ecosystem references
Updated link text to refer to community-built projects.
2025-12-09 12:44:33 -05:00
GitHub Action 2cf0d578fe chore: regen sdk 2025-12-09 17:43:58 +00:00
Jay 13e8fb382f docs: Update community plugins reference in documentation 2025-12-09 12:43:29 -05:00
GitHub Action 4090bc9dea chore: format code 2025-12-09 17:39:25 +00:00
Aiden Cline cec1caf99e ci: sdk stuff 2025-12-09 11:38:22 -06:00
GitHub Action c74da97d52 chore: regen sdk 2025-12-09 17:38:01 +00:00
Jay 1f2497ce69 docs: Add submission note for OpenCode projects
Added a note about submitting projects to the list.
2025-12-09 12:37:34 -05:00
GitHub Action 986f14cb15 chore: format code 2025-12-09 17:37:32 +00:00
GitHub Action 34f639d510 chore: regen sdk 2025-12-09 17:34:45 +00:00
Aiden Cline defe51c825 docs: fix name 2025-12-09 11:34:15 -06:00
GitHub Action 5a16acef8c chore: format code 2025-12-09 17:26:28 +00:00
Aiden Cline 2ce249dbc0 docs: OpenCode ecosystem (#5287)
Co-authored-by: GitHub Action <action@github.com>
2025-12-09 11:25:28 -06:00
GitHub Action 7ba6b18945 chore: format code 2025-12-09 17:10:13 +00:00
GitHub Action b8c0b393bf chore: regen sdk 2025-12-09 17:09:35 +00:00
Adam 5442adb517 wip(desktop): progress 2025-12-09 11:09:00 -06:00
Adam 6b2ac20abc wip(desktop): progress 2025-12-09 11:09:00 -06:00
GitHub Action 3efc95b157 chore: format code 2025-12-09 16:53:58 +00:00
GitHub Action cd9db8a81d chore: regen sdk 2025-12-09 16:53:24 +00:00
Dax Raad 036f5d4eef core: add project update timestamps to track when projects were last modified
Projects now track when they were last updated, making it easier for users
to see recent activity and identify stale projects in their workspace.
2025-12-09 11:52:53 -05:00
GitHub Action c4401290db chore: format code 2025-12-09 15:53:48 +00:00
GitHub Action 4a6deb6420 chore: regen sdk 2025-12-09 15:53:12 +00:00
André Cruz 87a03e1e30 fix(mcp): send custom headers regardless of OAuth settings (#5273)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-09 09:52:39 -06:00
GitHub Action 01dc9d7ec6 chore: format code 2025-12-09 15:47:54 +00:00
GitHub Action e78e0f9841 chore: regen sdk 2025-12-09 15:47:19 +00:00
Ravi Kumar 8326640670 feat(telemetry): Add userId and sessionId metadata to experimental_telemetry (#5279) 2025-12-09 09:46:48 -06:00
GitHub Action d079af4be2 chore: format code 2025-12-09 12:16:05 +00:00
GitHub Action 82c9584382 chore: regen sdk 2025-12-09 12:15:30 +00:00
Adam d3b6de855b chore: cleanup 2025-12-09 06:14:58 -06:00
Adam 5ad000fd99 chore: cleanup 2025-12-09 06:14:22 -06:00
Adam fe196da430 fix(tui): order 2025-12-09 06:13:36 -06:00
Adam 20662e2101 wip(desktop): progress 2025-12-09 06:12:09 -06:00
Adam 0a357be160 wip(desktop): progress 2025-12-09 06:12:09 -06:00
Adam d29205e677 fix: diff scroll gutter 2025-12-09 06:12:09 -06:00
GitHub Action 9d0630f094 ignore: update download stats 2025-12-09 2025-12-09 12:04:56 +00:00
Github Action b6844565e8 Update Nix flake.lock and hashes 2025-12-09 11:15:34 +00:00
GitHub Action 17d1b24def chore: format code 2025-12-09 11:15:02 +00:00
GitHub Action 3d279edf44 chore: regen sdk 2025-12-09 11:14:23 +00:00
Brendan Allan 0a47a3cea0 fix: use ts project references for desktop and tauri 2025-12-09 19:13:51 +08:00
Github Action 306d57fcde Update Nix flake.lock and hashes 2025-12-09 09:17:58 +00:00
GitHub Action ff6f1abf61 chore: format code 2025-12-09 09:17:36 +00:00
GitHub Action 331278a5be chore: regen sdk 2025-12-09 09:17:00 +00:00
Brendan Allan 78547f3c59 desktop: move updater logic to js 2025-12-09 17:16:24 +08:00
GitHub Action d32671224f chore: format code 2025-12-09 07:26:55 +00:00
GitHub Action 9ade416ad4 chore: regen sdk 2025-12-09 07:26:16 +00:00
Aiden Cline f8bd4ff705 core: refactor providerOptions function to accept Provider.Model for cleaner API 2025-12-09 01:25:36 -06:00
GitHub Action 2206e10d92 chore: format code 2025-12-09 06:42:56 +00:00
Aiden Cline e282d5dc42 ci: run format workflow after sdk workflow completion 2025-12-09 00:41:40 -06:00
GitHub Action 2b4a5aede1 chore: regen sdk 2025-12-09 06:37:35 +00:00
Aiden Cline 654a2cd6a4 core: remove unused fzf dependency to address CVE
- Eliminates fzf binary dependency that was no longer used after file search overhaul
- Removes fzf from Nix package configuration and Arch Linux PKGBUILD dependencies
2025-12-09 00:36:33 -06:00
GitHub Action 2252b5ca1b chore: format code 2025-12-09 03:18:47 +00:00
terakael c78dad8db5 tui: add permission indicator to footer (#4813)
Co-authored-by: terakael <terakael@gmail.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-08 21:18:17 -06:00
opencode 0569e8652c release: v1.0.137
Update Nix Hashes / update (push) Has been cancelled
2025-12-09 03:00:50 +00:00
GitHub Action f2d5b32e52 chore: regen sdk 2025-12-09 02:17:38 +00:00
Matt Silverlock b2f5ea7c30 themes: add new orng theme (#5267)
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-08 20:17:10 -06:00
GitHub Action 8eb8f1a16c chore: format code 2025-12-09 02:07:00 +00:00
Aiden Cline ad93d50ab7 ci: ignore 2025-12-08 20:06:27 -06:00
GitHub Action 73c5c328a8 chore: regen sdk 2025-12-09 01:07:23 +00:00
Dax Raad 72a13212e2 ci 2025-12-08 20:06:36 -05:00
Github Action 78bcf35f37 Update Nix flake.lock and hashes 2025-12-09 00:52:48 +00:00
GitHub Action 3a3ee3ecfd chore: format code 2025-12-09 00:52:05 +00:00
Sebastian Herrlinger 34e045c275 bump opentui to v0.1.59, allowing shift+space insert as char again 2025-12-09 01:51:27 +01:00
GitHub Action e187918ce0 chore: regen sdk 2025-12-08 23:46:24 +00:00
Aiden Cline 7ac17c7833 ci: ignore 2025-12-08 17:45:50 -06:00
GitHub Action b05d5b30fb chore: format code 2025-12-08 22:44:57 +00:00
Aiden Cline 57c08b01b7 ignore: mark ripgrep as an sst/tap/opencode depends_on 2025-12-08 16:42:32 -06:00
GitHub Action 08efb9cdf6 chore: regen sdk 2025-12-08 22:16:30 +00:00
Ariane Emory 8b51da768c fix: improve sidebar working directory colour contrast with modified files (resolves #5184 (#5188) 2025-12-08 16:15:40 -06:00
Github Action 862407c674 Update Nix flake.lock and hashes 2025-12-08 22:11:38 +00:00
GitHub Action a381aa7e0d chore: format code 2025-12-08 22:10:46 +00:00
Aiden Cline 4dcda3d53d fix: parcel watcher musl support 2025-12-08 16:08:53 -06:00
GitHub Action 479cca29a5 chore: regen sdk 2025-12-08 21:29:05 +00:00
Ravi Kumar e38814c597 fix: apply provider filtering to /connect endpoint (#5233) 2025-12-08 15:28:32 -06:00
GitHub Action 4d42daa9a3 chore: format code 2025-12-08 21:05:09 +00:00
Ariane Emory 130345bd5d fix: sort sessions by updated time (resolves #5074) (#5080) 2025-12-08 15:04:39 -06:00
Github Action 0baee00be4 Update Nix flake.lock and hashes 2025-12-08 21:00:43 +00:00
GitHub Action e0acd5d361 chore: regen sdk 2025-12-08 20:59:55 +00:00
Sebastian Herrlinger 1e7d78a215 bump opentui to v0.1.58, fixing diff color issues in light themes 2025-12-08 21:59:09 +01:00
GitHub Action 7a6cb85617 chore: format code 2025-12-08 20:34:25 +00:00
Shoubhit Dash 20530104ce add ocaml lsp support (#5230) 2025-12-08 14:33:45 -06:00
GitHub Action d22754dd68 chore: regen sdk 2025-12-08 20:06:51 +00:00
Sebastian Herrlinger db0e1ebb80 fix colors for dialog prompt (like session rename prompt in light theme) 2025-12-08 21:06:03 +01:00
Sebastian Herrlinger e83a47debe set theme text color for no matching items in auto complete 2025-12-08 20:59:00 +01:00
GitHub Action eed48e76de chore: format code 2025-12-08 18:06:48 +00:00
Aiden Cline ac70c1e813 docs: lsp & formatters 2025-12-08 12:05:25 -06:00
GitHub Action ac0bed16a2 chore: regen sdk 2025-12-08 18:02:54 +00:00
rari404 fab8ab2840 feat: add terraform-ls language server and formatter (#5243) 2025-12-08 12:02:25 -06:00
GitHub Action 09ff8eba00 chore: format code 2025-12-08 17:40:24 +00:00
rari404 9bd2ea5e5f feat: add bash-language-server LSP (#5246) 2025-12-08 11:39:49 -06:00
GitHub Action aa525482ae chore: regen sdk 2025-12-08 17:37:58 +00:00
Shoubhit Dash 285605737d add ocamlformat support (#5229) 2025-12-08 11:37:30 -06:00
GitHub Action 4201fe6e01 chore: format code 2025-12-08 17:31:56 +00:00
Daniel Polito 203f3312ee feat: ability to toggle MCP Servers in TUI (#4509) 2025-12-08 11:31:22 -06:00
GitHub Action 0c77c46dc7 chore: regen sdk 2025-12-08 17:28:14 +00:00
Jérôme Benoit 52bb43eebd fix: SAP AI Core Vercel AI SDK v2 support (#5180)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
2025-12-08 11:27:47 -06:00
GitHub Action e020f5355e chore: format code 2025-12-08 17:07:18 +00:00
rari404 9b86d4e595 feat: add theme support for thinking text opacity (#5240) 2025-12-08 11:06:46 -06:00
GitHub Action f3d3b41a3f chore: regen sdk 2025-12-08 16:27:32 +00:00
Bishal Roy a959199f09 feat: add catppuccin-macchiato theme (#5225) 2025-12-08 10:27:03 -06:00
Adam 5e3a59d5a2 feat: resize handle 2025-12-08 10:24:26 -06:00
Adam 9f23d85e20 wip(desktop): progress 2025-12-08 10:24:26 -06:00
GitHub Action 54e15b38ea chore: regen sdk 2025-12-08 15:48:52 +00:00
Adam d66b903e7f fix: opencode web command 2025-12-08 09:48:21 -06:00
Adam 4425c66732 revert: opencode web command changes
This reverts commit c347056246.
2025-12-08 09:47:43 -06:00
GitHub Action 0dd0250285 chore: format code 2025-12-08 15:40:18 +00:00
Adam c347056246 fix: opencode web command 2025-12-08 09:39:41 -06:00
GitHub Action 2a5255ac8f chore: regen sdk 2025-12-08 15:35:44 +00:00
Adam d9175be989 fix: opencode web command 2025-12-08 09:35:12 -06:00
GitHub Action cebbfcfbaa chore: format code 2025-12-08 15:19:26 +00:00
Adam de415be4f6 fix: opencode web command 2025-12-08 09:18:46 -06:00
Adam e8ce113b7f fix: bump localstorage 2025-12-08 09:05:26 -06:00
GitHub Action 8ca02b7664 chore: regen sdk 2025-12-08 12:48:30 +00:00
Adam 3f5bb21f16 chore: identity cleanup, vscode ext images 2025-12-08 06:47:57 -06:00
Github Action fe114c41b5 Update Nix flake.lock and hashes 2025-12-08 12:25:44 +00:00
GitHub Action d90fd8a5d7 chore: format code 2025-12-08 12:24:56 +00:00
Adam 9363c15b4a feat: better code and diff perf 2025-12-08 06:24:24 -06:00
GitHub Action 3325823f23 ignore: update download stats 2025-12-08 2025-12-08 12:04:36 +00:00
GitHub Action 4b4d8da1ad chore: regen sdk 2025-12-08 05:46:02 +00:00
Brendan Allan d531dff8d3 Export DesktopInterface from desktop and add PlatformContext 2025-12-08 13:43:36 +08:00
GitHub Action 923bf36593 chore: format code 2025-12-08 04:54:08 +00:00
Justin Vogt d6e499dd48 docs: Update "model" inheritance for agents (#4894)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-07 22:53:41 -06:00
GitHub Action aec1497c6e chore: regen sdk 2025-12-08 04:43:13 +00:00
Aiden Cline f5a77c8cd8 bump plugin versions 2025-12-07 22:42:35 -06:00
GitHub Action bc524eeb44 chore: format code 2025-12-08 04:22:50 +00:00
ry2009 725f658260 fix: ensure Auth.all returns valid objs (#5128) 2025-12-07 22:22:21 -06:00
GitHub Action af1080dd42 chore: regen sdk 2025-12-08 04:21:06 +00:00
Ariane Emory 63e54541fe fix: Sort themes in the /theme modal alphabetically (resolves #5217) (#5219)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-07 22:20:40 -06:00
GitHub Action c3f7a88c1c chore: format code 2025-12-08 04:20:07 +00:00
opencode-agent[bot] 586a8b7b31 docs: azure content filter note (#5212)
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: GitHub Action <action@github.com>
2025-12-07 22:19:39 -06:00
GitHub Action 981744f802 chore: regen sdk 2025-12-08 03:54:26 +00:00
Aiden Cline a3bb4a3c85 pdf support in read tool (#5222)
Co-authored-by: ammi1378 <ammi1378@users.noreply.github.com>
2025-12-07 21:54:00 -06:00
GitHub Action 06ba1f76dc chore: format code 2025-12-08 03:49:13 +00:00
Aiden Cline 076c8b2ca1 ci: fix fmt 2025-12-07 21:48:41 -06:00
Adam 9a90939ac4 fix: use diff context instead of prop drilling 2025-12-07 21:21:18 -06:00
GitHub Action 123a136093 chore: regen sdk 2025-12-08 02:56:09 +00:00
Dax Raad 4126fedbd4 openapi 2025-12-07 21:55:36 -05:00
Dax Raad 145d185e6f ci 2025-12-07 21:51:22 -05:00
GitHub Action 6b32667c7d chore: regen sdk 2025-12-08 02:29:26 +00:00
Aiden Cline d8401e1937 ci: fix fmt 2025-12-07 20:28:50 -06:00
GitHub Action 55d6fcc350 chore: format code 2025-12-08 02:11:26 +00:00
Aiden Cline 9ff39503e9 tweak: additional error msg parsing case 2025-12-07 20:10:54 -06:00
GitHub Action f96c181afd chore: regen sdk 2025-12-08 01:48:52 +00:00
Aiden Cline f9b75a09df ignore: update description 2025-12-07 19:48:23 -06:00
GitHub Action 6111ed79b1 chore: format code 2025-12-08 01:47:31 +00:00
Ariane Emory 4cf2322b7f fix: toggle timestamps now properly hides/shows timestamps when toggled (resolves #5142) (#5145) 2025-12-07 19:47:02 -06:00
GitHub Action 2c6fcc5dc1 chore: regen sdk 2025-12-08 01:44:32 +00:00
franlol 81ee8541ab feat: add experimental.text.complete plugin hook (#4962) 2025-12-07 19:44:04 -06:00
GitHub Action 5b3550ab9f chore: format code 2025-12-08 01:13:22 +00:00
Patrick Erichsen 4fabce58d8 fix(cli): prevent help text wrapping (#5185) 2025-12-07 19:12:54 -06:00
GitHub Action da7edb5f5c chore: regen sdk 2025-12-08 00:58:40 +00:00
Dax Raad bf0f85e37f playing with sdk docs 2025-12-07 19:58:04 -05:00
Github Action 7b52160bff Update Nix flake.lock and hashes 2025-12-08 00:05:25 +00:00
GitHub Action fde97ec4a7 chore: format code 2025-12-08 00:04:40 +00:00
Dax ea7ec60f51 v2 SDK (#5216)
Co-authored-by: GitHub Action <action@github.com>
2025-12-07 19:04:14 -05:00
GitHub Action 6667856ba5 chore: format code 2025-12-07 20:53:12 +00:00
Dax Raad 13b2cf50ae remove outdated SDKs 2025-12-07 15:52:27 -05:00
Github Action 93b0abfce9 Update Nix flake.lock and hashes 2025-12-07 20:49:39 +00:00
GitHub Action f7e4c47113 chore: regen sdk 2025-12-07 20:47:51 +00:00
André Cruz 509e43d6f8 feat(mcp): add OAuth authentication support for remote MCP servers (#5014) 2025-12-07 15:47:27 -05:00
GitHub Action e693192e06 chore: regen sdk 2025-12-07 19:23:56 +00:00
rari404 ec27759f90 feat: add uninstall command (#5208) 2025-12-07 13:23:30 -06:00
GitHub Action 9c938eec73 chore: format code 2025-12-07 19:08:48 +00:00
secretninjaman 238b907dd8 fix: use basename for shell detection to support non-standard paths (#5205)
Co-authored-by: Ayato French <a@ayatofrench.com>
2025-12-07 13:08:26 -06:00
GitHub Action c16d8c6db8 chore: format code 2025-12-07 19:06:37 +00:00
Aiden Cline 9856e3b798 ignore: add test for provider url case 2025-12-07 13:06:12 -06:00
GitHub Action 1d089272c8 chore: regen sdk 2025-12-07 19:01:34 +00:00
Aiden Cline c30b1130ee fix: provider url merging logic 2025-12-07 13:01:05 -06:00
GitHub Action 40ca222d09 chore: format code 2025-12-07 18:48:45 +00:00
Dax Raad 0ecccbfd17 enable zoom hotkeys 2025-12-07 13:48:12 -05:00
GitHub Action 3f4862ced6 chore: regen sdk 2025-12-07 18:42:48 +00:00
Brendan Allan 1574e2457b Desktop macOS codesigning and notarization (#5154)
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Dax Raad <d@ironbay.co>
2025-12-07 13:42:23 -05:00
GitHub Action af33212f77 chore: format code 2025-12-07 18:26:05 +00:00
Mikheil Berishvili 4eb82e8c04 fix: autocomplete popup repositions on window resize (#5196) 2025-12-07 12:25:35 -06:00
GitHub Action a45f0aac90 chore: regen sdk 2025-12-07 18:18:10 +00:00
Aiden Cline eb4afdca65 ci: fix format 2025-12-07 12:17:40 -06:00
GitHub Action 9391749577 chore: format code 2025-12-07 15:33:51 +00:00
Carsten Kragelund Jørgensen 36a25660e9 fix: update zed agent server linux url to tar.gz (#5194) 2025-12-07 09:33:23 -06:00
GitHub Action d9fe722da1 ignore: update download stats 2025-12-07 2025-12-07 12:04:11 +00:00
GitHub Action da722e7db9 chore: regen sdk 2025-12-07 05:55:31 +00:00
Aiden Cline 75a4dcbce8 tweak: make bash give agent more awareness of cwd, bump default timeout, drop max timeout (#5140) 2025-12-06 23:55:07 -06:00
GitHub Action 3a179fcd34 chore: format code 2025-12-07 05:45:36 +00:00
Arindam Majumder ad22fe9fe7 docs: Nebius Token Factory provider documentation (#2997) 2025-12-06 23:45:08 -06:00
GitHub Action 6d622d91be chore: regen sdk 2025-12-07 05:33:26 +00:00
Aiden Cline aa884b003e core: prevent sessions from disappearing after git init
Previously, sessions created in a non-git directory would disappear from
the session picker after running git init and making the first commit.
This happened because the migration logic ran prematurely before a stable
project ID existed.
2025-12-06 23:32:47 -06:00
GitHub Action e0f77940f9 chore: format code 2025-12-06 23:25:16 +00:00
Aiden Cline 3a1718f62b ci: fix review 2025-12-06 17:24:44 -06:00
GitHub Action f7f9d3e5b9 chore: regen sdk 2025-12-06 23:23:24 +00:00
Dax Raad 68501d7799 ci 2025-12-06 18:22:55 -05:00
GitHub Action f8a987b135 chore: format code 2025-12-06 23:22:04 +00:00
Dax Raad dfea6780d9 sync 2025-12-06 18:21:32 -05:00
GitHub Action 2ac8dd6361 chore: regen sdk 2025-12-06 23:19:47 +00:00
Dax Raad dd0945b9ca tui: add visual separator between username and timestamp for better readability 2025-12-06 18:19:18 -05:00
Dax Raad 1b05d5dd8e tui: prevent deprecated models from appearing in model picker 2025-12-06 18:18:45 -05:00
GitHub Action 6923cc4a6a chore: format code 2025-12-06 23:15:39 +00:00
Aiden Cline 3feb69e63c ci: fix format 2025-12-06 17:15:04 -06:00
GitHub Action 6723792fbb chore: regen sdk 2025-12-06 22:05:07 +00:00
Aiden Cline 3e36069f41 fix: reduce overhead of task tool metadata 2025-12-06 16:04:33 -06:00
GitHub Action 6a4ca92a6c chore: format code 2025-12-06 20:49:33 +00:00
Saatvik Arya 3ec34ee3dd feat(tui): add dynamic terminal window title (#5112) 2025-12-06 14:49:11 -06:00
GitHub Action 2e5c2d5e98 chore: regen sdk 2025-12-06 20:48:46 +00:00
Anchit Bajaj 4dbe17d4f1 fix: update description to lowercase for ACP command (to be consistent with other commands) (#5137) 2025-12-06 14:48:21 -06:00
GitHub Action f18776cb49 chore: format code 2025-12-06 20:43:15 +00:00
Ariane Emory 429aa24275 fix: make timestamp toggle text dynamic in command list (resolves #5106) (#5108) 2025-12-06 14:42:46 -06:00
GitHub Action 741c9d3c63 chore: regen sdk 2025-12-06 20:30:00 +00:00
Ben Vargas 419983c0f1 feat: restore experimental flag for websearch/codesearch tools (#5132)
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-06 14:29:34 -06:00
GitHub Action a59c80e076 chore: format code 2025-12-06 20:28:25 +00:00
Cody Rayment 55981205da docs: update server API reference with complete endpoint list (#5136) 2025-12-06 14:28:02 -06:00
Dak Washbrook c1e6037bda feat: add mercury.com theme (#5141) 2025-12-06 14:27:56 -06:00
Github Action 8e816a9283 Update Nix flake.lock and hashes 2025-12-06 12:45:14 +00:00
GitHub Action d165c6b15a chore: regen sdk 2025-12-06 12:44:22 +00:00
Adam 9111005165 fix: terminal serialization and isolation 2025-12-06 06:43:53 -06:00
GitHub Action 3ceac25fb5 ignore: update download stats 2025-12-06 2025-12-06 12:04:12 +00:00
GitHub Action 8ec771d5be chore: format code 2025-12-06 11:53:20 +00:00
Adam 659e5653bc fix: exclude dist 2025-12-06 05:52:47 -06:00
Adam 3ff6de261c chore: update tauri update pub key 2025-12-06 05:49:05 -06:00
GitHub Action 25dae77fcd chore: regen sdk 2025-12-06 01:21:17 +00:00
Dax Raad 68daadcb56 sync 2025-12-05 20:20:47 -05:00
GitHub Action c9b1bb0285 chore: format code 2025-12-06 01:14:58 +00:00
Dax ac5809e757 Documentation edits made through Mintlify web editor 2025-12-05 20:14:29 -05:00
Dax 0db209a636 Documentation edits made through Mintlify web editor 2025-12-05 20:14:10 -05:00
GitHub Action 6c65f4acd1 chore: regen sdk 2025-12-06 01:13:23 +00:00
Dax Raad 3d447e8b12 ci 2025-12-05 20:12:52 -05:00
Dax Raad f8807144d4 openapi route 2025-12-05 20:11:01 -05:00
GitHub Action ebb4c8a724 chore: format code 2025-12-06 01:09:36 +00:00
Dax Raad 893f232b2f openapi json 2025-12-05 20:07:24 -05:00
GitHub Action a6aaf5429c chore: format code 2025-12-06 01:02:41 +00:00
Dax Raad 4ef239a086 openapi generate 2025-12-05 20:01:55 -05:00
David Hill 9362368fd3 fix: add extra line break to install page 2025-12-05 23:43:26 +00:00
Adam b35e010e2a feat: consistent (updated) social share images 2025-12-05 15:42:19 -06:00
GitHub Action cc35e6a019 chore: format code 2025-12-05 21:42:15 +00:00
Dax Raad 3281888160 ignore: docs test 2025-12-05 16:41:35 -05:00
GitHub Action c6d0ae892e chore: regen sdk 2025-12-05 21:02:17 +00:00
Aiden Cline df67ae9cbe ci: regen sdk instead of failing tests 2025-12-05 15:01:41 -06:00
GitHub Action ebe20efb29 chore: format code 2025-12-05 20:50:13 +00:00
franlol b03b9b9017 feat: add optional scrollbar to the session chat (#5116)
Co-authored-by: Sebastian <hasta84@gmail.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>
2025-12-05 14:49:05 -06:00
opencode 73258c6193 release: v1.0.134
Update Nix Hashes / update (push) Has been cancelled
2025-12-05 20:46:49 +00:00
Github Action dfe3fb8ed3 Update Nix flake.lock and hashes 2025-12-05 20:41:00 +00:00
Brendan Allan cd6bfb3f69 OpenCode Desktop app (#5044)
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
2025-12-05 14:39:49 -06:00
Shantur Rathore ba417d80b1 tweak: bash tool improve output metadata for agent consumption, fix small timeout issue (#5131) 2025-12-05 13:56:56 -06:00
Nathan Thomas 40eb8b93e1 feat: add max steps for supervisor and sub-agents (#4062)
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-05 12:26:44 -06:00
Adam 6e6bd1e171 fix(desktop): terminal cursor position 2025-12-05 12:05:30 -06:00
Adam 81ee2d2332 fix(desktop): prompting 2025-12-05 10:51:35 -06:00
Aiden Cline 85974e9acd ignore: regen sdk 2025-12-05 10:50:14 -06:00
Noam Bressler 864c098701 add experimental.open_telemetry config option to enable OTEL spans (#4978)
Co-authored-by: noamzbr <noamzbr@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>
2025-12-05 10:48:22 -06:00
Adam cfbaf81ef8 fix(desktop): clone pty session on reconnect 2025-12-05 10:30:48 -06:00
Adam 87a791fdb9 fix(desktop): new session not selecting tab 2025-12-05 10:30:48 -06:00
Anthony Shew ada7cca10d feat(theme): Vercel (#5119)
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-05 10:01:20 -06:00
Julian Visser 52db0f23a6 fix: #5064 ProviderInitError github-copilot-enterprise (#5123) 2025-12-05 09:53:32 -06:00
Dax Raad 60388f7f03 do not use required pty for local dev 2025-12-05 10:39:44 -05:00
Frank 53ed1c912b Zen: add codex max 2025-12-05 09:05:00 -05:00
GitHub Action 5f7ab83de4 ignore: update download stats 2025-12-05 2025-12-05 12:04:29 +00:00
Aiden Cline 05d2f70529 ignore: tweak 2025-12-05 01:00:47 -06:00
Aiden Cline f950de95ba fix: ensure projects that go from having no commits to having commits have sessions migrated (#5105)
Co-authored-by: GitHub Action <action@github.com>
2025-12-05 00:49:07 -06:00
Aiden Cline a4e5a72c36 ci: keybinds 2025-12-05 00:22:28 -06:00
ry2009 03324d4277 tui: wrap dialog option descriptions (#5083) 2025-12-05 00:19:48 -06:00
Aiden Cline e53580cb68 ignore: cmd tweak 2025-12-05 00:11:47 -06:00
GitHub Action 332ebe36c3 chore: format code 2025-12-05 05:14:21 +00:00
Aiden Cline 5013d64b28 ignore: rm slop commnand (only for opencode repo this isnt shipping) 2025-12-04 23:13:41 -06:00
Aiden Cline 767a81f930 fix: ensure that vcs is still set to git even if no commits in repo 2025-12-04 23:02:11 -06:00
Aiden Cline 78046dac8b ci: review 2025-12-04 23:02:11 -06:00
Dax Raad 95168b8267 increase default scroll speed 2025-12-04 23:54:46 -05:00
Dax Raad c264e9c364 fix 2025-12-04 23:48:32 -05:00
Dax Raad 856e1e2948 fix pty builds 2025-12-04 23:47:57 -05:00
Jérôme Benoit bef4fdfc4b fix: add getModel to SAP AI Core provider for correct SDK initialization (#5086)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
2025-12-04 22:43:22 -06:00
opencode-agent[bot] 095a1ab041 docs: llama.cpp docs: limit moved under model (#5089)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
2025-12-04 22:39:13 -06:00
Dax Raad 71e578eac9 ignore: fix provider credentials query for BYOK
Provider credentials field was being selected from ProviderTable even when the table wasn't joined (when byokProvider was undefined). Now the join is conditional - when byokProvider exists, we join and get the credentials; when it doesn't, the join condition is always false so provider remains null.
2025-12-04 22:57:39 -05:00
Frank 4380727727 zen: fix byok 2025-12-04 21:53:33 -05:00
Github Action 392d46933b Update Nix flake.lock and hashes 2025-12-05 02:33:39 +00:00
GitHub Action 2bc0b46ff4 chore: format code 2025-12-05 02:33:01 +00:00
Adam 09f522f0aa Reapply "feat(desktop): terminal pane (#5081)"
This reverts commit f9dcd97936.
2025-12-04 20:32:08 -06:00
Github Action d82bd430f6 Update Nix flake.lock and hashes 2025-12-04 22:02:17 +00:00
opencode 49800a00bd release: v1.0.133
Update Nix Hashes / update (push) Has been cancelled
2025-12-04 22:02:17 +00:00
Aiden Cline f9dcd97936 Revert "feat(desktop): terminal pane (#5081)"
This reverts commit d763c11a6d.
2025-12-04 15:57:01 -06:00
Adam d763c11a6d feat(desktop): terminal pane (#5081)
Co-authored-by: Github Action <action@github.com>
Co-authored-by: Dax Raad <d@ironbay.co>
2025-12-04 15:37:29 -06:00
Dax Raad b1202ac6db core: add test for custom model npm package inheritance 2025-12-04 16:30:54 -05:00
Aiden Cline d469d7d441 tweak: bash tool description re commit stuff 2025-12-04 15:27:23 -06:00
Cason Adams 48dc520fb8 docs: add CodeCompanion.nvim integration instructions (#5079) 2025-12-04 14:49:51 -06:00
Dax Raad 668d5a76d5 core: ensure model npm package falls back to dev models config when not explicitly defined 2025-12-04 15:39:52 -05:00
Jérôme Benoit b9c1f10016 feat: Add SAP AI Core provider support (#5023)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
2025-12-04 14:07:23 -06:00
Aiden Cline 8a0c86cbdb bump: builtin plugin versions 2025-12-04 12:37:14 -06:00
Daniel Polito 7f86fe3f61 add optional prompt Input to Github Action (#4828)
Co-authored-by: Github Action <action@github.com>
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>
2025-12-04 12:10:56 -06:00
Aiden Cline a32cf70d7e tui: fix /new slash command being persisted in prompt input 2025-12-04 12:01:13 -06:00
Shantur Rathore a607f33552 tweak: bash tool messages regarding timeouts and truncation more clear for agent (#5066) 2025-12-04 11:33:00 -06:00
Aiden Cline 350a32274a fix: model not being passed correctly to tool 2025-12-04 11:15:30 -06:00
Daniel Gray 27c99b46cb Preserve prompt input when creating new session (#4993) 2025-12-04 11:12:58 -06:00
Adam 1d6e3d477b fix(tui): cursor color 2025-12-04 06:56:48 -06:00
GitHub Action efbb973393 ignore: update download stats 2025-12-04 2025-12-04 12:04:38 +00:00
Aiden Cline 45bc7a6a9d ci: cleaner 2025-12-04 01:14:09 -06:00
Aiden Cline 088ebb967f ci: only maintainer can trigger 2025-12-04 01:07:04 -06:00
Frank bcf740f98a zen: make session provider sticky 2025-12-03 23:33:46 -05:00
wsx99outlook 6b80fff2bb ci: use blacksmith runners in review workflow too (#5042) 2025-12-03 22:30:00 -06:00
GitHub Action 2e63fedb76 chore: format code 2025-12-04 04:29:03 +00:00
YeonGyu-Kim 5a9f4e5c60 fix: ensure checkUpgrade sets init: (#5040) 2025-12-03 22:28:35 -06:00
opencode d0a48a09e2 release: v1.0.132
Update Nix Hashes / update (push) Has been cancelled
2025-12-04 04:23:39 +00:00
GitHub Action c0a21e7025 chore: format code 2025-12-04 04:19:15 +00:00
Dax Raad 10cc15aabe fix anthropic api key error 2025-12-03 23:18:24 -05:00
opencode adf7681100 release: v1.0.131
Update Nix Hashes / update (push) Has been cancelled
2025-12-04 04:11:48 +00:00
Aiden Cline 0237905b96 fix: TypeError: undefined is not an object 2025-12-03 22:03:42 -06:00
GitHub Action e8aa79bab6 chore: format code 2025-12-04 03:42:08 +00:00
Frank 4ff5783e59 zen: fix chart loading 2025-12-03 22:41:31 -05:00
opencode dcfeb52983 release: v1.0.130
Update Nix Hashes / update (push) Has been cancelled
2025-12-04 03:38:18 +00:00
Jakub Matjanowski 46790e57e9 feat: Enhance DeepSeek reasoning content handling (#4975)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-03 21:31:36 -06:00
Jack Bisceglia 4bc3fa0826 docs: remove outdated theme section as system theme is now added back (#5041) 2025-12-03 21:10:26 -06:00
Aiden Cline 88cfb979be ci: add note about iife 2025-12-03 21:08:12 -06:00
Aiden Cline 32b5db754e fix: provider id issue 2025-12-03 20:45:55 -06:00
Aiden Cline f33f8ca109 fix: compaction type issue 2025-12-03 20:43:47 -06:00
Aiden Cline 598d63db63 fix: dax typo 2025-12-03 20:39:11 -06:00
Github Action 38bff1b372 Update Nix flake.lock and hashes 2025-12-04 02:34:26 +00:00
Aiden Cline e8c9b21f20 bump opentui 2025-12-03 20:33:08 -06:00
Dax 6d3fc63658 core: refactor provider and model system (#5033)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: thdxr <thdxr@users.noreply.github.com>
2025-12-03 21:09:03 -05:00
Dax Raad ee4437ff32 core: add provider test coverage for upcoming refactor
Add comprehensive test suite for Provider module to ensure safe
refactoring of provider internals. Tests cover:
- Provider loading from env vars and config
- Provider filtering (disabled_providers, enabled_providers)
- Model whitelist/blacklist
- Model aliasing and custom providers
- getModel, getProvider, closest, defaultModel functions

Also adds Env module for instance-scoped environment variable access,
enabling isolated test environments without global state pollution.
2025-12-03 18:30:42 -05:00
Frank 7a4aa68706 zen: fix chart loading
closes #5030
2025-12-03 18:12:28 -05:00
Aiden Cline f00380d285 ci: review tweak 2025-12-03 16:16:08 -06:00
Ariane Emory c00d4885c6 feat: add tool_details keybind w/ no default (#4976)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-03 13:38:09 -06:00
Aiden Cline 70f4722356 ci: review ready for review action 2025-12-03 13:35:49 -06:00
Aiden Cline 8898bf7ca4 ci: tweak review cmd 2025-12-03 13:34:45 -06:00
Frank e5b13b767e zen: usage graph respect light/dark mode 2025-12-03 14:24:44 -05:00
Aiden Cline 3181c68cbb ci: make review only fire on non draft pr creation 2025-12-03 13:10:40 -06:00
Aiden Cline c3c9003dbb ci: add pr review 2025-12-03 12:45:06 -06:00
Ariane Emory 921b98066d feat: add messages_last_user command to scroll TUI to last user message (implements #4847) (#4855)
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-03 12:30:11 -06:00
Luke Parker c5b4cc80cc fix: bunfs path on windows (#5011) 2025-12-03 11:21:13 -06:00
Spoon 0bccd1d578 feat: experimental.primary_tools, allow user to set the tools that should only be available to primary agents (#4913)
Co-authored-by: GitHub Action <action@github.com>
2025-12-03 11:19:43 -06:00
Aiden Cline 91db82c138 add retry case for grok resource exhausted 2025-12-03 11:16:27 -06:00
GitHub Action 0eb97086fc chore: format code 2025-12-03 16:47:25 +00:00
Aiden Cline 5b34636afa ignore: docs & style 2025-12-03 10:46:48 -06:00
GitHub Action f1138b9a55 ignore: update download stats 2025-12-03 2025-12-03 12:04:36 +00:00
opencode 23ff6dbba4 release: v1.0.129
Update Nix Hashes / update (push) Has been cancelled
2025-12-03 11:39:04 +00:00
Aiden Cline b457923970 core: fix GitHub Copilot Enterprise authentication failing with sdk.chat undefined error 2025-12-02 23:37:10 -06:00
Jason Cheatham 66e4a5a64e tweak: adjust light/dark theme toggle (#5007) 2025-12-02 23:03:24 -06:00
Ben Vargas 6c25e64658 fix: correct Provider type in chat.params plugin hook (#5003) 2025-12-02 22:50:21 -06:00
Jason Cheatham f2fd0f8f00 fix: handle 0 in ANSI theme color definitions (#5009) 2025-12-02 22:42:30 -06:00
Frank 44cdde5422 zen: fix removing provider 2025-12-02 21:52:12 -05:00
Github Action 88235dc618 Update Nix flake.lock and hashes 2025-12-03 02:17:49 +00:00
Sebastian Herrlinger 4d2b671d7b actually bump opentui to v0.1.55
- fix scrollbox empty/blank last items at bottom
- fix should not insert chars with modifiers in input/textarea anymore
- do not wrap OSC4 palette sequences for tmux 3.6
2025-12-03 03:16:32 +01:00
Sebastian Herrlinger 8098031eac Revert "bump opentui to v0.1.55"
This reverts commit 80636fec43.
2025-12-03 03:14:08 +01:00
Github Action 74c882d9d0 Update Nix flake.lock and hashes 2025-12-03 02:06:35 +00:00
Sebastian Herrlinger 80636fec43 bump opentui to v0.1.55
- fix scrollbox empty/blank last items at bottom
- fix should not insert chars with modifiers in input/textarea anymore
- do not wrap OSC4 palette sequences for tmux 3.6
2025-12-03 03:03:52 +01:00
Aiden Cline a8ad74aef3 add basic session list command 2025-12-02 19:24:05 -06:00
Aiden Cline e2e2b7934e Make homebrew update check use homebrew registry version info 2025-12-02 17:43:33 -06:00
Frank 28c802f399 wip: zen 2025-12-02 18:36:15 -05:00
Dalton Alexandre bcfa63aa4e fix: allow unignoring files in .ignore (#4814) 2025-12-02 17:15:12 -06:00
Jay V d40feafb01 ignore: commands 2025-12-02 18:09:16 -05:00
Jay V 2a8473891b docs: replace deprecated opencode auth login command with /connect across all documentation
Users no longer need to exit the TUI to add providers - they can now use the /connect command directly in the terminal interface. Updated all provider setup instructions to use simplified format with /connect command instead of the deprecated opencode auth login CLI command. Added /connect to TUI commands reference and streamlined provider documentation to show clearer, more concise setup steps.
2025-12-02 18:08:39 -05:00
Aiden Cline a4e3451d5c tweak: make message border match color of agent it was sent to 2025-12-02 16:59:31 -06:00
Aiden Cline 53a7c2885b bump default lsp server timeout 2025-12-02 16:04:08 -06:00
Adam f354507d42 fix: session turn margins 2025-12-02 15:50:24 -06:00
Jaga Santagostino f17e1def32 toggle to hide username in TUI (#4750)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-02 15:11:03 -06:00
GitHub Action 3183e8b7d4 chore: format code 2025-12-02 20:28:17 +00:00
Aiden Cline 9d2b9ef2d4 ci: dont forget our european designer 2025-12-02 14:27:41 -06:00
opencode-agent[bot] 733e5cd876 add OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT (#4996)
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-02 20:14:23 +00:00
opencode 4ee4f7bcb3 release: v1.0.128
Update Nix Hashes / update (push) Has been cancelled
2025-12-02 20:14:22 +00:00
GitHub Action da7ecda9ea chore: format code 2025-12-02 19:52:47 +00:00
Frank 1f11d4fb1a zen: data dumper 2025-12-02 14:51:27 -05:00
Adam b308503ab5 chore: remove comment (redeploy) 2025-12-02 13:49:48 -06:00
Adam 3b10de4a28 fix: vite config 2025-12-02 13:08:59 -06:00
U Cirello 6ce1de476a fix(run): allow messages to start with dash (-) (#4904) 2025-12-02 12:52:05 -06:00
Aiden Cline d9b0848a61 tweak: hide [REDACTED] chunks 2025-12-02 12:29:20 -06:00
Adam 46dd3b8166 chore: update landing page stats 2025-12-02 11:59:37 -06:00
Adam eca07be072 chore: update landing page stats 2025-12-02 11:52:53 -06:00
GitHub Action 165d57b88e chore: format code 2025-12-02 17:07:27 +00:00
David Hill 28c44f7e5a Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-02 17:06:54 +00:00
David Hill 39d5bdff4b fix: add docs button 2025-12-02 17:06:37 +00:00
GitHub Action b9f8480b2f chore: format code 2025-12-02 17:02:39 +00:00
David Hill 58b30d678a Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-12-02 17:00:45 +00:00
David Hill 408cdaf5e0 fix: website hero copy 2025-12-02 17:00:40 +00:00
GitHub Action cae23cde09 chore: format code 2025-12-02 16:45:56 +00:00
Dalton Alexandre 702fb2562c fix: handle ANSI color indexes in theme resolution (#4842) 2025-12-02 10:45:22 -06:00
opencode 785d0b60b6 release: v1.0.127
Update Nix Hashes / update (push) Has been cancelled
2025-12-02 16:05:59 +00:00
Adam 67ab9dc4d0 fix: share page ssr 2025-12-02 07:08:25 -06:00
Github Action 77494cb7df Update Nix flake.lock and hashes 2025-12-02 12:52:28 +00:00
GitHub Action 08f8856d6c chore: format code 2025-12-02 12:51:37 +00:00
Adam 79a4c1544d fix: type error 2025-12-02 06:51:05 -06:00
Adam c0a35141e6 feat: better code and diff rendering performance 2025-12-02 06:50:21 -06:00
GitHub Action 221bb64aeb ignore: update download stats 2025-12-02 2025-12-02 12:04:35 +00:00
David Hill 2555dba188 fix: update the install copy 2025-12-02 11:37:41 +00:00
Aiden Cline 6355ed6ae7 feat: add overridable review slash command (#4973) 2025-12-02 00:18:58 -06:00
Ariane Emory 1864e8c863 feat: toggle tool details visibility (resolves #4824) (#4882)
Co-authored-by: GitHub Action <action@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>
2025-12-01 19:38:44 -06:00
Adam 61d0d66dae fix: install orange 2025-12-01 19:21:55 -06:00
Aiden Cline 86522f1b3e fix: tui crash when no authed providers and default provider disabled (#4964) 2025-12-01 18:35:40 -06:00
Frank dc32705bc9 zen: remove unnecessary transactions 2025-12-01 18:33:32 -05:00
Stephen Collings 1eaf5c31d3 fix(auth): Respect disabled/enabled providers config in auth login (#4940)
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-01 17:01:10 -06:00
Aiden Cline 677b19e22e fix: add .quiet 2025-12-01 16:59:32 -06:00
opencode-agent[bot] 8e248ae045 fix: respect npm registry (#4958)
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-01 16:58:39 -06:00
2298 changed files with 69120 additions and 51670 deletions
-57
View File
@@ -1,57 +0,0 @@
#
# This file is intentionally in the wrong dir, will move and add later....
#
name: Guidelines Check
on:
# Disabled - uncomment to re-enable
# pull_request_target:
# types: [opened, synchronize]
jobs:
check-guidelines:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Install opencode
run: curl -fsSL https://opencode.ai/install | bash
- name: Check PR guidelines compliance
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "gh pr review*": "deny", "*": "deny" } }'
run: |
opencode run -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}'
<pr-number>
${{ github.event.pull_request.number }}
</pr-number>
<pr-description>
${{ github.event.pull_request.body }}
</pr-description>
Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. 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
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.
Command MUST be like this.
```
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments \
-f 'body=[summary of issue]' -f 'commit_id=${{ github.event.pull_request.head.sha }}' -f 'path=[path-to-file]' -F "line=[line]" -f 'side=RIGHT'
```
Only create comments for actual violations. If the code follows all guidelines, don't run any gh commands."
-63
View File
@@ -1,63 +0,0 @@
name: Auto-label TUI Issues
on:
issues:
types: [opened]
jobs:
auto-label:
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
issues: write
steps:
- name: Auto-label and assign issues
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const issue = context.payload.issue;
const title = issue.title;
const description = issue.body || '';
// Check for "opencode web" keyword
const webPattern = /(opencode web)/i;
const isWebRelated = webPattern.test(title) || webPattern.test(description);
// Check for version patterns like v1.0.x or 1.0.x
const versionPattern = /[v]?1\.0\./i;
const isVersionRelated = versionPattern.test(title) || versionPattern.test(description);
// Check for "nix" keyword
const nixPattern = /\bnix\b/i;
const isNixRelated = nixPattern.test(title) || nixPattern.test(description);
const labels = [];
if (isWebRelated) {
labels.push('web');
// Assign to adamdotdevin
await github.rest.issues.addAssignees({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
assignees: ['adamdotdevin']
});
} else if (isVersionRelated) {
// Only add opentui if NOT web-related
labels.push('opentui');
}
if (isNixRelated) {
labels.push('nix');
}
if (labels.length > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: labels
});
}
+69
View File
@@ -0,0 +1,69 @@
name: Docs Update
on:
schedule:
- cron: "0 */12 * * *"
workflow_dispatch:
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="4 hours ago" --pretty=format:"- %h %s" 2>/dev/null || echo "")
if [ -z "$COMMITS" ]; then
echo "No commits in the last 4 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 4 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.
+5
View File
@@ -16,6 +16,8 @@ jobs:
with:
fetch-depth: 1
- uses: ./.github/actions/setup-bun
- name: Install opencode
run: curl -fsSL https://opencode.ai/install | bash
@@ -55,4 +57,7 @@ jobs:
Feel free to ignore if none of these address your specific case.'
Additionally, if the issue mentions keybinds, keyboard shortcuts, or key bindings, please add a comment mentioning the pinned keybinds issue #4997:
'For keybind-related issues, please also check our pinned keybinds documentation: #4997'
If no clear duplicates are found, do not comment."
-29
View File
@@ -1,29 +0,0 @@
name: format
on:
push:
branches-ignore:
- production
pull_request:
branches-ignore:
- production
workflow_dispatch:
jobs:
format:
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Bun
uses: ./.github/actions/setup-bun
- name: run
run: |
./script/format.ts
env:
CI: true
+51
View File
@@ -0,0 +1,51 @@
name: generate
on:
push:
branches:
- dev
workflow_dispatch:
jobs:
generate:
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
ref: ${{ github.event.pull_request.head.ref || github.ref_name }}
- name: Setup Bun
uses: ./.github/actions/setup-bun
- name: Generate
run: ./script/generate.ts
- name: Commit and push
run: |
if [ -z "$(git status --porcelain)" ]; then
echo "No changes to commit"
exit 0
fi
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add -A
git commit -m "chore: generate"
git push origin HEAD:${{ github.ref_name }} --no-verify
# if ! git push origin HEAD:${{ github.event.pull_request.head.ref || github.ref_name }} --no-verify; then
# echo ""
# echo "============================================"
# echo "Failed to push generated code."
# echo "Please run locally and push:"
# echo ""
# echo " ./script/generate.ts"
# echo " git add -A && git commit -m \"chore: generate\" && git push"
# echo ""
# echo "============================================"
# exit 1
# fi
+1 -1
View File
@@ -2,7 +2,7 @@ name: discord
on:
release:
types: [published] # fires only when a release is published
types: [released] # fires when a draft release is published
jobs:
notify:
+2 -1
View File
@@ -29,5 +29,6 @@ jobs:
uses: sst/opencode/github@latest
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
OPENCODE_PERMISSION: '{"bash": "deny"}'
with:
model: opencode/claude-haiku-4-5
model: opencode/claude-opus-4-5
+181 -29
View File
@@ -2,11 +2,15 @@ name: publish
run-name: "${{ format('release {0}', inputs.bump) }}"
on:
push:
branches:
- dev
- snapshot-*
workflow_dispatch:
inputs:
bump:
description: "Bump major, minor, or patch"
required: true
required: false
type: choice
options:
- major
@@ -17,15 +21,17 @@ on:
required: false
type: string
concurrency: ${{ github.workflow }}-${{ github.ref }}
concurrency: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.version || inputs.bump }}
permissions:
id-token: write
contents: write
packages: write
jobs:
publish:
runs-on: blacksmith-4vcpu-ubuntu-2404
if: github.repository == 'sst/opencode'
steps:
- uses: actions/checkout@v3
with:
@@ -33,33 +39,11 @@ jobs:
- run: git fetch --force --tags
- uses: actions/setup-go@v5
with:
go-version: ">=1.24.0"
cache: true
cache-dependency-path: go.sum
- uses: ./.github/actions/setup-bun
- name: Install makepkg
run: |
sudo apt-get update
sudo apt-get install -y pacman-package-manager
- name: Setup SSH for AUR
run: |
mkdir -p ~/.ssh
echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
git config --global user.email "opencode@sst.dev"
git config --global user.name "opencode"
ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts || true
- name: Install OpenCode
run: curl -fsSL https://opencode.ai/install | bash
- name: Setup npm auth
run: |
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
if: inputs.bump || inputs.version
run: bun i -g opencode-ai@1.0.169
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
@@ -68,14 +52,182 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Publish
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- uses: actions/setup-node@v4
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
- name: Setup Git Identity
run: |
./script/publish.ts
git config --global user.email "opencode@sst.dev"
git config --global user.name "opencode"
git remote set-url origin https://x-access-token:${{ secrets.SST_GITHUB_TOKEN }}@github.com/${{ github.repository }}
- name: Publish
id: publish
run: ./script/publish-start.ts
env:
OPENCODE_BUMP: ${{ inputs.bump }}
OPENCODE_VERSION: ${{ inputs.version }}
OPENCODE_CHANNEL: latest
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
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 }}
version: ${{ steps.publish.outputs.version }}
publish-tauri:
needs: publish
continue-on-error: true
strategy:
fail-fast: false
matrix:
settings:
- host: macos-latest
target: x86_64-apple-darwin
- host: macos-latest
target: aarch64-apple-darwin
- host: blacksmith-4vcpu-windows-2025
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
with:
fetch-depth: 0
ref: ${{ needs.publish.outputs.tag }}
- uses: apple-actions/import-codesign-certs@v2
if: ${{ runner.os == 'macOS' }}
with:
keychain: build
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
- name: Verify Certificate
if: ${{ runner.os == 'macOS' }}
run: |
CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application")
CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}')
echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV
echo "Certificate imported."
- name: Setup Apple API Key
if: ${{ runner.os == 'macOS' }}
run: |
echo "${{ secrets.APPLE_API_KEY_PATH }}" > $RUNNER_TEMP/apple-api-key.p8
- run: git fetch --force --tags
- uses: ./.github/actions/setup-bun
- name: install dependencies (ubuntu only)
if: contains(matrix.settings.host, 'ubuntu')
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.settings.target }}
- uses: Swatinem/rust-cache@v2
with:
workspaces: packages/desktop/src-tauri
shared-key: ${{ matrix.settings.target }}
- name: Prepare
run: |
cd packages/desktop
bun ./scripts/prepare.ts
env:
OPENCODE_VERSION: ${{ needs.publish.outputs.version }}
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
AUR_KEY: ${{ secrets.AUR_KEY }}
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
if: contains(matrix.settings.host, 'ubuntu')
run: |
cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force
echo "Installed tauri-cli version:"
cargo tauri --version
- name: Build and upload artifacts
timeout-minutes: 20
uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_BUNDLER_NEW_APPIMAGE_FORMAT: true
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }}
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/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
publish-release:
needs:
- publish
- publish-tauri
if: needs.publish.outputs.tag
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
ref: ${{ needs.publish.outputs.tag }}
- uses: ./.github/actions/setup-bun
- name: Setup SSH for AUR
run: |
sudo apt-get update
sudo apt-get install -y pacman-package-manager
mkdir -p ~/.ssh
echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
git config --global user.email "opencode@sst.dev"
git config --global user.name "opencode"
ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts || true
- run: ./script/publish-complete.ts
env:
OPENCODE_VERSION: ${{ needs.publish.outputs.version }}
AUR_KEY: ${{ secrets.AUR_KEY }}
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
@@ -0,0 +1,29 @@
name: release-github-action
on:
push:
branches:
- dev
paths:
- "github/**"
concurrency: ${{ github.workflow }}-${{ github.ref }}
permissions:
contents: write
jobs:
release:
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: git fetch --force --tags
- name: Release
run: |
git config --global user.email "opencode@sst.dev"
git config --global user.name "opencode"
./github/script/release
+83
View File
@@ -0,0 +1,83 @@
name: Guidelines Check
on:
issue_comment:
types: [created]
jobs:
check-guidelines:
if: |
github.event.issue.pull_request &&
startsWith(github.event.comment.body, '/review') &&
contains(fromJson('["OWNER","MEMBER"]'), github.event.comment.author_association)
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
pull-requests: write
steps:
- name: Get PR number
id: pr-number
run: |
if [ "${{ github.event_name }}" = "pull_request_target" ]; then
echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
else
echo "number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
fi
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: ./.github/actions/setup-bun
- name: Install opencode
run: curl -fsSL https://opencode.ai/install | bash
- name: Get PR details
id: pr-details
run: |
gh api /repos/${{ github.repository }}/pulls/${{ steps.pr-number.outputs.number }} > pr_data.json
echo "title=$(jq -r .title pr_data.json)" >> $GITHUB_OUTPUT
echo "sha=$(jq -r .head.sha pr_data.json)" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check PR guidelines compliance
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "gh pr review*": "deny", "*": "deny" } }'
PR_TITLE: ${{ steps.pr-details.outputs.title }}
run: |
PR_BODY=$(jq -r .body pr_data.json)
opencode run -m anthropic/claude-opus-4-5 "A new pull request has been created: '${PR_TITLE}'
<pr-number>
${{ steps.pr-number.outputs.number }}
</pr-number>
<pr-description>
$PR_BODY
</pr-description>
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 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.
Generally, write a comment instead of writing suggested change if you can help it.
Command MUST be like this.
\`\`\`
gh api \
--method POST \
-H \"Accept: application/vnd.github+json\" \
-H \"X-GitHub-Api-Version: 2022-11-28\" \
/repos/${{ github.repository }}/pulls/${{ steps.pr-number.outputs.number }}/comments \
-f 'body=[summary of issue]' -f 'commit_id=${{ steps.pr-details.outputs.sha }}' -f 'path=[path-to-file]' -F \"line=[line]\" -f 'side=RIGHT'
\`\`\`
Only create comments for actual violations. If the code follows all guidelines, comment on the issue using gh cli: 'lgtm' AND NOTHING ELSE!!!!."
-38
View File
@@ -1,38 +0,0 @@
name: snapshot
on:
workflow_dispatch:
push:
branches:
- dev
- test-bedrock
- v0
- otui-diffs
- snapshot-*
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
publish:
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- run: git fetch --force --tags
- uses: actions/setup-go@v5
with:
go-version: ">=1.24.0"
cache: true
cache-dependency-path: go.sum
- uses: ./.github/actions/setup-bun
- name: Publish
run: |
./script/publish.ts
env:
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
+29
View File
@@ -0,0 +1,29 @@
name: "Auto-close stale issues"
on:
schedule:
- cron: "30 1 * * *" # Daily at 1:30 AM
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/stale@v10
with:
days-before-stale: 90
days-before-close: 7
stale-issue-label: "stale"
close-issue-message: |
[automated] Closing due to 90+ days of inactivity.
Feel free to reopen if you still need this!
stale-issue-message: |
[automated] This issue has had no activity for 90 days.
It will be closed in 7 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 == 'sst/opencode'
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: write
+1 -1
View File
@@ -31,4 +31,4 @@ 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 }}
-6
View File
@@ -28,9 +28,3 @@ jobs:
bun turbo test
env:
CI: true
- name: Check SDK is up to date
run: |
bun ./packages/sdk/js/script/build.ts
git diff --exit-code packages/sdk/js/src/gen packages/sdk/js/dist
continue-on-error: false
+37
View File
@@ -0,0 +1,37 @@
name: Issue Triage
on:
issues:
types: [opened]
jobs:
triage:
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Setup Bun
uses: ./.github/actions/setup-bun
- name: Install opencode
run: curl -fsSL https://opencode.ai/install | bash
- name: Triage issue
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_BODY: ${{ github.event.issue.body }}
run: |
opencode run --agent triage "The following issue was just opened, triage it:
Title: $ISSUE_TITLE
$ISSUE_BODY"
+3 -1
View File
@@ -6,10 +6,10 @@ node_modules
.idea
.vscode
*~
openapi.json
playground
tmp
dist
ts-dist
.turbo
**/.serena
.serena/
@@ -18,3 +18,5 @@ refs
Session.vim
opencode.json
a.out
target
.scripts
+2
View File
@@ -6,6 +6,8 @@ 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
+77
View File
@@ -0,0 +1,77 @@
---
mode: primary
hidden: true
model: opencode/claude-haiku-4-5
tools:
"*": false
"github-triage": true
---
You are a triage agent responsible for triaging github issues.
Use your github-triage tool to triage issues.
## Labels
### windows
Use for any issue that mentions Windows (the OS). Be sure they are saying that they are on Windows.
- Use if they mention WSL too
#### perf
Performance-related issues:
- Slow performance
- High RAM usage
- High CPU usage
**Only** add if it's likely a RAM or CPU issue. **Do not** add for LLM slowness.
#### desktop
Desktop app issues:
- `opencode web` command
- The desktop app itself
**Only** add if it's specifically about the Desktop application or `opencode web` view. **Do not** add for terminal, TUI, or general opencode issues.
#### nix
**Only** add if the issue explicitly mentions nix.
#### 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.
If the issue doesn't have "zen" in it then don't add zen label
#### docs
Add if the issue requests better documentation or docs updates.
#### opentui
TUI issues potentially caused by our underlying TUI library:
- Keybindings not working
- Scroll speed issues (too fast/slow/laggy)
- Screen flickering
- Crashes with opentui in the log
**Do not** add for general TUI bugs.
When assigning to people here are the following rules:
adamdotdev:
ONLY assign adam if the issue will have the "desktop" label.
fwang:
ONLY assign fwang if the issue will have the "zen" label.
jayair:
ONLY assign jayair if the issue will have the "docs" label.
In all other cases use best judgment. Avoid assigning to kommander needlessly, when in doubt assign to rekram1-node.
+3 -1
View File
@@ -1,5 +1,7 @@
---
description: Git commit and push
description: git commit and push
model: opencode/glm-4.6
subtask: true
---
commit and push
-8
View File
@@ -1,8 +0,0 @@
---
description: hello world iaosd ioasjdoiasjd oisadjoisajd osiajd oisaj dosaij dsoajsajdaijdoisa jdoias jdoias jdoia jois jo jdois jdoias jdoias j djoasdj
---
hey there $ARGUMENTS
!`ls`
check out @README.md
+1 -1
View File
@@ -1,5 +1,5 @@
---
description: "Find issue(s) on github"
description: "find issue(s) on github"
model: opencode/claude-haiku-4-5
---
+15
View File
@@ -0,0 +1,15 @@
---
description: Remove AI code slop
---
Check the diff against dev, and remove all AI generated slop introduced in this branch.
This includes:
- Extra comments that a human wouldn't add or is inconsistent with the rest of the file
- Extra defensive checks or try/catch blocks that are abnormal for that area of the codebase (especially if called by trusted / validated codepaths)
- Casts to any to get around type issues
- Any other style that is inconsistent with the file
- Unnecessary emoji usage
Report at the end with only a 1-3 sentence summary of what you changed
+1 -1
View File
@@ -1,5 +1,5 @@
---
description: Spellcheck all markdown file changes
description: spellcheck all markdown file changes
---
Look at all the unstaged changes to markdown (.md, .mdx) files, pull out the lines that have changed, and check for spelling and grammar errors.
+4
View File
@@ -0,0 +1,4 @@
declare module "*.txt" {
const content: string
export default content
}
+9 -19
View File
@@ -1,27 +1,17 @@
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-openai-codex-auth"],
"enterprise": {
"url": "https://enterprise.dev.opencode.ai",
},
// "plugin": ["opencode-openai-codex-auth"],
// "enterprise": {
// "url": "https://enterprise.dev.opencode.ai",
// },
"instructions": ["STYLE_GUIDE.md"],
"provider": {
"opencode": {
"options": {
// "baseURL": "http://localhost:8080",
},
"options": {},
},
},
"mcp": {
"exa": {
"type": "remote",
"url": "https://mcp.exa.ai/mcp",
},
"morph": {
"type": "local",
"command": ["bunx", "@morphllm/morphmcp"],
"environment": {
"ENABLED_TOOLS": "warp_grep",
},
},
"mcp": {},
"tools": {
"github-triage": 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
+90
View File
@@ -0,0 +1,90 @@
/// <reference path="../env.d.ts" />
// import { Octokit } from "@octokit/rest"
import { tool } from "@opencode-ai/plugin"
import DESCRIPTION from "./github-triage.txt"
function getIssueNumber(): number {
const issue = parseInt(process.env.ISSUE_NUMBER ?? "", 10)
if (!issue) throw new Error("ISSUE_NUMBER env var not set")
return issue
}
async function githubFetch(endpoint: string, options: RequestInit = {}) {
const response = await fetch(`https://api.github.com${endpoint}`, {
...options,
headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
Accept: "application/vnd.github+json",
"Content-Type": "application/json",
...options.headers,
},
})
if (!response.ok) {
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`)
}
return response.json()
}
export default tool({
description: DESCRIPTION,
args: {
assignee: tool.schema
.enum(["thdxr", "adamdotdevin", "rekram1-node", "fwang", "jayair", "kommander"])
.describe("The username of the assignee")
.default("rekram1-node"),
labels: tool.schema
.array(tool.schema.enum(["nix", "opentui", "perf", "desktop", "zen", "docs", "windows"]))
.describe("The labels(s) to add to the issue")
.default([]),
},
async execute(args) {
const issue = getIssueNumber()
// const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
const owner = "sst"
const repo = "opencode"
const results: string[] = []
if (args.assignee === "adamdotdevin" && !args.labels.includes("desktop")) {
throw new Error("Only desktop issues should be assigned to adamdotdevin")
}
if (args.assignee === "fwang" && !args.labels.includes("zen")) {
throw new Error("Only zen issues should be assigned to fwang")
}
if (args.assignee === "kommander" && !args.labels.includes("opentui")) {
throw new Error("Only opentui issues should be assigned to kommander")
}
// await octokit.rest.issues.addAssignees({
// owner,
// repo,
// issue_number: issue,
// assignees: [args.assignee],
// })
await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/assignees`, {
method: "POST",
body: JSON.stringify({ assignees: [args.assignee] }),
})
results.push(`Assigned @${args.assignee} to issue #${issue}`)
const labels: string[] = args.labels.map((label) => (label === "desktop" ? "web" : label))
if (labels.length > 0) {
// await octokit.rest.issues.addLabels({
// owner,
// repo,
// issue_number: issue,
// labels,
// })
await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/labels`, {
method: "POST",
body: JSON.stringify({ labels }),
})
results.push(`Added labels: ${args.labels.join(", ")}`)
}
return results.join("\n")
},
})
+88
View File
@@ -0,0 +1,88 @@
Use this tool to assign and/or label a Github issue.
You can assign the following users:
- thdxr
- adamdotdevin
- fwang
- jayair
- kommander
- rekram1-node
You can use the following labels:
- nix
- opentui
- perf
- web
- zen
- docs
Always try to assign an issue, if in doubt, assign rekram1-node to it.
## Breakdown of responsibilities:
### thdxr
Dax is responsible for managing core parts of the application, for large feature requests, api changes, or things that require significant changes to the codebase assign him.
This relates to OpenCode server primarily but has overlap with just about anything
### adamdotdevin
Adam is responsible for managing the Desktop/Web app. If there is an issue relating to the desktop app or `opencode web` command. Assign him.
### fwang
Frank is responsible for managing Zen, if you see complaints about OpenCode Zen, maybe it's the dashboard, the model quality, billing issues, etc. Assign him to the issue.
### jayair
Jay is responsible for documentation. If there is an issue relating to documentation assign him.
### kommander
Sebastian is responsible for managing an OpenTUI (a library for building terminal user interfaces). OpenCode's TUI is built with OpenTUI. If there are issues about:
- random characters on screen
- keybinds not working on different terminals
- general terminal stuff
Then assign the issue to Him.
### rekram1-node
ALL BUGS SHOULD BE assigned to rekram1-node unless they have the `opentui` label.
Assign Aiden to an issue as a catch all, if you can't assign anyone else. Most of the time this will be bugs/polish things.
If no one else makes sense to assign, assign rekram1-node to it.
Always assign to aiden if the issue mentions "acp", "zed", or model performance issues
## Breakdown of Labels:
### nix
Any issue that mentions nix, or nixos should have a nix label
### opentui
Anything relating to the TUI itself should have an opentui label
### perf
Anything related to slow performance, high ram, high cpu usage, or any other performance related issue should have a perf label
### desktop
Anything related to `opencode web` command or the desktop app should have a desktop label. Never add this label for anything terminal/tui related
### zen
Anything related to OpenCode Zen, billing, or model quality from Zen should have a zen label
### docs
Anything related to the documentation should have a docs label
### windows
Use for any issue that involves the windows OS
+1
View File
@@ -0,0 +1 @@
sst-env.d.ts
+1 -41
View File
@@ -1,47 +1,7 @@
## IMPORTANT
- 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()
## 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"
}
}
]
}
}
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE.
+7 -5
View File
@@ -40,7 +40,9 @@ Want to take on an issue? Leave a comment and a maintainer may assign it to you
- `packages/plugin`: Source for `@opencode-ai/plugin`
> [!NOTE]
> After touching `packages/opencode/src/server/server.ts`, run "./packages/sdk/js/script/build.ts" to regenerate the JS sdk.
> 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.
Please try to follow the [style guide](./STYLE_GUIDE.md)
### Setting up a Debugger
@@ -51,12 +53,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:
+21 -5
View File
@@ -7,7 +7,7 @@
</picture>
</a>
</p>
<p align="center">The AI coding agent built for the terminal.</p>
<p align="center">The open source 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>
@@ -30,13 +30,29 @@ scoop bucket add extras; scoop install extras/opencode # Windows
choco install opencode # Windows
brew install opencode # macOS and Linux
paru -S opencode-bin # Arch Linux
mise use --pin -g ubi:sst/opencode # Any OS
mise use -g github:sst/opencode # Any OS
nix run nixpkgs#opencode # or github:sst/opencode for latest dev branch
```
> [!TIP]
> Remove versions older than 0.1.x before installing.
### 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).
| Platform | 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`, or AppImage |
```bash
# macOS (Homebrew)
brew install --cask opencode-desktop
```
#### Installation Directory
The install script respects the following priority order for the installation path:
@@ -63,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).
@@ -78,11 +94,11 @@ If you're interested in contributing to OpenCode, please read our [contributing
### Building on OpenCode
If you are working on a project that's related to OpenCode and is using "opencode" as a part of its name; for example, "opencode-dashboard" or "opencode-mobile", please add a note to your README to clarify that it is not built by the OpenCode team and is not affiliated with us in anyway.
If you are working on a project that's related to OpenCode and is using "opencode" as a part of its name; for example, "opencode-dashboard" or "opencode-mobile", please add a note to your README to clarify that it is not built by the OpenCode team and is not affiliated with us in any way.
### 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:
+115
View File
@@ -0,0 +1,115 @@
<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/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>
</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 opencode # macOS 與 Linux
paru -S opencode-bin # Arch Linux
mise use -g github:sst/opencode # 任何作業系統
nix run nixpkgs#opencode # 或使用 github:sst/opencode 以取得最新開發分支
```
> [!TIP]
> 安裝前請先移除 0.1.x 以前的舊版本。
### 桌面應用程式 (BETA)
OpenCode 也提供桌面版應用程式。您可以直接從 [發佈頁面 (releases page)](https://github.com/sst/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** - 預設模式,具備完整權限的 Agent,適用於開發工作。
- **plan** - 唯讀模式,適用於程式碼分析與探索。
- 預設禁止修改檔案。
- 執行 bash 指令前會詢問權限。
- 非常適合用來探索陌生的程式碼庫或規劃變更。
此外,OpenCode 還包含一個 **general** 子 Agent,用於處理複雜搜尋與多步驟任務。此 Agent 供系統內部使用,亦可透過在訊息中輸入 `@general` 來呼叫。
了解更多關於 [Agents](https://opencode.ai/docs/agents) 的資訊。
### 線上文件
關於如何設定 OpenCode 的詳細資訊,請參閱我們的 [**官方文件**](https://opencode.ai/docs)。
### 參與貢獻
如果您有興趣參與 OpenCode 的開發,請在提交 Pull Request 前先閱讀我們的 [貢獻指南 (Contributing Docs)](./CONTRIBUTING.md)。
### 基於 OpenCode 進行開發
如果您正在開發與 OpenCode 相關的專案,並在名稱中使用了 "opencode"(例如 "opencode-dashboard" 或 "opencode-mobile"),請在您的 README 中加入聲明,說明該專案並非由 OpenCode 團隊開發,且與我們沒有任何隸屬關係。
### 常見問題 (FAQ)
#### 這跟 Claude Code 有什麼不同?
在功能面上與 Claude Code 非常相似。以下是關鍵差異:
- 100% 開源。
- 不綁定特定的服務提供商。雖然我們推薦使用透過 [OpenCode Zen](https://opencode.ai/zen) 提供的模型,但 OpenCode 也可搭配 Claude, OpenAI, Google 甚至本地模型使用。隨著模型不斷演進,彼此間的差距會縮小且價格會下降,因此具備「不限廠商 (provider-agnostic)」的特性至關重要。
- 內建 LSP (語言伺服器協定) 支援。
- 專注於終端機介面 (TUI)。OpenCode 由 Neovim 愛好者與 [terminal.shop](https://terminal.shop) 的創作者打造;我們將不斷挑戰終端機介面的極限。
- 客戶端/伺服器架構 (Client/Server Architecture)。這讓 OpenCode 能夠在您的電腦上運行的同時,由行動裝置進行遠端操控。這意味著 TUI 前端只是眾多可能的客戶端之一。
#### 另一個同名的 Repo 是什麼?
另一個名稱相近的儲存庫與本專案無關。您可以點此[閱讀背後的故事](https://x.com/thdxr/status/1933561254481666466)。
---
**加入我們的社群** [Discord](https://discord.gg/opencode) | [X.com](https://x.com/opencode)
+184 -157
View File
@@ -1,159 +1,186 @@
# 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) |
| 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) |
+12
View File
@@ -0,0 +1,12 @@
## 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()
+609 -311
View File
File diff suppressed because it is too large Load Diff
Generated
+3 -3
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1764587062,
"narHash": "sha256-hdFa0TAVQAQLDF31cEW3enWmBP+b592OvHs6WVe3D8k=",
"lastModified": 1766840161,
"narHash": "sha256-Ss/LHpJJsng8vz1Pe33RSGIWUOcqM1fjrehjUkdrWio=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c1cb7d097cb250f6e1904aacd5f2ba5ffd8a49ce",
"rev": "3edc4a30ed3903fdf6f90c837f961fa6b49582d1",
"type": "github"
},
"original": {
+2 -2
View File
@@ -6,7 +6,7 @@ Mention `/opencode` in your comment, and opencode will execute tasks within your
## Features
#### Explain an issues
#### Explain an issue
Leave the following comment on a GitHub issue. `opencode` will read the entire thread, including all comments, and reply with a clear explanation.
@@ -14,7 +14,7 @@ Leave the following comment on a GitHub issue. `opencode` will read the entire t
/opencode explain this issue
```
#### Fix an issues
#### Fix an issue
Leave the following comment on a GitHub issue. opencode will create a new branch, implement the changes, and open a PR with the changes.
+45
View File
@@ -9,21 +9,66 @@ inputs:
description: "Model to use"
required: true
agent:
description: "Agent to use. Must be a primary agent. Falls back to default_agent from config or 'build' if not found."
required: false
share:
description: "Share the opencode session (defaults to true for public repos)"
required: false
prompt:
description: "Custom prompt to override the default prompt"
required: false
use_github_token:
description: "Use GITHUB_TOKEN directly instead of OpenCode App token exchange. When true, skips OIDC and uses the GITHUB_TOKEN env var."
required: false
default: "false"
mentions:
description: "Comma-separated list of trigger phrases (case-insensitive). Defaults to '/opencode,/oc'"
required: false
oidc_base_url:
description: "Base URL for OIDC token exchange API. Only required when running a custom GitHub App install. Defaults to https://api.opencode.ai"
required: false
runs:
using: "composite"
steps:
- name: Get opencode version
id: version
shell: bash
run: |
VERSION=$(curl -sf https://api.github.com/repos/sst/opencode/releases/latest | grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4)
echo "version=${VERSION:-latest}" >> $GITHUB_OUTPUT
- name: Cache opencode
id: cache
uses: actions/cache@v4
with:
path: ~/.opencode/bin
key: opencode-${{ runner.os }}-${{ runner.arch }}-${{ steps.version.outputs.version }}
- name: Install opencode
if: steps.cache.outputs.cache-hit != 'true'
shell: bash
run: curl -fsSL https://opencode.ai/install | bash
- name: Add opencode to PATH
shell: bash
run: echo "$HOME/.opencode/bin" >> $GITHUB_PATH
- name: Run opencode
shell: bash
id: run_opencode
run: opencode github run
env:
MODEL: ${{ inputs.model }}
AGENT: ${{ inputs.agent }}
SHARE: ${{ inputs.share }}
PROMPT: ${{ inputs.prompt }}
USE_GITHUB_TOKEN: ${{ inputs.use_github_token }}
MENTIONS: ${{ inputs.mentions }}
OIDC_BASE_URL: ${{ inputs.oidc_base_url }}
+31 -2
View File
@@ -318,6 +318,10 @@ function useEnvRunUrl() {
return `/${repo.owner}/${repo.repo}/actions/runs/${runId}`
}
function useEnvAgent() {
return process.env["AGENT"] || undefined
}
function useEnvShare() {
const value = process.env["SHARE"]
if (!value) return undefined
@@ -570,24 +574,49 @@ async function subscribeSessionEvents() {
}
async function summarize(response: string) {
const payload = useContext().payload as IssueCommentEvent
try {
return await chat(`Summarize the following in less than 40 characters:\n\n${response}`)
} catch (e) {
if (isScheduleEvent()) {
return "Scheduled task changes"
}
const payload = useContext().payload as IssueCommentEvent
return `Fix issue: ${payload.issue.title}`
}
}
async function resolveAgent(): Promise<string | undefined> {
const envAgent = useEnvAgent()
if (!envAgent) return undefined
// Validate the agent exists and is a primary agent
const agents = await client.agent.list<true>()
const agent = agents.data?.find((a) => a.name === envAgent)
if (!agent) {
console.warn(`agent "${envAgent}" not found. Falling back to default agent`)
return undefined
}
if (agent.mode === "subagent") {
console.warn(`agent "${envAgent}" is a subagent, not a primary agent. Falling back to default agent`)
return undefined
}
return envAgent
}
async function chat(text: string, files: PromptFiles = []) {
console.log("Sending message to opencode...")
const { providerID, modelID } = useEnvModel()
const agent = await resolveAgent()
const chat = await client.session.chat<true>({
path: session,
body: {
providerID,
modelID,
agent: "build",
agent,
parts: [
{
type: "text",
+1 -1
View File
@@ -13,7 +13,7 @@
"@actions/core": "1.11.1",
"@actions/github": "6.0.1",
"@octokit/graphql": "9.0.1",
"@octokit/rest": "22.0.0",
"@octokit/rest": "catalog:",
"@opencode-ai/sdk": "workspace:*"
}
}
+1 -1
View File
@@ -6,4 +6,4 @@
/// <reference path="../sst-env.d.ts" />
import "sst"
export {}
export {}
+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",
},
})
+5 -1
View File
@@ -102,6 +102,8 @@ const ZEN_MODELS = [
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"),
]
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
@@ -116,7 +118,8 @@ const gatewayKv = new sst.cloudflare.Kv("GatewayKv")
// CONSOLE
////////////////
const bucket = new sst.cloudflare.Bucket("ConsoleData")
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")
@@ -135,6 +138,7 @@ new sst.cloudflare.x.SolidStart("Console", {
path: "packages/console/app",
link: [
bucket,
bucketNew,
database,
AUTH_API_URL,
STRIPE_WEBHOOK_SECRET,
-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",
},
})
+3 -3
View File
@@ -1,10 +1,10 @@
import { SECRET } from "./secret"
import { domain } from "./stage"
import { domain, shortDomain } from "./stage"
const storage = new sst.cloudflare.Bucket("EnterpriseStorage")
const enterprise = new sst.cloudflare.x.SolidStart("Enterprise", {
domain: "enterprise." + domain,
const teams = new sst.cloudflare.x.SolidStart("Teams", {
domain: shortDomain,
path: "packages/enterprise",
buildCommand: "bun run build:cloudflare",
environment: {
+6
View File
@@ -11,3 +11,9 @@ new cloudflare.RegionalHostname("RegionalHostname", {
regionKey: "us",
zoneId: zoneID,
})
export const shortDomain = (() => {
if ($app.stage === "production") return "opncd.ai"
if ($app.stage === "dev") return "dev.opncd.ai"
return `${$app.stage}.dev.opncd.ai`
})()
+103 -48
View File
@@ -4,10 +4,54 @@ APP=opencode
MUTED='\033[0;2m'
RED='\033[0;31m'
ORANGE='\033[38;2;255;140;0m'
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)
--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
EOF
}
requested_version=${VERSION:-}
no_modify_path=false
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
;;
--no-modify-path)
no_modify_path=true
shift
;;
*)
echo -e "${ORANGE}Warning: Unknown option '$1'${NC}" >&2
shift
;;
esac
done
raw_os=$(uname -s)
os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]')
@@ -111,8 +155,18 @@ if [ -z "$requested_version" ]; then
exit 1
fi
else
# Strip leading 'v' if present
requested_version="${requested_version#v}"
url="https://github.com/sst/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/sst/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/sst/opencode/releases${NC}"
exit 1
fi
fi
print_message() {
@@ -240,22 +294,23 @@ download_with_progress() {
download_and_install() {
print_message info "\n${MUTED}Installing ${NC}opencode ${MUTED}version: ${NC}$specific_version"
mkdir -p opencodetmp && cd opencodetmp
local tmp_dir="${TMPDIR:-/tmp}/opencode_install_$$"
mkdir -p "$tmp_dir"
if [[ "$os" == "windows" ]] || ! download_with_progress "$url" "$filename"; then
# Fallback to standard curl on Windows or if custom progress fails
curl -# -L -o "$filename" "$url"
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"
fi
if [ "$os" = "linux" ]; then
tar -xzf "$filename"
tar -xzf "$tmp_dir/$filename" -C "$tmp_dir"
else
unzip -q "$filename"
unzip -q "$tmp_dir/$filename" -d "$tmp_dir"
fi
mv opencode "$INSTALL_DIR"
mv "$tmp_dir/opencode" "$INSTALL_DIR"
chmod 755 "${INSTALL_DIR}/opencode"
cd .. && rm -rf opencodetmp
rm -rf "$tmp_dir"
}
check_version
@@ -303,42 +358,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
@@ -353,10 +408,10 @@ echo -e "${MUTED}█░░█ █░░█ █▀▀▀ █░░█ ${NC}█░
echo -e "${MUTED}▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ${NC}▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀"
echo -e ""
echo -e ""
echo -e "${MUTED}To get started, navigate to a project and run:${NC}"
echo -e "opencode ${MUTED}Use free models${NC}"
echo -e "opencode auth login ${MUTED}Add paid provider API keys${NC}"
echo -e "opencode help ${MUTED}List commands and options${NC}"
echo -e "${MUTED}OpenCode includes free models, to start:${NC}"
echo -e ""
echo -e "cd <project> ${MUTED}# Open directory${NC}"
echo -e "opencode ${MUTED}# Run command${NC}"
echo -e ""
echo -e "${MUTED}For more information visit ${NC}https://opencode.ai/docs"
echo -e ""
+1 -1
View File
@@ -1,3 +1,3 @@
{
"nodeModules": "sha256-9BfJ3dFq/UYyhsnK3Sfx6rb6CT8bCvFOFOqD2+W1WQE="
"nodeModules": "sha256-lloUZt5mLyNWAcbQrJB4wGUKvKu24WFEhOLfZD5/FMg="
}
+2 -2
View File
@@ -1,4 +1,4 @@
{ lib, stdenvNoCC, bun, fzf, ripgrep, makeBinaryWrapper }:
{ lib, stdenvNoCC, bun, ripgrep, makeBinaryWrapper }:
args:
let
scripts = args.scripts;
@@ -97,7 +97,7 @@ stdenvNoCC.mkDerivation (finalAttrs: {
makeWrapper ${bun}/bin/bun $out/bin/opencode \
--add-flags "run" \
--add-flags "$out/lib/opencode/dist/src/index.js" \
--prefix PATH : ${lib.makeBinPath [ fzf ripgrep ]} \
--prefix PATH : ${lib.makeBinPath [ ripgrep ]} \
--argv0 opencode
runHook postInstall
+16 -7
View File
@@ -4,7 +4,7 @@
"description": "AI-powered development tool",
"private": true,
"type": "module",
"packageManager": "bun@1.3.3",
"packageManager": "bun@1.3.5",
"scripts": {
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
"typecheck": "bun turbo typecheck",
@@ -20,7 +20,8 @@
"packages/slack"
],
"catalog": {
"@types/bun": "1.3.3",
"@types/bun": "1.3.4",
"@octokit/rest": "22.0.0",
"@hono/zod-validator": "0.4.2",
"ulid": "3.0.1",
"@kobalte/core": "0.13.11",
@@ -30,18 +31,22 @@
"@tsconfig/bun": "1.0.9",
"@cloudflare/workers-types": "4.20251008.0",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@pierre/precision-diffs": "0.5.7",
"@pierre/diffs": "1.0.2",
"@solid-primitives/storage": "4.3.3",
"@tailwindcss/vite": "4.1.11",
"diff": "8.0.2",
"ai": "5.0.97",
"hono": "4.7.10",
"hono-openapi": "1.1.1",
"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.20251014.1",
"@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",
@@ -54,6 +59,7 @@
}
},
"devDependencies": {
"@actions/artifact": "5.0.1",
"@tsconfig/bun": "catalog:",
"husky": "9.1.7",
"prettier": "3.6.2",
@@ -62,6 +68,7 @@
},
"dependencies": {
"@aws-sdk/client-s3": "3.933.0",
"@opencode-ai/plugin": "workspace:*",
"@opencode-ai/script": "workspace:*",
"@opencode-ai/sdk": "workspace:*",
"typescript": "catalog:"
@@ -78,7 +85,6 @@
"trustedDependencies": [
"esbuild",
"protobufjs",
"sharp",
"tree-sitter",
"tree-sitter-bash",
"web-tree-sitter"
@@ -86,5 +92,8 @@
"overrides": {
"@types/bun": "catalog:",
"@types/node": "catalog:"
},
"patchedDependencies": {
"ghostty-web@0.3.0": "patches/ghostty-web@0.3.0.patch"
}
}
+1
View File
@@ -0,0 +1 @@
src/assets/theme.css
+34
View File
@@ -0,0 +1,34 @@
## Usage
Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`.
This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be 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.)
+2
View File
@@ -0,0 +1,2 @@
[test]
preload = ["./happydom.ts"]
+75
View File
@@ -0,0 +1,75 @@
import { GlobalRegistrator } from "@happy-dom/global-registrator"
GlobalRegistrator.register()
const originalGetContext = HTMLCanvasElement.prototype.getContext
// @ts-expect-error - we're overriding with a simplified mock
HTMLCanvasElement.prototype.getContext = function (contextType: string, _options?: unknown) {
if (contextType === "2d") {
return {
canvas: this,
fillStyle: "#000000",
strokeStyle: "#000000",
font: "12px monospace",
textAlign: "start",
textBaseline: "alphabetic",
globalAlpha: 1,
globalCompositeOperation: "source-over",
imageSmoothingEnabled: true,
lineWidth: 1,
lineCap: "butt",
lineJoin: "miter",
miterLimit: 10,
shadowBlur: 0,
shadowColor: "rgba(0, 0, 0, 0)",
shadowOffsetX: 0,
shadowOffsetY: 0,
fillRect: () => {},
strokeRect: () => {},
clearRect: () => {},
fillText: () => {},
strokeText: () => {},
measureText: (text: string) => ({ width: text.length * 8 }),
drawImage: () => {},
save: () => {},
restore: () => {},
scale: () => {},
rotate: () => {},
translate: () => {},
transform: () => {},
setTransform: () => {},
resetTransform: () => {},
createLinearGradient: () => ({ addColorStop: () => {} }),
createRadialGradient: () => ({ addColorStop: () => {} }),
createPattern: () => null,
beginPath: () => {},
closePath: () => {},
moveTo: () => {},
lineTo: () => {},
bezierCurveTo: () => {},
quadraticCurveTo: () => {},
arc: () => {},
arcTo: () => {},
ellipse: () => {},
rect: () => {},
fill: () => {},
stroke: () => {},
clip: () => {},
isPointInPath: () => false,
isPointInStroke: () => false,
getTransform: () => ({}),
getImageData: () => ({
data: new Uint8ClampedArray(0),
width: 0,
height: 0,
}),
putImageData: () => {},
createImageData: () => ({
data: new Uint8ClampedArray(0),
width: 0,
height: 0,
}),
} as unknown as CanvasRenderingContext2D
}
return originalGetContext.call(this, contextType as "2d", _options)
}
+53
View File
@@ -0,0 +1,53 @@
<!doctype html>
<html lang="en" style="background-color: var(--background-base)">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>OpenCode</title>
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
<meta name="theme-color" content="#F8F7F7" />
<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">
;(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)
}
})()
</script>
</head>
<body class="antialiased overscroll-none text-12-regular overflow-hidden">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" class="flex flex-col h-screen"></div>
<script src="/src/entry.tsx" type="module"></script>
</body>
</html>
+62
View File
@@ -0,0 +1,62 @@
{
"name": "@opencode-ai/app",
"version": "1.0.205",
"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
+1
View File
@@ -0,0 +1 @@
../../ui/src/assets/images/social-share-zen.png
+1
View File
@@ -0,0 +1 @@
../../ui/src/assets/images/social-share.png
+272
View File
@@ -0,0 +1,272 @@
import { describe, test, expect, beforeAll, afterEach } from "bun:test"
import { Terminal, Ghostty } from "ghostty-web"
import { SerializeAddon } from "./serialize"
let ghostty: Ghostty
beforeAll(async () => {
ghostty = await Ghostty.load()
})
const terminals: Terminal[] = []
afterEach(() => {
for (const term of terminals) {
term.dispose()
}
terminals.length = 0
document.body.innerHTML = ""
})
function createTerminal(cols = 80, rows = 24): { term: Terminal; addon: SerializeAddon; container: HTMLElement } {
const container = document.createElement("div")
document.body.appendChild(container)
const term = new Terminal({ cols, rows, ghostty })
const addon = new SerializeAddon()
term.loadAddon(addon)
term.open(container)
terminals.push(term)
return { term, addon, container }
}
function writeAndWait(term: Terminal, data: string): Promise<void> {
return new Promise((resolve) => {
term.write(data, resolve)
})
}
describe("SerializeAddon", () => {
describe("ANSI color preservation", () => {
test("should preserve text attributes (bold, italic, underline)", async () => {
const { term, addon } = createTerminal()
const input = "\x1b[1mBOLD\x1b[0m \x1b[3mITALIC\x1b[0m \x1b[4mUNDER\x1b[0m"
await writeAndWait(term, input)
const origLine = term.buffer.active.getLine(0)
expect(origLine!.getCell(0)!.isBold()).toBe(1)
expect(origLine!.getCell(5)!.isItalic()).toBe(1)
expect(origLine!.getCell(12)!.isUnderline()).toBe(1)
const serialized = addon.serialize({ range: { start: 0, end: 0 } })
const { term: term2 } = createTerminal()
terminals.push(term2)
await writeAndWait(term2, serialized)
const line = term2.buffer.active.getLine(0)
const boldCell = line!.getCell(0)
expect(boldCell!.getChars()).toBe("B")
expect(boldCell!.isBold()).toBe(1)
const italicCell = line!.getCell(5)
expect(italicCell!.getChars()).toBe("I")
expect(italicCell!.isItalic()).toBe(1)
const underCell = line!.getCell(12)
expect(underCell!.getChars()).toBe("U")
expect(underCell!.isUnderline()).toBe(1)
})
test("should preserve basic 16-color foreground colors", async () => {
const { term, addon } = createTerminal()
const input = "\x1b[31mRED\x1b[32mGREEN\x1b[34mBLUE\x1b[0mNORMAL"
await writeAndWait(term, input)
const origLine = term.buffer.active.getLine(0)
const origRedFg = origLine!.getCell(0)!.getFgColor()
const origGreenFg = origLine!.getCell(3)!.getFgColor()
const origBlueFg = origLine!.getCell(8)!.getFgColor()
const serialized = addon.serialize({ range: { start: 0, end: 0 } })
const { term: term2 } = createTerminal()
terminals.push(term2)
await writeAndWait(term2, serialized)
const line = term2.buffer.active.getLine(0)
expect(line).toBeDefined()
const redCell = line!.getCell(0)
expect(redCell!.getChars()).toBe("R")
expect(redCell!.getFgColor()).toBe(origRedFg)
const greenCell = line!.getCell(3)
expect(greenCell!.getChars()).toBe("G")
expect(greenCell!.getFgColor()).toBe(origGreenFg)
const blueCell = line!.getCell(8)
expect(blueCell!.getChars()).toBe("B")
expect(blueCell!.getFgColor()).toBe(origBlueFg)
})
test("should preserve 256-color palette colors", async () => {
const { term, addon } = createTerminal()
const input = "\x1b[38;5;196mRED256\x1b[0mNORMAL"
await writeAndWait(term, input)
const origLine = term.buffer.active.getLine(0)
const origRedFg = origLine!.getCell(0)!.getFgColor()
const serialized = addon.serialize({ range: { start: 0, end: 0 } })
const { term: term2 } = createTerminal()
terminals.push(term2)
await writeAndWait(term2, serialized)
const line = term2.buffer.active.getLine(0)
const redCell = line!.getCell(0)
expect(redCell!.getChars()).toBe("R")
expect(redCell!.getFgColor()).toBe(origRedFg)
})
test("should preserve RGB/truecolor colors", async () => {
const { term, addon } = createTerminal()
const input = "\x1b[38;2;255;128;64mRGB_TEXT\x1b[0mNORMAL"
await writeAndWait(term, input)
const origLine = term.buffer.active.getLine(0)
const origRgbFg = origLine!.getCell(0)!.getFgColor()
const serialized = addon.serialize({ range: { start: 0, end: 0 } })
const { term: term2 } = createTerminal()
terminals.push(term2)
await writeAndWait(term2, serialized)
const line = term2.buffer.active.getLine(0)
const rgbCell = line!.getCell(0)
expect(rgbCell!.getChars()).toBe("R")
expect(rgbCell!.getFgColor()).toBe(origRgbFg)
})
test("should preserve background colors", async () => {
const { term, addon } = createTerminal()
const input = "\x1b[48;2;255;0;0mRED_BG\x1b[48;2;0;255;0mGREEN_BG\x1b[0mNORMAL"
await writeAndWait(term, input)
const origLine = term.buffer.active.getLine(0)
const origRedBg = origLine!.getCell(0)!.getBgColor()
const origGreenBg = origLine!.getCell(6)!.getBgColor()
const serialized = addon.serialize({ range: { start: 0, end: 0 } })
const { term: term2 } = createTerminal()
terminals.push(term2)
await writeAndWait(term2, serialized)
const line = term2.buffer.active.getLine(0)
const redBgCell = line!.getCell(0)
expect(redBgCell!.getChars()).toBe("R")
expect(redBgCell!.getBgColor()).toBe(origRedBg)
const greenBgCell = line!.getCell(6)
expect(greenBgCell!.getChars()).toBe("G")
expect(greenBgCell!.getBgColor()).toBe(origGreenBg)
})
test("should handle combined colors and attributes", async () => {
const { term, addon } = createTerminal()
const input =
"\x1b[1;38;2;255;0;0;48;2;255;255;0mCOMBO\x1b[0mNORMAL "
await writeAndWait(term, input)
const origLine = term.buffer.active.getLine(0)
const origFg = origLine!.getCell(0)!.getFgColor()
const origBg = origLine!.getCell(0)!.getBgColor()
expect(origLine!.getCell(0)!.isBold()).toBe(1)
const serialized = addon.serialize({ range: { start: 0, end: 0 } })
const cleanSerialized = serialized.replace(/\x1b\[\d+X/g, "")
expect(cleanSerialized.startsWith("\x1b[1;")).toBe(true)
const { term: term2 } = createTerminal()
terminals.push(term2)
await writeAndWait(term2, cleanSerialized)
const line = term2.buffer.active.getLine(0)
const comboCell = line!.getCell(0)
expect(comboCell!.getChars()).toBe("C")
expect(cleanSerialized).toContain("\x1b[1;38;2;255;0;0;48;2;255;255;0m")
})
})
describe("round-trip serialization", () => {
test("should not produce ECH sequences", async () => {
const { term, addon } = createTerminal()
await writeAndWait(term, "\x1b[31mHello\x1b[0m World")
const serialized = addon.serialize()
const hasECH = /\x1b\[\d+X/.test(serialized)
expect(hasECH).toBe(false)
})
test("multi-line content should not have garbage characters", 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()
expect(/\x1b\[\d+X/.test(serialized)).toBe(false)
const { term: term2 } = createTerminal()
terminals.push(term2)
await writeAndWait(term2, serialized)
for (let row = 0; row < 3; row++) {
const line = term2.buffer.active.getLine(row)?.translateToString(true)
expect(line?.includes("𑼝")).toBe(false)
}
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("serialized output written to new terminal should match original colors", async () => {
const { term, addon } = createTerminal(40, 5)
const input = "\x1b[38;2;255;0;0mHello\x1b[0m \x1b[38;2;0;255;0mWorld\x1b[0m! "
await writeAndWait(term, input)
const origLine = term.buffer.active.getLine(0)
const origHelloFg = origLine!.getCell(0)!.getFgColor()
const origWorldFg = origLine!.getCell(6)!.getFgColor()
const serialized = addon.serialize({ range: { start: 0, end: 0 } })
const { term: term2 } = createTerminal(40, 5)
terminals.push(term2)
await writeAndWait(term2, serialized)
const newLine = term2.buffer.active.getLine(0)
expect(newLine!.getCell(0)!.getChars()).toBe("H")
expect(newLine!.getCell(0)!.getFgColor()).toBe(origHelloFg)
expect(newLine!.getCell(6)!.getChars()).toBe("W")
expect(newLine!.getCell(6)!.getFgColor()).toBe(origWorldFg)
expect(newLine!.getCell(11)!.getChars()).toBe("!")
})
})
})
+595
View File
@@ -0,0 +1,595 @@
/**
* SerializeAddon - Serialize terminal buffer contents
*
* Port of xterm.js addon-serialize for ghostty-web.
* Enables serialization of terminal contents to a string that can
* be written back to restore terminal state.
*
* Usage:
* ```typescript
* const serializeAddon = new SerializeAddon();
* term.loadAddon(serializeAddon);
* const content = serializeAddon.serialize();
* ```
*/
import type { ITerminalAddon, ITerminalCore, IBufferRange } from "ghostty-web"
// ============================================================================
// Buffer Types (matching ghostty-web internal interfaces)
// ============================================================================
interface IBuffer {
readonly type: "normal" | "alternate"
readonly cursorX: number
readonly cursorY: number
readonly viewportY: number
readonly baseY: number
readonly length: number
getLine(y: number): IBufferLine | undefined
getNullCell(): IBufferCell
}
interface IBufferLine {
readonly length: number
readonly isWrapped: boolean
getCell(x: number): IBufferCell | undefined
translateToString(trimRight?: boolean, startColumn?: number, endColumn?: number): string
}
interface IBufferCell {
getChars(): string
getCode(): number
getWidth(): number
getFgColorMode(): number
getBgColorMode(): number
getFgColor(): number
getBgColor(): number
isBold(): number
isItalic(): number
isUnderline(): number
isStrikethrough(): number
isBlink(): number
isInverse(): number
isInvisible(): number
isFaint(): number
isDim(): boolean
}
// ============================================================================
// Types
// ============================================================================
export interface ISerializeOptions {
/**
* The row range to serialize. When an explicit range is specified, the cursor
* will get its final repositioning.
*/
range?: ISerializeRange
/**
* The number of rows in the scrollback buffer to serialize, starting from
* the bottom of the scrollback buffer. When not specified, all available
* rows in the scrollback buffer will be serialized.
*/
scrollback?: number
/**
* Whether to exclude the terminal modes from the serialization.
* Default: false
*/
excludeModes?: boolean
/**
* Whether to exclude the alt buffer from the serialization.
* Default: false
*/
excludeAltBuffer?: boolean
}
export interface ISerializeRange {
/**
* The line to start serializing (inclusive).
*/
start: number
/**
* The line to end serializing (inclusive).
*/
end: number
}
export interface IHTMLSerializeOptions {
/**
* The number of rows in the scrollback buffer to serialize, starting from
* the bottom of the scrollback buffer.
*/
scrollback?: number
/**
* Whether to only serialize the selection.
* Default: false
*/
onlySelection?: boolean
/**
* Whether to include the global background of the terminal.
* Default: false
*/
includeGlobalBackground?: boolean
/**
* The range to serialize. This is prioritized over onlySelection.
*/
range?: {
startLine: number
endLine: number
startCol: number
}
}
// ============================================================================
// Helper Functions
// ============================================================================
function constrain(value: number, low: number, high: number): number {
return Math.max(low, Math.min(value, high))
}
function equalFg(cell1: IBufferCell, cell2: IBufferCell): boolean {
return cell1.getFgColorMode() === cell2.getFgColorMode() && cell1.getFgColor() === cell2.getFgColor()
}
function equalBg(cell1: IBufferCell, cell2: IBufferCell): boolean {
return cell1.getBgColorMode() === cell2.getBgColorMode() && cell1.getBgColor() === cell2.getBgColor()
}
function equalFlags(cell1: IBufferCell, cell2: IBufferCell): boolean {
return (
!!cell1.isInverse() === !!cell2.isInverse() &&
!!cell1.isBold() === !!cell2.isBold() &&
!!cell1.isUnderline() === !!cell2.isUnderline() &&
!!cell1.isBlink() === !!cell2.isBlink() &&
!!cell1.isInvisible() === !!cell2.isInvisible() &&
!!cell1.isItalic() === !!cell2.isItalic() &&
!!cell1.isDim() === !!cell2.isDim() &&
!!cell1.isStrikethrough() === !!cell2.isStrikethrough()
)
}
// ============================================================================
// Base Serialize Handler
// ============================================================================
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()
const startRow = range.start.y
const endRow = range.end.y
const startColumn = range.start.x
const endColumn = range.end.x
this._beforeSerialize(endRow - startRow, 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)
for (let col = startLineColumn; col < endLineColumn; col++) {
const c = line.getCell(col)
if (!c) {
continue
}
this._nextCell(c, oldCell, row, col)
oldCell = c
}
}
this._rowEnd(row, row === endRow)
}
this._afterSerialize()
return this._serializeString(excludeFinalCursorPosition)
}
protected _nextCell(_cell: IBufferCell, _oldCell: IBufferCell, _row: number, _col: number): void {}
protected _rowEnd(_row: number, _isLastRow: boolean): void {}
protected _beforeSerialize(_rows: number, _startRow: number, _endRow: number): void {}
protected _afterSerialize(): void {}
protected _serializeString(_excludeFinalCursorPosition?: boolean): string {
return ""
}
}
// ============================================================================
// String Serialize Handler
// ============================================================================
class StringSerializeHandler extends BaseSerializeHandler {
private _rowIndex: number = 0
private _allRows: string[] = []
private _allRowSeparators: string[] = []
private _currentRow: string = ""
private _nullCellCount: number = 0
private _cursorStyle: IBufferCell
private _firstRow: number = 0
private _lastCursorRow: number = 0
private _lastCursorCol: number = 0
private _lastContentCursorRow: number = 0
private _lastContentCursorCol: number = 0
constructor(
buffer: IBuffer,
private readonly _terminal: ITerminalCore,
) {
super(buffer)
this._cursorStyle = this._buffer.getNullCell()
}
protected _beforeSerialize(rows: number, start: number, _end: number): void {
this._allRows = new Array<string>(rows)
this._lastContentCursorRow = start
this._lastCursorRow = start
this._firstRow = start
}
protected _rowEnd(row: number, isLastRow: boolean): void {
let rowSeparator = ""
if (!isLastRow) {
const nextLine = this._buffer.getLine(row + 1)
if (!nextLine?.isWrapped) {
rowSeparator = "\r\n"
this._lastCursorRow = row + 1
this._lastCursorCol = 0
}
}
this._allRows[this._rowIndex] = this._currentRow
this._allRowSeparators[this._rowIndex++] = rowSeparator
this._currentRow = ""
this._nullCellCount = 0
}
private _diffStyle(cell: IBufferCell, oldCell: IBufferCell): number[] {
const sgrSeq: number[] = []
const fgChanged = !equalFg(cell, oldCell)
const bgChanged = !equalBg(cell, oldCell)
const flagsChanged = !equalFlags(cell, oldCell)
if (fgChanged || bgChanged || flagsChanged) {
if (this._isAttributeDefault(cell)) {
if (!this._isAttributeDefault(oldCell)) {
sgrSeq.push(0)
}
} else {
if (flagsChanged) {
if (!!cell.isInverse() !== !!oldCell.isInverse()) {
sgrSeq.push(cell.isInverse() ? 7 : 27)
}
if (!!cell.isBold() !== !!oldCell.isBold()) {
sgrSeq.push(cell.isBold() ? 1 : 22)
}
if (!!cell.isUnderline() !== !!oldCell.isUnderline()) {
sgrSeq.push(cell.isUnderline() ? 4 : 24)
}
if (!!cell.isBlink() !== !!oldCell.isBlink()) {
sgrSeq.push(cell.isBlink() ? 5 : 25)
}
if (!!cell.isInvisible() !== !!oldCell.isInvisible()) {
sgrSeq.push(cell.isInvisible() ? 8 : 28)
}
if (!!cell.isItalic() !== !!oldCell.isItalic()) {
sgrSeq.push(cell.isItalic() ? 3 : 23)
}
if (!!cell.isDim() !== !!oldCell.isDim()) {
sgrSeq.push(cell.isDim() ? 2 : 22)
}
if (!!cell.isStrikethrough() !== !!oldCell.isStrikethrough()) {
sgrSeq.push(cell.isStrikethrough() ? 9 : 29)
}
}
if (fgChanged) {
const color = cell.getFgColor()
const mode = cell.getFgColorMode()
if (mode === 2 || mode === 3 || mode === -1) {
sgrSeq.push(38, 2, (color >>> 16) & 0xff, (color >>> 8) & 0xff, color & 0xff)
} else if (mode === 1) {
// Palette
if (color >= 16) {
sgrSeq.push(38, 5, color)
} else {
sgrSeq.push(color & 8 ? 90 + (color & 7) : 30 + (color & 7))
}
} else {
sgrSeq.push(39)
}
}
if (bgChanged) {
const color = cell.getBgColor()
const mode = cell.getBgColorMode()
if (mode === 2 || mode === 3 || mode === -1) {
sgrSeq.push(48, 2, (color >>> 16) & 0xff, (color >>> 8) & 0xff, color & 0xff)
} else if (mode === 1) {
// Palette
if (color >= 16) {
sgrSeq.push(48, 5, color)
} else {
sgrSeq.push(color & 8 ? 100 + (color & 7) : 40 + (color & 7))
}
} else {
sgrSeq.push(49)
}
}
}
}
return sgrSeq
}
private _isAttributeDefault(cell: IBufferCell): boolean {
const mode = cell.getFgColorMode()
const bgMode = cell.getBgColorMode()
if (mode === 0 && bgMode === 0) {
return (
!cell.isBold() &&
!cell.isItalic() &&
!cell.isUnderline() &&
!cell.isBlink() &&
!cell.isInverse() &&
!cell.isInvisible() &&
!cell.isDim() &&
!cell.isStrikethrough()
)
}
const fgColor = cell.getFgColor()
const bgColor = cell.getBgColor()
const nullCell = this._buffer.getNullCell()
const nullFg = nullCell.getFgColor()
const nullBg = nullCell.getBgColor()
return (
fgColor === nullFg &&
bgColor === nullBg &&
!cell.isBold() &&
!cell.isItalic() &&
!cell.isUnderline() &&
!cell.isBlink() &&
!cell.isInverse() &&
!cell.isInvisible() &&
!cell.isDim() &&
!cell.isStrikethrough()
)
}
protected _nextCell(cell: IBufferCell, _oldCell: IBufferCell, row: number, col: number): void {
const isPlaceHolderCell = cell.getWidth() === 0
if (isPlaceHolderCell) {
return
}
const codepoint = cell.getCode()
const isGarbage = codepoint >= 0xf000
const isEmptyCell = codepoint === 0 || cell.getChars() === "" || isGarbage
const sgrSeq = this._diffStyle(cell, this._cursorStyle)
const styleChanged = isEmptyCell ? !equalBg(this._cursorStyle, cell) : sgrSeq.length > 0
if (styleChanged) {
if (this._nullCellCount > 0) {
this._currentRow += `\u001b[${this._nullCellCount}C`
this._nullCellCount = 0
}
this._lastContentCursorRow = this._lastCursorRow = row
this._lastContentCursorCol = this._lastCursorCol = col
this._currentRow += `\u001b[${sgrSeq.join(";")}m`
const line = this._buffer.getLine(row)
const cellFromLine = line?.getCell(col)
if (cellFromLine) {
this._cursorStyle = cellFromLine
}
}
if (isEmptyCell) {
this._nullCellCount += cell.getWidth()
} else {
if (this._nullCellCount > 0) {
this._currentRow += `\u001b[${this._nullCellCount}C`
this._nullCellCount = 0
}
this._currentRow += cell.getChars()
this._lastContentCursorRow = this._lastCursorRow = row
this._lastContentCursorCol = this._lastCursorCol = col + cell.getWidth()
}
}
protected _serializeString(excludeFinalCursorPosition?: boolean): string {
let rowEnd = this._allRows.length
if (this._buffer.length - this._firstRow <= this._terminal.rows) {
rowEnd = this._lastContentCursorRow + 1 - this._firstRow
this._lastCursorCol = this._lastContentCursorCol
this._lastCursorRow = this._lastContentCursorRow
}
let content = ""
for (let i = 0; i < rowEnd; i++) {
content += this._allRows[i]
if (i + 1 < rowEnd) {
content += this._allRowSeparators[i]
}
}
if (!excludeFinalCursorPosition) {
const absoluteCursorRow = (this._buffer.baseY ?? 0) + this._buffer.cursorY
const cursorRow = constrain(absoluteCursorRow - this._firstRow + 1, 1, Number.MAX_SAFE_INTEGER)
const cursorCol = this._buffer.cursorX + 1
content += `\u001b[${cursorRow};${cursorCol}H`
}
return content
}
}
// ============================================================================
// SerializeAddon Class
// ============================================================================
export class SerializeAddon implements ITerminalAddon {
private _terminal?: ITerminalCore
/**
* Activate the addon (called by Terminal.loadAddon)
*/
public activate(terminal: ITerminalCore): void {
this._terminal = terminal
}
/**
* Dispose the addon and clean up resources
*/
public dispose(): void {
this._terminal = undefined
}
/**
* Serializes terminal rows into a string that can be written back to the
* terminal to restore the state. The cursor will also be positioned to the
* correct cell.
*
* @param options Custom options to allow control over what gets serialized.
*/
public serialize(options?: ISerializeOptions): string {
if (!this._terminal) {
throw new Error("Cannot use addon until it has been loaded")
}
const terminal = this._terminal as any
const buffer = terminal.buffer
if (!buffer) {
return ""
}
const normalBuffer = buffer.normal || buffer.active
const altBuffer = buffer.alternate
if (!normalBuffer) {
return ""
}
let content = options?.range
? this._serializeBufferByRange(normalBuffer, options.range, true)
: this._serializeBufferByScrollback(normalBuffer, options?.scrollback)
if (!options?.excludeAltBuffer && buffer.active?.type === "alternate" && altBuffer) {
const alternateContent = this._serializeBufferByScrollback(altBuffer, undefined)
content += `\u001b[?1049h\u001b[H${alternateContent}`
}
return content
}
/**
* Serializes terminal content as plain text (no escape sequences)
* @param options Custom options to allow control over what gets serialized.
*/
public serializeAsText(options?: { scrollback?: number; trimWhitespace?: boolean }): string {
if (!this._terminal) {
throw new Error("Cannot use addon until it has been loaded")
}
const terminal = this._terminal as any
const buffer = terminal.buffer
if (!buffer) {
return ""
}
const activeBuffer = buffer.active || buffer.normal
if (!activeBuffer) {
return ""
}
const maxRows = activeBuffer.length
const scrollback = options?.scrollback
const correctRows = scrollback === undefined ? maxRows : constrain(scrollback + this._terminal.rows, 0, maxRows)
const startRow = maxRows - correctRows
const endRow = maxRows - 1
const lines: string[] = []
for (let row = startRow; row <= endRow; row++) {
const line = activeBuffer.getLine(row)
if (line) {
const text = line.translateToString(options?.trimWhitespace ?? true)
lines.push(text)
}
}
// Trim trailing empty lines if requested
if (options?.trimWhitespace) {
while (lines.length > 0 && lines[lines.length - 1] === "") {
lines.pop()
}
}
return lines.join("\n")
}
private _serializeBufferByScrollback(buffer: IBuffer, scrollback?: number): string {
const maxRows = buffer.length
const rows = this._terminal?.rows ?? 24
const correctRows = scrollback === undefined ? maxRows : constrain(scrollback + rows, 0, maxRows)
return this._serializeBufferByRange(
buffer,
{
start: maxRows - correctRows,
end: maxRows - 1,
},
false,
)
}
private _serializeBufferByRange(
buffer: IBuffer,
range: ISerializeRange,
excludeFinalCursorPosition: boolean,
): string {
const handler = new StringSerializeHandler(buffer, this._terminal!)
const cols = this._terminal?.cols ?? 80
return handler.serialize(
{
start: { x: 0, y: range.start },
end: { x: cols, y: range.end },
},
excludeFinalCursorPosition,
)
}
}
+95
View File
@@ -0,0 +1,95 @@
import "@/index.css"
import { ErrorBoundary, Show } 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 { LayoutProvider } from "@/context/layout"
import { GlobalSDKProvider } from "@/context/global-sdk"
import { TerminalProvider } from "@/context/terminal"
import { PromptProvider } from "@/context/prompt"
import { NotificationProvider } from "@/context/notification"
import { DialogProvider } from "@opencode-ai/ui/context/dialog"
import { CommandProvider } from "@/context/command"
import Layout from "@/pages/layout"
import Home from "@/pages/home"
import DirectoryLayout from "@/pages/directory-layout"
import Session from "@/pages/session"
import { ErrorPage } from "./pages/error"
import { iife } from "@opencode-ai/util/iife"
declare global {
interface Window {
__OPENCODE__?: { updaterEnabled?: boolean; port?: number }
}
}
const url = iife(() => {
const param = new URLSearchParams(document.location.search).get("url")
if (param) return param
if (location.hostname.includes("opencode.ai")) return "http://localhost:4096"
if (window.__OPENCODE__) return `http://127.0.0.1:${window.__OPENCODE__.port}`
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
})
export function App() {
return (
<MetaProvider>
<Font />
<ThemeProvider>
<ErrorBoundary fallback={(error) => <ErrorPage error={error} />}>
<DialogProvider>
<MarkedProvider>
<DiffComponentProvider component={Diff}>
<CodeComponentProvider component={Code}>
<GlobalSDKProvider url={url}>
<GlobalSyncProvider>
<LayoutProvider>
<NotificationProvider>
<Router
root={(props) => (
<CommandProvider>
<Layout>{props.children}</Layout>
</CommandProvider>
)}
>
<Route path="/" component={Home} />
<Route path="/:dir" component={DirectoryLayout}>
<Route path="/" component={() => <Navigate href="session" />} />
<Route
path="/session/:id?"
component={(p) => (
<Show when={p.params.id || true} keyed>
<TerminalProvider>
<PromptProvider>
<Session />
</PromptProvider>
</TerminalProvider>
</Show>
)}
/>
</Route>
</Router>
</NotificationProvider>
</LayoutProvider>
</GlobalSyncProvider>
</GlobalSDKProvider>
</CodeComponentProvider>
</DiffComponentProvider>
</MarkedProvider>
</DialogProvider>
</ErrorBoundary>
</ThemeProvider>
</MetaProvider>
)
}
@@ -0,0 +1,383 @@
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 { Link } from "@/components/link"
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()
const globalSync = useGlobalSync()
const globalSDK = useGlobalSDK()
const platform = usePlatform()
const provider = createMemo(() => globalSync.data.provider.all.find((x) => x.id === props.provider)!)
const methods = createMemo(
() =>
globalSync.data.provider_auth[props.provider] ?? [
{
type: "api",
label: "API key",
},
],
)
const [store, setStore] = createStore({
methodIndex: undefined as undefined | number,
authorization: undefined as undefined | ProviderAuthAuthorization,
state: "pending" as undefined | "pending" | "complete" | "error",
error: undefined as string | undefined,
})
const method = createMemo(() => (store.methodIndex !== undefined ? methods().at(store.methodIndex!) : undefined))
async function selectMethod(index: number) {
const method = methods()[index]
setStore(
produce((draft) => {
draft.methodIndex = index
draft.authorization = undefined
draft.state = undefined
draft.error = undefined
}),
)
if (method.type === "oauth") {
setStore("state", "pending")
const start = Date.now()
await globalSDK.client.provider.oauth
.authorize(
{
providerID: props.provider,
method: index,
},
{ throwOnError: true },
)
.then((x) => {
const elapsed = Date.now() - start
const delay = 1000 - elapsed
if (delay > 0) {
setTimeout(() => {
setStore("state", "complete")
setStore("authorization", x.data!)
}, delay)
return
}
setStore("state", "complete")
setStore("authorization", x.data!)
})
.catch((e) => {
setStore("state", "error")
setStore("error", String(e))
})
}
}
let listRef: ListRef | undefined
function handleKey(e: KeyboardEvent) {
if (e.key === "Enter" && e.target instanceof HTMLInputElement) {
return
}
if (e.key === "Escape") return
listRef?.onKeyDown(e)
}
onMount(() => {
if (methods().length === 1) {
selectMethod(0)
}
document.addEventListener("keydown", handleKey)
onCleanup(() => {
document.removeEventListener("keydown", handleKey)
})
})
async function complete() {
await globalSDK.client.global.dispose()
dialog.close()
showToast({
variant: "success",
icon: "circle-check",
title: `${provider().name} connected`,
description: `${provider().name} models are now available to use.`,
})
}
function goBack() {
if (methods().length === 1) {
dialog.show(() => <DialogSelectProvider />)
return
}
if (store.authorization) {
setStore("authorization", undefined)
setStore("methodIndex", undefined)
return
}
if (store.methodIndex) {
setStore("methodIndex", undefined)
return
}
dialog.show(() => <DialogSelectProvider />)
}
return (
<Dialog title={<IconButton tabIndex={-1} icon="arrow-left" variant="ghost" onClick={goBack} />}>
<div class="flex flex-col gap-6 px-2.5 pb-3">
<div class="px-2.5 flex gap-4 items-center">
<ProviderIcon id={props.provider as IconName} class="size-5 shrink-0 icon-strong-base" />
<div class="text-16-medium text-text-strong">
<Switch>
<Match when={props.provider === "anthropic" && method()?.label?.toLowerCase().includes("max")}>
Login with Claude Pro/Max
</Match>
<Match when={true}>Connect {provider().name}</Match>
</Switch>
</div>
</div>
<div class="px-2.5 pb-10 flex flex-col gap-6">
<Switch>
<Match when={store.methodIndex === undefined}>
<div class="text-14-regular text-text-base">Select login method for {provider().name}.</div>
<div class="">
<List
ref={(ref) => {
listRef = ref
}}
items={methods}
key={(m) => m?.label}
onSelect={async (method, index) => {
if (!method) return
selectMethod(index)
}}
>
{(i) => (
<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>
<span>{i.label}</span>
</div>
)}
</List>
</div>
</Match>
<Match when={store.state === "pending"}>
<div class="text-14-regular text-text-base">
<div class="flex items-center gap-x-2">
<Spinner />
<span>Authorization in progress...</span>
</div>
</div>
</Match>
<Match when={store.state === "error"}>
<div class="text-14-regular text-text-base">
<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>
</div>
</Match>
<Match when={method()?.type === "api"}>
{iife(() => {
const [formStore, setFormStore] = createStore({
value: "",
error: undefined as string | undefined,
})
async function handleSubmit(e: SubmitEvent) {
e.preventDefault()
const form = e.currentTarget as HTMLFormElement
const formData = new FormData(form)
const apiKey = formData.get("apiKey") as string
if (!apiKey?.trim()) {
setFormStore("error", "API key is required")
return
}
setFormStore("error", undefined)
await globalSDK.client.auth.set({
providerID: props.provider,
auth: {
type: "api",
key: apiKey,
},
})
await complete()
}
return (
<div class="flex flex-col gap-6">
<Switch>
<Match when={provider().id === "opencode"}>
<div class="flex flex-col gap-4">
<div class="text-14-regular text-text-base">
OpenCode Zen gives you access to a curated set of reliable optimized models for coding
agents.
</div>
<div class="text-14-regular text-text-base">
With a single API key you'll get access to models such as Claude, GPT, Gemini, GLM and more.
</div>
<div class="text-14-regular text-text-base">
Visit{" "}
<Link href="https://opencode.ai/zen" tabIndex={-1}>
opencode.ai/zen
</Link>{" "}
to collect your API key.
</div>
</div>
</Match>
<Match when={true}>
<div class="text-14-regular text-text-base">
Enter your {provider().name} API key to connect your account and use {provider().name} models
in OpenCode.
</div>
</Match>
</Switch>
<form onSubmit={handleSubmit} class="flex flex-col items-start gap-4">
<TextField
autofocus
type="text"
label={`${provider().name} API key`}
placeholder="API key"
name="apiKey"
value={formStore.value}
onChange={setFormStore.bind(null, "value")}
validationState={formStore.error ? "invalid" : undefined}
error={formStore.error}
/>
<Button class="w-auto" type="submit" size="large" variant="primary">
Submit
</Button>
</form>
</div>
)
})}
</Match>
<Match when={method()?.type === "oauth"}>
<Switch>
<Match when={store.authorization?.method === "code"}>
{iife(() => {
const [formStore, setFormStore] = createStore({
value: "",
error: undefined as string | undefined,
})
onMount(() => {
if (store.authorization?.method === "code" && store.authorization?.url) {
platform.openLink(store.authorization.url)
}
})
async function handleSubmit(e: SubmitEvent) {
e.preventDefault()
const form = e.currentTarget as HTMLFormElement
const formData = new FormData(form)
const code = formData.get("code") as string
if (!code?.trim()) {
setFormStore("error", "Authorization code is required")
return
}
setFormStore("error", undefined)
const { error } = await globalSDK.client.provider.oauth.callback({
providerID: props.provider,
method: store.methodIndex,
code,
})
if (!error) {
await complete()
return
}
setFormStore("error", "Invalid authorization code")
}
return (
<div class="flex flex-col gap-6">
<div class="text-14-regular text-text-base">
Visit <Link href={store.authorization!.url}>this link</Link> to collect your authorization
code to connect your account and use {provider().name} models in OpenCode.
</div>
<form onSubmit={handleSubmit} class="flex flex-col items-start gap-4">
<TextField
autofocus
type="text"
label={`${method()?.label} authorization code`}
placeholder="Authorization code"
name="code"
value={formStore.value}
onChange={setFormStore.bind(null, "value")}
validationState={formStore.error ? "invalid" : undefined}
error={formStore.error}
/>
<Button class="w-auto" type="submit" size="large" variant="primary">
Submit
</Button>
</form>
</div>
)
})}
</Match>
<Match when={store.authorization?.method === "auto"}>
{iife(() => {
const code = createMemo(() => {
const instructions = store.authorization?.instructions
if (instructions?.includes(":")) {
return instructions?.split(":")[1]?.trim()
}
return instructions
})
onMount(async () => {
const result = await globalSDK.client.provider.oauth.callback({
providerID: props.provider,
method: store.methodIndex,
})
if (result.error) {
// TODO: show error
dialog.close()
return
}
await complete()
})
return (
<div class="flex flex-col gap-6">
<div class="text-14-regular text-text-base">
Visit <Link href={store.authorization!.url}>this link</Link> and enter the code below to
connect your account and use {provider().name} models in OpenCode.
</div>
<TextField label="Confirmation code" class="font-mono" value={code()} readOnly copyable />
<div class="text-14-regular text-text-base flex items-center gap-4">
<Spinner />
<span>Waiting for authorization...</span>
</div>
</div>
)
})}
</Match>
</Switch>
</Match>
</Switch>
</div>
</div>
</Dialog>
)
}
@@ -0,0 +1,57 @@
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
search={{ placeholder: "Search models", autofocus: true }}
emptyMessage="No model results"
key={(x) => `${x?.provider?.id}:${x?.id}`}
items={local.model.list()}
filterKeys={["provider.name", "name", "id"]}
sortBy={(a, b) => a.name.localeCompare(b.name)}
groupBy={(x) => x.provider.name}
sortGroupsBy={(a, b) => {
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) => {
if (!x) return
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-3">
<span>{i.name}</span>
<div onClick={(e) => e.stopPropagation()}>
<Switch
checked={
!!local.model.visible({
modelID: i.id,
providerID: i.provider.id,
})
}
onChange={(checked) => {
local.model.setVisibility({ modelID: i.id, providerID: i.provider.id }, checked)
}}
/>
</div>
</div>
)}
</List>
</Dialog>
)
}
@@ -0,0 +1,48 @@
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 { useLocal } from "@/context/local"
export function DialogSelectFile() {
const layout = useLayout()
const local = useLocal()
const dialog = useDialog()
const params = useParams()
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
const tabs = createMemo(() => layout.tabs(sessionKey()))
return (
<Dialog title="Select file">
<List
search={{ placeholder: "Search files", autofocus: true }}
emptyMessage="No files found"
items={local.file.searchFiles}
key={(x) => x}
onSelect={(path) => {
if (path) {
tabs().open("file://" + path)
}
dialog.close()
}}
>
{(i) => (
<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: 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">
{getDirectory(i)}
</span>
<span class="text-text-strong whitespace-nowrap">{getFilename(i)}</span>
</div>
</div>
</div>
)}
</List>
</Dialog>
)
}
@@ -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>
)
}
@@ -0,0 +1,110 @@
import { Button } from "@opencode-ai/ui/button"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
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 { 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()
const dialog = useDialog()
const providers = useProviders()
let listRef: ListRef | undefined
const handleKey = (e: KeyboardEvent) => {
if (e.key === "Escape") return
listRef?.onKeyDown(e)
}
onMount(() => {
document.addEventListener("keydown", handleKey)
onCleanup(() => {
document.removeEventListener("keydown", handleKey)
})
})
return (
<Dialog title="Select model">
<div class="flex flex-col gap-3 px-2.5">
<div class="text-14-medium text-text-base px-2.5">Free models provided by OpenCode</div>
<List
ref={(ref) => (listRef = ref)}
items={local.model.list}
current={local.model.current()}
key={(x) => `${x.provider.id}:${x.id}`}
onSelect={(x) => {
local.model.set(x ? { modelID: x.id, providerID: x.provider.id } : undefined, {
recent: true,
})
dialog.close()
}}
>
{(i) => (
<div class="w-full flex items-center gap-x-2.5">
<span>{i.name}</span>
<Tag>Free</Tag>
<Show when={i.latest}>
<Tag>Latest</Tag>
</Show>
</div>
)}
</List>
<div />
<div />
</div>
<div class="px-1.5 pb-1.5">
<div class="w-full rounded-sm border border-border-weak-base bg-surface-raised-base">
<div class="w-full flex flex-col items-start gap-4 px-1.5 pt-4 pb-4">
<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 px-0"
key={(x) => x?.id}
items={providers.popular}
activeIcon="plus-small"
sortBy={(a, b) => {
if (popularProviders.includes(a.id) && popularProviders.includes(b.id))
return popularProviders.indexOf(a.id) - popularProviders.indexOf(b.id)
return a.name.localeCompare(b.name)
}}
onSelect={(x) => {
if (!x) return
dialog.show(() => <DialogConnectProvider provider={x.id} />)
}}
>
{(i) => (
<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>
</Show>
<Show when={i.id === "anthropic"}>
<div class="text-14-regular text-text-weak">Connect with Claude Pro/Max or API key</div>
</Show>
</div>
)}
</List>
<Button
variant="ghost"
class="w-full justify-start px-[11px] py-3.5 gap-4.5 text-14-medium"
icon="dot-grid"
onClick={() => {
dialog.show(() => <DialogSelectProvider />)
}}
>
View all providers
</Button>
</div>
</div>
</div>
</div>
</Dialog>
)
}
@@ -0,0 +1,83 @@
import { Component, createMemo, 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 { DialogSelectProvider } from "./dialog-select-provider"
import { DialogManageModels } from "./dialog-manage-models"
export const DialogSelectModel: Component<{ provider?: string }> = (props) => {
const local = useLocal()
const dialog = useDialog()
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 (
<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>
}
>
<List
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}
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,
})
dialog.close()
}}
>
{(i) => (
<div class="w-full flex items-center gap-x-3">
<span>{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>
<Button
variant="ghost"
class="ml-3 mt-5 mb-6 text-text-base self-start"
onClick={() => dialog.show(() => <DialogManageModels />)}
>
Manage models
</Button>
</Dialog>
)
}
@@ -0,0 +1,54 @@
import { Component, Show } from "solid-js"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { popularProviders, useProviders } from "@/hooks/use-providers"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
import { Tag } from "@opencode-ai/ui/tag"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { IconName } from "@opencode-ai/ui/icons/provider"
import { DialogConnectProvider } from "./dialog-connect-provider"
export const DialogSelectProvider: Component = () => {
const dialog = useDialog()
const providers = useProviders()
return (
<Dialog title="Connect provider">
<List
search={{ placeholder: "Search providers", autofocus: true }}
activeIcon="plus-small"
key={(x) => x?.id}
items={providers.all}
filterKeys={["id", "name"]}
groupBy={(x) => (popularProviders.includes(x.id) ? "Popular" : "Other")}
sortBy={(a, b) => {
if (popularProviders.includes(a.id) && popularProviders.includes(b.id))
return popularProviders.indexOf(a.id) - popularProviders.indexOf(b.id)
return a.name.localeCompare(b.name)
}}
sortGroupsBy={(a, b) => {
if (a.category === "Popular" && b.category !== "Popular") return -1
if (b.category === "Popular" && a.category !== "Popular") return 1
return 0
}}
onSelect={(x) => {
if (!x) return
dialog.show(() => <DialogConnectProvider provider={x.id} />)
}}
>
{(i) => (
<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>
</Show>
<Show when={i.id === "anthropic"}>
<div class="text-14-regular text-text-weak">Connect with Claude Pro/Max or API key</div>
</Show>
</div>
)}
</List>
</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>
)
+215
View File
@@ -0,0 +1,215 @@
import { useGlobalSync } from "@/context/global-sync"
import { useGlobalSDK } from "@/context/global-sdk"
import { useLayout } from "@/context/layout"
import { Session } from "@opencode-ai/sdk/v2/client"
import { Button } from "@opencode-ai/ui/button"
import { Icon } from "@opencode-ai/ui/icon"
import { Mark } from "@opencode-ai/ui/logo"
import { Popover } from "@opencode-ai/ui/popover"
import { Select } from "@opencode-ai/ui/select"
import { TextField } from "@opencode-ai/ui/text-field"
import { Tooltip } from "@opencode-ai/ui/tooltip"
import { base64Decode } from "@opencode-ai/util/encode"
import { useCommand } from "@/context/command"
import { getFilename } from "@opencode-ai/util/path"
import { A, useParams } from "@solidjs/router"
import { createMemo, createResource, Show } from "solid-js"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { iife } from "@opencode-ai/util/iife"
export function Header(props: {
navigateToProject: (directory: string) => void
navigateToSession: (session: Session | undefined) => void
onMobileMenuToggle?: () => void
}) {
const globalSync = useGlobalSync()
const globalSDK = useGlobalSDK()
const layout = useLayout()
const params = useParams()
const command = useCommand()
return (
<header class="h-12 shrink-0 bg-background-base border-b border-border-weak-base flex" data-tauri-drag-region>
<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={props.onMobileMenuToggle}
>
<Icon name="menu" size="small" />
</button>
<A
href="/"
classList={{
"hidden xl:flex": true,
"w-12 shrink-0 px-4 py-3.5": true,
"items-center justify-start self-stretch": true,
"border-r border-border-weak-base": true,
}}
style={{ width: layout.sidebar.opened() ? `${layout.sidebar.width()}px` : undefined }}
data-tauri-drag-region
>
<Mark class="shrink-0" />
</A>
<div class="pl-4 px-6 flex items-center justify-between gap-4 w-full">
<Show when={layout.projects.list().length > 0 && params.dir}>
{(directory) => {
const currentDirectory = createMemo(() => base64Decode(directory()))
const store = createMemo(() => globalSync.child(currentDirectory())[0])
const sessions = createMemo(() => (store().session ?? []).filter((s) => !s.parentID))
const currentSession = createMemo(() => sessions().find((s) => s.id === params.id))
const shareEnabled = createMemo(() => store().config.share !== "disabled")
return (
<>
<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={layout.projects.list().map((project) => project.worktree)}
current={currentDirectory()}
label={(x) => getFilename(x)}
onSelect={(x) => (x ? props.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>
<Select
options={sessions()}
current={currentSession()}
placeholder="New session"
label={(x) => x.title}
value={(x) => x.id}
onSelect={props.navigateToSession}
class="text-14-regular text-text-base max-w-[calc(100vw-180px)] md:max-w-md"
variant="ghost"
/>
</div>
<Show when={currentSession()}>
<Tooltip
class="hidden xl:block"
value={
<div class="flex items-center gap-2">
<span>New session</span>
<span class="text-icon-base text-12-medium">{command.keybind("session.new")}</span>
</div>
}
>
<Button as={A} href={`/${params.dir}/session`} icon="plus-small">
New session
</Button>
</Tooltip>
</Show>
</div>
<div class="flex items-center gap-4">
<Show when={currentSession()?.summary?.files}>
<Tooltip
class="hidden md:block shrink-0"
value={
<div class="flex items-center gap-2">
<span>Toggle review</span>
<span class="text-icon-base text-12-medium">{command.keybind("review.toggle")}</span>
</div>
}
>
<Button variant="ghost" class="group/review-toggle size-6 p-0" onClick={layout.review.toggle}>
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
<Icon
name={layout.review.opened() ? "layout-right" : "layout-left"}
size="small"
class="group-hover/review-toggle:hidden"
/>
<Icon
name={layout.review.opened() ? "layout-right-partial" : "layout-left-partial"}
size="small"
class="hidden group-hover/review-toggle:inline-block"
/>
<Icon
name={layout.review.opened() ? "layout-right-full" : "layout-left-full"}
size="small"
class="hidden group-active/review-toggle:inline-block"
/>
</div>
</Button>
</Tooltip>
</Show>
<Tooltip
class="hidden md:block shrink-0"
value={
<div class="flex items-center gap-2">
<span>Toggle terminal</span>
<span class="text-icon-base text-12-medium">{command.keybind("terminal.toggle")}</span>
</div>
}
>
<Button variant="ghost" class="group/terminal-toggle size-6 p-0" onClick={layout.terminal.toggle}>
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
<Icon
size="small"
name={layout.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={layout.terminal.opened() ? "layout-bottom" : "layout-bottom-full"}
class="hidden group-active/terminal-toggle:inline-block"
/>
</div>
</Button>
</Tooltip>
<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: currentDirectory() })
.then((r) => r.data?.share?.url)
.catch((e) => {
console.error("Failed to share session", e)
return undefined
})
}
return shareURL
},
)
return (
<Show when={url()}>
{(url) => <TextField value={url()} readOnly copyable class="w-72" />}
</Show>
)
})}
</Popover>
</Show>
</div>
</>
)
}}
</Show>
</div>
</header>
)
}
+17
View File
@@ -0,0 +1,17 @@
import { ComponentProps, splitProps } from "solid-js"
import { usePlatform } from "@/context/platform"
export interface LinkProps extends ComponentProps<"button"> {
href: string
}
export function Link(props: LinkProps) {
const platform = usePlatform()
const [local, rest] = splitProps(props, ["href", "children"])
return (
<button class="text-text-strong underline" onClick={() => platform.openLink(local.href)} {...rest}>
{local.children}
</button>
)
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,63 @@
import { createMemo, Show } from "solid-js"
import { Tooltip } from "@opencode-ai/ui/tooltip"
import { ProgressCircle } from "@opencode-ai/ui/progress-circle"
import { useSync } from "@/context/sync"
import { useParams } from "@solidjs/router"
import { AssistantMessage } from "@opencode-ai/sdk/v2"
export function SessionContextUsage() {
const sync = useSync()
const params = useParams()
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) => x.role === "assistant" && x.tokens.output > 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,
}
})
return (
<Show when={context?.()}>
{(ctx) => (
<Tooltip
value={
<div class="flex flex-col gap-1">
<div class="flex gap-3">
<span class="opacity-70 text-right flex-1">Tokens</span>
<span class="text-left flex-1">{ctx().tokens}</span>
</div>
<div class="flex gap-3">
<span class="opacity-70 text-right flex-1">Usage</span>
<span class="text-left flex-1">{ctx().percentage ?? 0}%</span>
</div>
<div class="flex gap-3">
<span class="opacity-70 text-right flex-1">Cost</span>
<span class="text-left flex-1">{cost()}</span>
</div>
</div>
}
placement="top"
>
<div class="flex items-center gap-1.5">
<ProgressCircle size={16} strokeWidth={2} percentage={ctx().percentage ?? 0} />
{/* <span class="text-12-medium text-text-weak">{`${ctx().percentage ?? 0}%`}</span> */}
</div>
</Tooltip>
)}
</Show>
)
}
@@ -0,0 +1,40 @@
import { createMemo, Show } from "solid-js"
import { Icon } from "@opencode-ai/ui/icon"
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">
<Icon
name="code"
size="small"
classList={{
"text-icon-critical-base": lspStats().hasError,
"text-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,36 @@
import { createMemo, Show } from "solid-js"
import { Button } from "@opencode-ai/ui/button"
import { Icon } from "@opencode-ai/ui/icon"
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 />)}>
<Icon
name="mcp"
size="small"
classList={{
"text-icon-critical-base": mcpStats().failed,
"text-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,32 @@
import { createMemo, Show, type ParentProps } from "solid-js"
import { usePlatform } from "@/context/platform"
import { useSync } from "@/context/sync"
import { useGlobalSync } from "@/context/global-sync"
export function StatusBar(props: ParentProps) {
const platform = usePlatform()
const sync = useSync()
const globalSync = useGlobalSync()
const directoryDisplay = createMemo(() => {
const directory = sync.data.path.directory || ""
const home = globalSync.data.path.home || ""
const short = home && directory.startsWith(home) ? directory.replace(home, "~") : directory
const branch = sync.data.vcs?.branch
return branch ? `${short}:${branch}` : short
})
return (
<div class="h-8 w-full shrink-0 flex items-center justify-between px-2 border-t border-border-weak-base bg-background-base">
<div class="flex items-center gap-3">
<Show when={platform.version}>
<span class="text-12-regular text-text-weak">v{platform.version}</span>
</Show>
<Show when={directoryDisplay()}>
<span class="text-12-regular text-text-weak">{directoryDisplay()}</span>
</Show>
</div>
<div class="flex items-center">{props.children}</div>
</div>
)
}
+242
View File
@@ -0,0 +1,242 @@
import { 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 } 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
}
const DEFAULT_TERMINAL_COLORS: Record<"light" | "dark", TerminalColors> = {
light: {
background: "#fcfcfc",
foreground: "#211e1e",
cursor: "#211e1e",
},
dark: {
background: "#191515",
foreground: "#d4d4d4",
cursor: "#d4d4d4",
},
}
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
let term: Term
let ghostty: Ghostty
let serializeAddon: SerializeAddon
let fitAddon: FitAddon
let handleResize: () => void
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-base"] ?? fallback.foreground
const background = resolved["background-stronger"] ?? fallback.background
return {
background,
foreground: text,
cursor: text,
}
}
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 = () => term?.focus()
const copySelection = () => {
if (!term || !term.hasSelection()) return false
const selection = term.getSelection()
if (!selection) return false
const clipboard = navigator.clipboard
if (clipboard?.writeText) {
clipboard.writeText(selection).catch(() => {})
return true
}
if (!document.body) return false
const textarea = document.createElement("textarea")
textarea.value = selection
textarea.setAttribute("readonly", "")
textarea.style.position = "fixed"
textarea.style.opacity = "0"
document.body.appendChild(textarea)
textarea.select()
const copied = document.execCommand("copy")
document.body.removeChild(textarea)
return copied
}
const handlePointerDown = () => {
const activeElement = document.activeElement
if (activeElement instanceof HTMLElement && activeElement !== container) {
activeElement.blur()
}
focusTerminal()
}
onMount(async () => {
ghostty = await Ghostty.load()
ws = new WebSocket(sdk.url + `/pty/${local.pty.id}/connect?directory=${encodeURIComponent(sdk.directory)}`)
term = new Term({
cursorBlink: true,
fontSize: 14,
fontFamily: "IBM Plex Mono, monospace",
allowTransparency: true,
theme: terminalColors(),
scrollback: 10_000,
ghostty,
})
term.attachCustomKeyEventHandler((event) => {
const key = event.key.toLowerCase()
if (key === "c") {
const macCopy = event.metaKey && !event.ctrlKey && !event.altKey
const linuxCopy = event.ctrlKey && event.shiftKey && !event.metaKey
if ((macCopy || linuxCopy) && copySelection()) {
event.preventDefault()
return true
}
}
// allow for ctrl-` to toggle terminal in parent
if (event.ctrlKey && key === "`") {
event.preventDefault()
return true
}
return false
})
fitAddon = new FitAddon()
serializeAddon = new SerializeAddon()
term.loadAddon(serializeAddon)
term.loadAddon(fitAddon)
term.open(container)
container.addEventListener("pointerdown", handlePointerDown)
focusTerminal()
if (local.pty.buffer) {
if (local.pty.rows && local.pty.cols) {
term.resize(local.pty.cols, local.pty.rows)
}
term.reset()
term.write(local.pty.buffer)
if (local.pty.scrollY) {
term.scrollToLine(local.pty.scrollY)
}
fitAddon.fit()
}
fitAddon.observeResize()
handleResize = () => fitAddon.fit()
window.addEventListener("resize", handleResize)
term.onResize(async (size) => {
if (ws && ws.readyState === WebSocket.OPEN) {
await sdk.client.pty
.update({
ptyID: local.pty.id,
size: {
cols: size.cols,
rows: size.rows,
},
})
.catch(() => {})
}
})
term.onData((data) => {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(data)
}
})
term.onKey((key) => {
if (key.key == "Enter") {
props.onSubmit?.()
}
})
// term.onScroll((ydisp) => {
// console.log("Scroll position:", ydisp)
// })
ws.addEventListener("open", () => {
console.log("WebSocket connected")
sdk.client.pty
.update({
ptyID: local.pty.id,
size: {
cols: term.cols,
rows: term.rows,
},
})
.catch(() => {})
})
ws.addEventListener("message", (event) => {
term.write(event.data)
})
ws.addEventListener("error", (error) => {
console.error("WebSocket error:", error)
props.onConnectError?.(error)
})
ws.addEventListener("close", () => {
console.log("WebSocket disconnected")
})
})
onCleanup(() => {
if (handleResize) {
window.removeEventListener("resize", handleResize)
}
container.removeEventListener("pointerdown", handlePointerDown)
if (serializeAddon && props.onCleanup) {
const buffer = serializeAddon.serialize()
props.onCleanup({
...local.pty,
buffer,
rows: term.rows,
cols: term.cols,
scrollY: term.getViewportY(),
})
}
ws?.close()
term?.dispose()
})
return (
<div
ref={container}
data-component="terminal"
data-prevent-autofocus
style={{ "background-color": terminalColors().background }}
classList={{
...(local.classList ?? {}),
"size-full px-6 py-3 font-mono": true,
[local.class ?? ""]: !!local.class,
}}
{...others}
/>
)
}
+243
View File
@@ -0,0 +1,243 @@
import { createMemo, createSignal, onCleanup, onMount, Show, type Accessor } from "solid-js"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
const IS_MAC = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform)
export type KeybindConfig = string
export interface Keybind {
key: string
ctrl: boolean
meta: boolean
shift: boolean
alt: boolean
}
export interface CommandOption {
id: string
title: string
description?: string
category?: string
keybind?: KeybindConfig
slash?: string
suggested?: boolean
disabled?: boolean
onSelect?: (source?: "palette" | "keybind" | "slash") => void
}
export function parseKeybind(config: string): Keybind[] {
if (!config || config === "none") return []
return config.split(",").map((combo) => {
const parts = combo.trim().toLowerCase().split("+")
const keybind: Keybind = {
key: "",
ctrl: false,
meta: false,
shift: false,
alt: false,
}
for (const part of parts) {
switch (part) {
case "ctrl":
case "control":
keybind.ctrl = true
break
case "meta":
case "cmd":
case "command":
keybind.meta = true
break
case "mod":
if (IS_MAC) keybind.meta = true
else keybind.ctrl = true
break
case "alt":
case "option":
keybind.alt = true
break
case "shift":
keybind.shift = true
break
default:
keybind.key = part
break
}
}
return keybind
})
}
export function matchKeybind(keybinds: Keybind[], event: KeyboardEvent): boolean {
const eventKey = event.key.toLowerCase()
for (const kb of keybinds) {
const keyMatch = kb.key === eventKey
const ctrlMatch = kb.ctrl === (event.ctrlKey || false)
const metaMatch = kb.meta === (event.metaKey || false)
const shiftMatch = kb.shift === (event.shiftKey || false)
const altMatch = kb.alt === (event.altKey || false)
if (keyMatch && ctrlMatch && metaMatch && shiftMatch && altMatch) {
return true
}
}
return false
}
export function formatKeybind(config: string): string {
if (!config || config === "none") return ""
const keybinds = parseKeybind(config)
if (keybinds.length === 0) return ""
const kb = keybinds[0]
const parts: string[] = []
if (kb.ctrl) parts.push(IS_MAC ? "⌃" : "Ctrl")
if (kb.alt) parts.push(IS_MAC ? "⌥" : "Alt")
if (kb.shift) parts.push(IS_MAC ? "⇧" : "Shift")
if (kb.meta) parts.push(IS_MAC ? "⌘" : "Meta")
if (kb.key) {
const displayKey = kb.key.length === 1 ? kb.key.toUpperCase() : kb.key.charAt(0).toUpperCase() + kb.key.slice(1)
parts.push(displayKey)
}
return IS_MAC ? parts.join("") : parts.join("+")
}
function DialogCommand(props: { options: CommandOption[] }) {
const dialog = useDialog()
return (
<Dialog title="Commands">
<List
search={{ placeholder: "Search commands", autofocus: true }}
emptyMessage="No commands found"
items={() => props.options.filter((x) => !x.id.startsWith("suggested.") || !x.disabled)}
key={(x) => x?.id}
filterKeys={["title", "description", "category"]}
groupBy={(x) => x.category ?? ""}
onSelect={(option) => {
if (option) {
dialog.close()
option.onSelect?.("palette")
}
}}
>
{(option) => (
<div class="w-full flex items-center justify-between gap-4">
<div class="flex items-center gap-2 min-w-0">
<span class="text-14-regular text-text-strong whitespace-nowrap">{option.title}</span>
<Show when={option.description}>
<span class="text-14-regular text-text-weak truncate">{option.description}</span>
</Show>
</div>
<Show when={option.keybind}>
<span class="text-12-regular text-text-subtle shrink-0">{formatKeybind(option.keybind!)}</span>
</Show>
</div>
)}
</List>
</Dialog>
)
}
export const { use: useCommand, provider: CommandProvider } = createSimpleContext({
name: "Command",
init: () => {
const [registrations, setRegistrations] = createSignal<Accessor<CommandOption[]>[]>([])
const [suspendCount, setSuspendCount] = createSignal(0)
const dialog = useDialog()
const options = createMemo(() => {
const all = registrations().flatMap((x) => x())
const suggested = all.filter((x) => x.suggested && !x.disabled)
return [
...suggested.map((x) => ({
...x,
id: "suggested." + x.id,
category: "Suggested",
})),
...all,
]
})
const suspended = () => suspendCount() > 0
const showPalette = () => {
if (!dialog.active) {
dialog.show(() => <DialogCommand options={options().filter((x) => !x.disabled)} />)
}
}
const handleKeyDown = (event: KeyboardEvent) => {
if (suspended()) return
const paletteKeybinds = parseKeybind("mod+shift+p")
if (matchKeybind(paletteKeybinds, event)) {
event.preventDefault()
showPalette()
return
}
for (const option of options()) {
if (option.disabled) continue
if (!option.keybind) continue
const keybinds = parseKeybind(option.keybind)
if (matchKeybind(keybinds, event)) {
event.preventDefault()
option.onSelect?.("keybind")
return
}
}
}
onMount(() => {
document.addEventListener("keydown", handleKeyDown)
})
onCleanup(() => {
document.removeEventListener("keydown", handleKeyDown)
})
return {
register(cb: () => CommandOption[]) {
const results = createMemo(cb)
setRegistrations((arr) => [results, ...arr])
onCleanup(() => {
setRegistrations((arr) => arr.filter((x) => x !== results))
})
},
trigger(id: string, source?: "palette" | "keybind" | "slash") {
for (const option of options()) {
if (option.id === id || option.id === "suggested." + id) {
option.onSelect?.(source)
return
}
}
},
keybind(id: string) {
const option = options().find((x) => x.id === id || x.id === "suggested." + id)
if (!option?.keybind) return ""
return formatKeybind(option.keybind)
},
show: showPalette,
keybinds(enabled: boolean) {
setSuspendCount((count) => count + (enabled ? -1 : 1))
},
suspended,
get options() {
return options()
},
}
},
})
@@ -1,30 +1,32 @@
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/client"
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { createGlobalEmitter } from "@solid-primitives/event-bus"
import { onCleanup } from "solid-js"
import { usePlatform } from "./platform"
export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleContext({
name: "GlobalSDK",
init: (props: { url: string }) => {
const abort = new AbortController()
const sdk = createOpencodeClient({
const eventSdk = createOpencodeClient({
baseUrl: props.url,
signal: abort.signal,
// signal: AbortSignal.timeout(1000 * 60 * 10),
})
const emitter = createGlobalEmitter<{
[key: string]: Event
}>()
sdk.global.event().then(async (events) => {
eventSdk.global.event().then(async (events) => {
for await (const event of events.stream) {
// console.log("event", event)
emitter.emit(event.directory, event.payload)
emitter.emit(event.directory ?? "global", event.payload)
}
})
onCleanup(() => {
abort.abort()
const platform = usePlatform()
const sdk = createOpencodeClient({
baseUrl: props.url,
signal: AbortSignal.timeout(1000 * 60 * 10),
fetch: platform.fetch,
throwOnError: true,
})
return { url: props.url, client: sdk, event: emitter }
+458
View File
@@ -0,0 +1,458 @@
import {
type Message,
type Agent,
type Session,
type Part,
type Config,
type Path,
type Project,
type FileDiff,
type Todo,
type SessionStatus,
type ProviderListResponse,
type ProviderAuthResponse,
type Command,
type McpStatus,
type LspStatus,
type VcsInfo,
type Permission,
createOpencodeClient,
} from "@opencode-ai/sdk/v2/client"
import { createStore, produce, reconcile } from "solid-js/store"
import { Binary } from "@opencode-ai/util/binary"
import { retry } from "@opencode-ai/util/retry"
import { useGlobalSDK } from "./global-sdk"
import { ErrorPage, type InitError } from "../pages/error"
import { createContext, useContext, onMount, type ParentProps, Switch, Match } from "solid-js"
import { showToast } from "@opencode-ai/ui/toast"
import { getFilename } from "@opencode-ai/util/path"
type State = {
ready: boolean
agent: Agent[]
command: Command[]
project: string
provider: ProviderListResponse
config: Config
path: Path
session: Session[]
session_status: {
[sessionID: string]: SessionStatus
}
session_diff: {
[sessionID: string]: FileDiff[]
}
todo: {
[sessionID: string]: Todo[]
}
permission: {
[sessionID: string]: Permission[]
}
mcp: {
[name: string]: McpStatus
}
lsp: LspStatus[]
vcs: VcsInfo | undefined
limit: number
message: {
[sessionID: string]: Message[]
}
part: {
[messageID: string]: Part[]
}
}
function createGlobalSync() {
const globalSDK = useGlobalSDK()
const [globalStore, setGlobalStore] = createStore<{
ready: boolean
error?: InitError
path: Path
project: Project[]
provider: ProviderListResponse
provider_auth: ProviderAuthResponse
children: Record<string, State>
}>({
ready: false,
path: { state: "", config: "", worktree: "", directory: "", home: "" },
project: [],
provider: { all: [], connected: [], default: {} },
provider_auth: {},
children: {},
})
const children: Record<string, ReturnType<typeof createStore<State>>> = {}
function child(directory: string) {
if (!directory) console.error("No directory provided")
if (!children[directory]) {
setGlobalStore("children", directory, {
project: "",
provider: { all: [], connected: [], default: {} },
config: {},
path: { state: "", config: "", worktree: "", directory: "", home: "" },
ready: false,
agent: [],
command: [],
session: [],
session_status: {},
session_diff: {},
todo: {},
permission: {},
mcp: {},
lsp: [],
vcs: undefined,
limit: 5,
message: {},
part: {},
})
children[directory] = createStore(globalStore.children[directory])
bootstrapInstance(directory)
}
return children[directory]
}
async function loadSessions(directory: string) {
const [store, setStore] = child(directory)
globalSDK.client.session
.list({ directory })
.then((x) => {
const fourHoursAgo = Date.now() - 4 * 60 * 60 * 1000
const nonArchived = (x.data ?? [])
.slice()
.filter((s) => !s.time.archived)
.sort((a, b) => a.id.localeCompare(b.id))
// Include up to the limit, plus any updated in the last 4 hours
const sessions = nonArchived.filter((s, i) => {
if (i < store.limit) return true
const updated = new Date(s.time.updated).getTime()
return updated > fourHoursAgo
})
setStore("session", sessions)
})
.catch((err) => {
console.error("Failed to load sessions", err)
const project = getFilename(directory)
showToast({ title: `Failed to load sessions for ${project}`, description: err.message })
})
}
async function bootstrapInstance(directory: string) {
if (!directory) return
const [, setStore] = child(directory)
const sdk = createOpencodeClient({
baseUrl: globalSDK.url,
directory,
throwOnError: true,
})
const load = {
project: () => sdk.project.current().then((x) => setStore("project", x.data!.id)),
provider: () =>
sdk.provider.list().then((x) => {
const data = x.data!
setStore("provider", {
...data,
all: data.all.map((provider) => ({
...provider,
models: Object.fromEntries(
Object.entries(provider.models).filter(([, info]) => info.status !== "deprecated"),
),
})),
})
}),
path: () => sdk.path.get().then((x) => setStore("path", x.data!)),
agent: () => sdk.app.agents().then((x) => setStore("agent", x.data ?? [])),
command: () => sdk.command.list().then((x) => setStore("command", x.data ?? [])),
session: () => loadSessions(directory),
status: () => sdk.session.status().then((x) => setStore("session_status", x.data!)),
config: () => sdk.config.get().then((x) => setStore("config", x.data!)),
mcp: () => sdk.mcp.status().then((x) => setStore("mcp", x.data ?? {})),
lsp: () => sdk.lsp.status().then((x) => setStore("lsp", x.data ?? [])),
vcs: () => sdk.vcs.get().then((x) => setStore("vcs", x.data)),
permission: () =>
sdk.permission.list().then((x) => {
const grouped: Record<string, typeof x.data> = {}
for (const perm of x.data ?? []) {
grouped[perm.sessionID] = grouped[perm.sessionID] ?? []
grouped[perm.sessionID]!.push(perm)
}
setStore("permission", grouped)
}),
}
await Promise.all(Object.values(load).map((p) => retry(p).catch((e) => setGlobalStore("error", e))))
.then(() => setStore("ready", true))
.catch((e) => setGlobalStore("error", e))
}
globalSDK.event.listen((e) => {
const directory = e.name
const event = e.details
if (directory === "global") {
switch (event?.type) {
case "global.disposed": {
bootstrap()
break
}
case "project.updated": {
const result = Binary.search(globalStore.project, event.properties.id, (s) => s.id)
if (result.found) {
setGlobalStore("project", result.index, reconcile(event.properties))
return
}
setGlobalStore(
"project",
produce((draft) => {
draft.splice(result.index, 0, event.properties)
}),
)
break
}
}
return
}
const [store, setStore] = child(directory)
switch (event.type) {
case "server.instance.disposed": {
bootstrapInstance(directory)
break
}
case "session.updated": {
const result = Binary.search(store.session, event.properties.info.id, (s) => s.id)
if (event.properties.info.time.archived) {
if (result.found) {
setStore(
"session",
produce((draft) => {
draft.splice(result.index, 1)
}),
)
}
break
}
if (result.found) {
setStore("session", result.index, reconcile(event.properties.info))
break
}
setStore(
"session",
produce((draft) => {
draft.splice(result.index, 0, event.properties.info)
}),
)
break
}
case "session.diff":
setStore("session_diff", event.properties.sessionID, reconcile(event.properties.diff, { key: "file" }))
break
case "todo.updated":
setStore("todo", event.properties.sessionID, reconcile(event.properties.todos))
break
case "session.status": {
setStore("session_status", event.properties.sessionID, reconcile(event.properties.status))
break
}
case "message.updated": {
const messages = store.message[event.properties.info.sessionID]
if (!messages) {
setStore("message", event.properties.info.sessionID, [event.properties.info])
break
}
const result = Binary.search(messages, event.properties.info.id, (m) => m.id)
if (result.found) {
setStore("message", event.properties.info.sessionID, result.index, reconcile(event.properties.info))
break
}
setStore(
"message",
event.properties.info.sessionID,
produce((draft) => {
draft.splice(result.index, 0, event.properties.info)
}),
)
break
}
case "message.removed": {
const messages = store.message[event.properties.sessionID]
if (!messages) break
const result = Binary.search(messages, event.properties.messageID, (m) => m.id)
if (result.found) {
setStore(
"message",
event.properties.sessionID,
produce((draft) => {
draft.splice(result.index, 1)
}),
)
}
break
}
case "message.part.updated": {
const part = event.properties.part
const parts = store.part[part.messageID]
if (!parts) {
setStore("part", part.messageID, [part])
break
}
const result = Binary.search(parts, part.id, (p) => p.id)
if (result.found) {
setStore("part", part.messageID, result.index, reconcile(part))
break
}
setStore(
"part",
part.messageID,
produce((draft) => {
draft.splice(result.index, 0, part)
}),
)
break
}
case "message.part.removed": {
const parts = store.part[event.properties.messageID]
if (!parts) break
const result = Binary.search(parts, event.properties.partID, (p) => p.id)
if (result.found) {
setStore(
"part",
event.properties.messageID,
produce((draft) => {
draft.splice(result.index, 1)
}),
)
}
break
}
case "vcs.branch.updated": {
setStore("vcs", { branch: event.properties.branch })
break
}
case "permission.updated": {
const permissions = store.permission[event.properties.sessionID]
if (!permissions) {
setStore("permission", event.properties.sessionID, [event.properties])
} else {
const result = Binary.search(permissions, event.properties.id, (p) => p.id)
setStore(
"permission",
event.properties.sessionID,
produce((draft) => {
if (result.found) {
draft[result.index] = event.properties
return
}
draft.push(event.properties)
}),
)
}
break
}
case "permission.replied": {
const permissions = store.permission[event.properties.sessionID]
if (!permissions) break
const result = Binary.search(permissions, event.properties.permissionID, (p) => p.id)
if (!result.found) break
setStore(
"permission",
event.properties.sessionID,
produce((draft) => {
draft.splice(result.index, 1)
}),
)
break
}
}
})
async function bootstrap() {
const health = await globalSDK.client.global
.health()
.then((x) => x.data)
.catch(() => undefined)
if (!health?.healthy) {
setGlobalStore(
"error",
new Error(`Could not connect to server. Is there a server running at \`${globalSDK.url}\`?`),
)
return
}
return Promise.all([
retry(() =>
globalSDK.client.path.get().then((x) => {
setGlobalStore("path", x.data!)
}),
),
retry(() =>
globalSDK.client.project.list().then(async (x) => {
setGlobalStore(
"project",
x.data!.filter((p) => !p.worktree.includes("opencode-test")).sort((a, b) => a.id.localeCompare(b.id)),
)
}),
),
retry(() =>
globalSDK.client.provider.list().then((x) => {
const data = x.data!
setGlobalStore("provider", {
...data,
all: data.all.map((provider) => ({
...provider,
models: Object.fromEntries(
Object.entries(provider.models).filter(([, info]) => info.status !== "deprecated"),
),
})),
})
}),
),
retry(() =>
globalSDK.client.provider.auth().then((x) => {
setGlobalStore("provider_auth", x.data ?? {})
}),
),
])
.then(() => setGlobalStore("ready", true))
.catch((e) => setGlobalStore("error", e))
}
onMount(() => {
bootstrap()
})
return {
data: globalStore,
get ready() {
return globalStore.ready
},
get error() {
return globalStore.error
},
child,
bootstrap,
project: {
loadSessions,
},
}
}
const GlobalSyncContext = createContext<ReturnType<typeof createGlobalSync>>()
export function GlobalSyncProvider(props: ParentProps) {
const value = createGlobalSync()
return (
<Switch>
<Match when={value.error}>
<ErrorPage error={value.error} />
</Match>
<Match when={value.ready}>
<GlobalSyncContext.Provider value={value}>{props.children}</GlobalSyncContext.Provider>
</Match>
</Switch>
)
}
export function useGlobalSync() {
const context = useContext(GlobalSyncContext)
if (!context) throw new Error("useGlobalSync must be used within GlobalSyncProvider")
return context
}
+260
View File
@@ -0,0 +1,260 @@
import { createStore, produce } from "solid-js/store"
import { batch, createMemo, onMount } from "solid-js"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { useGlobalSync } from "./global-sync"
import { useGlobalSDK } from "./global-sdk"
import { Project } from "@opencode-ai/sdk/v2"
import { persisted } from "@/utils/persist"
const AVATAR_COLOR_KEYS = ["pink", "mint", "orange", "purple", "cyan", "lime"] as const
export type AvatarColorKey = (typeof AVATAR_COLOR_KEYS)[number]
export function getAvatarColors(key?: string) {
if (key && AVATAR_COLOR_KEYS.includes(key as AvatarColorKey)) {
return {
background: `var(--avatar-background-${key})`,
foreground: `var(--avatar-text-${key})`,
}
}
return {
background: "var(--surface-info-base)",
foreground: "var(--text-base)",
}
}
type SessionTabs = {
active?: string
all: string[]
}
export type LocalProject = Partial<Project> & { worktree: string; expanded: boolean }
export const { use: useLayout, provider: LayoutProvider } = createSimpleContext({
name: "Layout",
init: () => {
const globalSdk = useGlobalSDK()
const globalSync = useGlobalSync()
const [store, setStore, _, ready] = persisted(
"layout.v3",
createStore({
projects: [] as { worktree: string; expanded: boolean }[],
sidebar: {
opened: false,
width: 280,
},
terminal: {
opened: false,
height: 280,
},
review: {
opened: true,
},
session: {
width: 600,
},
sessionTabs: {} as Record<string, SessionTabs>,
}),
)
const usedColors = new Set<AvatarColorKey>()
function pickAvailableColor(): AvatarColorKey {
const available = AVATAR_COLOR_KEYS.filter((c) => !usedColors.has(c))
if (available.length === 0) return AVATAR_COLOR_KEYS[Math.floor(Math.random() * AVATAR_COLOR_KEYS.length)]
return available[Math.floor(Math.random() * available.length)]
}
function enrich(project: { worktree: string; expanded: boolean }) {
const metadata = globalSync.data.project.find((x) => x.worktree === project.worktree)
return [
{
...project,
...(metadata ?? {}),
},
]
}
function colorize(project: LocalProject) {
if (project.icon?.color) return project
const color = pickAvailableColor()
usedColors.add(color)
project.icon = { ...project.icon, color }
if (project.id) {
globalSdk.client.project.update({ projectID: project.id, icon: { color } })
}
return project
}
const enriched = createMemo(() => store.projects.flatMap(enrich))
const list = createMemo(() => enriched().flatMap(colorize))
onMount(() => {
Promise.all(
store.projects.map((project) => {
return globalSync.project.loadSessions(project.worktree)
}),
)
})
return {
ready,
projects: {
list,
open(directory: string) {
if (store.projects.find((x) => x.worktree === directory)) {
return
}
globalSync.project.loadSessions(directory)
setStore("projects", (x) => [{ worktree: directory, expanded: true }, ...x])
},
close(directory: string) {
setStore("projects", (x) => x.filter((x) => x.worktree !== directory))
},
expand(directory: string) {
const index = store.projects.findIndex((x) => x.worktree === directory)
if (index !== -1) setStore("projects", index, "expanded", true)
},
collapse(directory: string) {
const index = store.projects.findIndex((x) => x.worktree === directory)
if (index !== -1) setStore("projects", index, "expanded", false)
},
move(directory: string, toIndex: number) {
setStore("projects", (projects) => {
const fromIndex = projects.findIndex((x) => x.worktree === directory)
if (fromIndex === -1 || fromIndex === toIndex) return projects
const result = [...projects]
const [item] = result.splice(fromIndex, 1)
result.splice(toIndex, 0, item)
return result
})
},
},
sidebar: {
opened: createMemo(() => store.sidebar.opened),
open() {
setStore("sidebar", "opened", true)
},
close() {
setStore("sidebar", "opened", false)
},
toggle() {
setStore("sidebar", "opened", (x) => !x)
},
width: createMemo(() => store.sidebar.width),
resize(width: number) {
setStore("sidebar", "width", width)
},
},
terminal: {
opened: createMemo(() => store.terminal.opened),
open() {
setStore("terminal", "opened", true)
},
close() {
setStore("terminal", "opened", false)
},
toggle() {
setStore("terminal", "opened", (x) => !x)
},
height: createMemo(() => store.terminal.height),
resize(height: number) {
setStore("terminal", "height", height)
},
},
review: {
opened: createMemo(() => store.review?.opened ?? true),
open() {
setStore("review", "opened", true)
},
close() {
setStore("review", "opened", false)
},
toggle() {
setStore("review", "opened", (x) => !x)
},
},
session: {
width: createMemo(() => store.session?.width ?? 600),
resize(width: number) {
if (!store.session) {
setStore("session", { width })
} else {
setStore("session", "width", width)
}
},
},
tabs(sessionKey: string) {
const tabs = createMemo(() => store.sessionTabs[sessionKey] ?? { all: [] })
return {
tabs,
active: createMemo(() => tabs().active),
all: createMemo(() => tabs().all),
setActive(tab: string | undefined) {
if (!store.sessionTabs[sessionKey]) {
setStore("sessionTabs", sessionKey, { all: [], active: tab })
} else {
setStore("sessionTabs", sessionKey, "active", tab)
}
},
setAll(all: string[]) {
if (!store.sessionTabs[sessionKey]) {
setStore("sessionTabs", sessionKey, { all, active: undefined })
} else {
setStore("sessionTabs", sessionKey, "all", all)
}
},
async open(tab: string) {
const current = store.sessionTabs[sessionKey] ?? { all: [] }
if (tab !== "review") {
if (!current.all.includes(tab)) {
if (!store.sessionTabs[sessionKey]) {
setStore("sessionTabs", sessionKey, { all: [tab], active: tab })
} else {
setStore("sessionTabs", sessionKey, "all", [...current.all, tab])
setStore("sessionTabs", sessionKey, "active", tab)
}
return
}
}
if (!store.sessionTabs[sessionKey]) {
setStore("sessionTabs", sessionKey, { all: [], active: tab })
} else {
setStore("sessionTabs", sessionKey, "active", tab)
}
},
close(tab: string) {
const current = store.sessionTabs[sessionKey]
if (!current) return
batch(() => {
setStore(
"sessionTabs",
sessionKey,
"all",
current.all.filter((x) => x !== tab),
)
if (current.active === tab) {
const index = current.all.findIndex((f) => f === tab)
const previous = current.all[Math.max(0, index - 1)]
setStore("sessionTabs", sessionKey, "active", previous)
}
})
},
move(tab: string, to: number) {
const current = store.sessionTabs[sessionKey]
if (!current) return
const index = current.all.findIndex((f) => f === tab)
if (index === -1) return
setStore(
"sessionTabs",
sessionKey,
"all",
produce((opened) => {
opened.splice(to, 0, opened.splice(index, 1)[0])
}),
)
},
}
},
}
},
})
@@ -1,11 +1,15 @@
import { createStore, produce, reconcile } from "solid-js/store"
import { batch, createEffect, createMemo } from "solid-js"
import { uniqueBy } from "remeda"
import type { FileContent, FileNode, Model, Provider, File as FileStatus } from "@opencode-ai/sdk"
import { batch, createMemo } from "solid-js"
import { filter, firstBy, flat, groupBy, mapValues, pipe, uniqueBy, values } from "remeda"
import type { FileContent, FileNode, Model, Provider, File as FileStatus } from "@opencode-ai/sdk/v2"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { useSDK } from "./sdk"
import { useSync } from "./sync"
import { base64Encode } from "@/utils"
import { base64Encode } from "@opencode-ai/util/encode"
import { useProviders } from "@/hooks/use-providers"
import { DateTime } from "luxon"
import { persisted } from "@/utils/persist"
import { showToast } from "@opencode-ai/ui/toast"
export type LocalFile = FileNode &
Partial<{
@@ -25,6 +29,7 @@ export type View = LocalFile["view"]
export type LocalModel = Omit<Model, "provider"> & {
provider: Provider
latest?: boolean
}
export type ModelKey = { providerID: string; modelID: string }
@@ -36,10 +41,17 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
init: () => {
const sdk = useSDK()
const sync = useSync()
const providers = useProviders()
function isModelValid(model: ModelKey) {
const provider = sync.data.provider.find((x) => x.id === model.providerID)
return !!provider?.models[model.modelID]
const provider = providers.all().find((x) => x.id === model.providerID)
return (
!!provider?.models[model.modelID] &&
providers
.connected()
.map((p) => p.id)
.includes(model.providerID)
)
}
function getFirstValidModel(...modelFns: (() => ModelKey | undefined)[]) {
@@ -50,26 +62,8 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
}
}
// Automatically update model when agent changes
createEffect(() => {
const value = agent.current()
if (value.model) {
if (isModelValid(value.model))
model.set({
providerID: value.model.providerID,
modelID: value.model.modelID,
})
// else
// toast.show({
// type: "warning",
// message: `Agent ${value.name}'s configured model ${value.model.providerID}/${value.model.modelID} is not valid`,
// duration: 3000,
// })
}
})
const agent = (() => {
const list = createMemo(() => sync.data.agent.filter((x) => x.mode !== "subagent"))
const list = createMemo(() => sync.data.agent.filter((x) => x.mode !== "subagent" && !x.hidden))
const [store, setStore] = createStore<{
current: string
}>({
@@ -99,23 +93,62 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
})()
const model = (() => {
const [store, setStore] = createStore<{
const [store, setStore, _, modelReady] = persisted(
"model.v1",
createStore<{
user: (ModelKey & { visibility: "show" | "hide"; favorite?: boolean })[]
recent: ModelKey[]
}>({
user: [],
recent: [],
}),
)
const [ephemeral, setEphemeral] = createStore<{
model: Record<string, ModelKey>
recent: ModelKey[]
}>({
model: {},
recent: [],
})
const value = localStorage.getItem("model")
setStore("recent", JSON.parse(value ?? "[]"))
createEffect(() => {
localStorage.setItem("model", JSON.stringify(store.recent))
})
const available = createMemo(() =>
providers.connected().flatMap((p) =>
Object.values(p.models).map((m) => ({
...m,
provider: p,
})),
),
)
const latest = createMemo(() =>
pipe(
available(),
filter((x) => Math.abs(DateTime.fromISO(x.release_date).diffNow().as("months")) < 6),
groupBy((x) => x.provider.id),
mapValues((models) =>
pipe(
models,
groupBy((x) => x.family),
values(),
(groups) =>
groups.flatMap((g) => {
const first = firstBy(g, [(x) => x.release_date, "desc"])
return first ? [{ modelID: first.id, providerID: first.provider.id }] : []
}),
),
),
values(),
flat(),
),
)
const list = createMemo(() =>
sync.data.provider.flatMap((p) => Object.values(p.models).map((m) => ({ ...m, provider: p }) as LocalModel)),
available().map((m) => ({
...m,
name: m.name.replace("(latest)", "").trim(),
latest: m.name.includes("(latest)"),
})),
)
const find = (key: ModelKey) => list().find((m) => m.id === key?.modelID && m.provider.id === key.providerID)
const fallbackModel = createMemo(() => {
@@ -134,18 +167,23 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
return item
}
}
const provider = sync.data.provider[0]
const model = Object.values(provider.models)[0]
return {
providerID: provider.id,
modelID: model.id,
for (const p of providers.connected()) {
if (p.id in providers.default()) {
return {
providerID: p.id,
modelID: providers.default()[p.id],
}
}
}
throw new Error("No default model found")
})
const currentModel = createMemo(() => {
const current = createMemo(() => {
const a = agent.current()
const key = getFirstValidModel(
() => store.model[a.name],
() => ephemeral.model[a.name],
() => a.model,
fallbackModel,
)!
@@ -156,10 +194,12 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const cycle = (direction: 1 | -1) => {
const recentList = recent()
const current = currentModel()
if (!current) return
const currentModel = current()
if (!currentModel) return
const index = recentList.findIndex((x) => x?.provider.id === current.provider.id && x?.id === current.id)
const index = recentList.findIndex(
(x) => x?.provider.id === currentModel.provider.id && x?.id === currentModel.id,
)
if (index === -1) return
let next = index + direction
@@ -175,14 +215,25 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
})
}
function updateVisibility(model: ModelKey, visibility: "show" | "hide") {
const index = store.user.findIndex((x) => x.modelID === model.modelID && x.providerID === model.providerID)
if (index >= 0) {
setStore("user", index, { visibility })
} else {
setStore("user", store.user.length, { ...model, visibility })
}
}
return {
current: currentModel,
ready: modelReady,
current,
recent,
list,
cycle,
set(model: ModelKey | undefined, options?: { recent?: boolean }) {
batch(() => {
setStore("model", agent.current().name, model ?? fallbackModel())
setEphemeral("model", agent.current().name, model ?? fallbackModel())
if (model) updateVisibility(model, "show")
if (options?.recent && model) {
const uniq = uniqueBy([model, ...store.recent], (x) => x.providerID + x.modelID)
if (uniq.length > 5) uniq.pop()
@@ -190,6 +241,17 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
}
})
},
visible(model: ModelKey) {
const user = store.user.find((x) => x.modelID === model.modelID && x.providerID === model.providerID)
return (
user?.visibility !== "hide" &&
(latest().find((x) => x.modelID === model.modelID && x.providerID === model.providerID) ||
user?.visibility === "show")
)
},
setVisibility(model: ModelKey, visible: boolean) {
updateVisibility(model, visible ? "show" : "hide")
},
}
})()
@@ -197,11 +259,11 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const [store, setStore] = createStore<{
node: Record<string, LocalFile>
}>({
node: Object.fromEntries(sync.data.node.map((x) => [x.path, x])),
node: {}, // Object.fromEntries(sync.data.node.map((x) => [x.path, x])),
})
const changeset = createMemo(() => new Set(sync.data.changes.map((f) => f.path)))
const changes = createMemo(() => Array.from(changeset()).sort((a, b) => a.localeCompare(b)))
// const changeset = createMemo(() => new Set(sync.data.changes.map((f) => f.path)))
// const changes = createMemo(() => Array.from(changeset()).sort((a, b) => a.localeCompare(b)))
// createEffect((prev: FileStatus[]) => {
// const removed = prev.filter((p) => !sync.data.changes.find((c) => c.path === p.path))
@@ -229,16 +291,16 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
// return sync.data.changes
// }, sync.data.changes)
const changed = (path: string) => {
const node = store.node[path]
if (node?.status) return true
const set = changeset()
if (set.has(path)) return true
for (const p of set) {
if (p.startsWith(path ? path + "/" : "")) return true
}
return false
}
// const changed = (path: string) => {
// const node = store.node[path]
// if (node?.status) return true
// const set = changeset()
// if (set.has(path)) return true
// for (const p of set) {
// if (p.startsWith(path ? path + "/" : "")) return true
// }
// return false
// }
// const resetNode = (path: string) => {
// setStore("node", path, {
@@ -257,16 +319,26 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const load = async (path: string) => {
const relativePath = relative(path)
sdk.client.file.read({ query: { path: relativePath } }).then((x) => {
setStore(
"node",
relativePath,
produce((draft) => {
draft.loaded = true
draft.content = x.data
}),
)
})
await sdk.client.file
.read({ path: relativePath })
.then((x) => {
if (!store.node[relativePath]) return
setStore(
"node",
relativePath,
produce((draft) => {
draft.loaded = true
draft.content = x.data
}),
)
})
.catch((e) => {
showToast({
variant: "error",
title: "Failed to load file",
description: e.message,
})
})
}
const fetch = async (path: string) => {
@@ -280,7 +352,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const init = async (path: string) => {
const relativePath = relative(path)
if (!store.node[relativePath]) await fetch(path)
if (store.node[relativePath].loaded) return
if (store.node[relativePath]?.loaded) return
return load(relativePath)
}
@@ -300,28 +372,30 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
context.addActive()
if (options?.pinned) setStore("node", path, "pinned", true)
if (options?.view && store.node[relativePath].view === undefined) setStore("node", path, "view", options.view)
if (store.node[relativePath].loaded) return
if (store.node[relativePath]?.loaded) return
return load(relativePath)
}
const list = async (path: string) => {
return sdk.client.file.list({ query: { path: path + "/" } }).then((x) => {
setStore(
"node",
produce((draft) => {
x.data!.forEach((node) => {
if (node.path in draft) return
draft[node.path] = node
})
}),
)
})
return sdk.client.file
.list({ path: path + "/" })
.then((x) => {
setStore(
"node",
produce((draft) => {
x.data!.forEach((node) => {
if (node.path in draft) return
draft[node.path] = node
})
}),
)
})
.catch(() => {})
}
const searchFiles = (query: string) =>
sdk.client.find.files({ query: { query, dirs: "false" } }).then((x) => x.data!)
const searchFiles = (query: string) => sdk.client.find.files({ query, dirs: "false" }).then((x) => x.data!)
const searchFilesAndDirectories = (query: string) =>
sdk.client.find.files({ query: { query, dirs: "true" } }).then((x) => x.data!)
sdk.client.find.files({ query, dirs: "true" }).then((x) => x.data!)
sdk.event.listen((e) => {
const event = e.details
@@ -329,14 +403,14 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
case "file.watcher.updated":
const relativePath = relative(event.properties.file)
if (relativePath.startsWith(".git/")) return
load(relativePath)
if (store.node[relativePath]) load(relativePath)
break
}
})
return {
node: async (path: string) => {
if (!store.node[path]) {
if (!store.node[path] || !store.node[path].loaded) {
await init(path)
}
return store.node[path]
@@ -347,7 +421,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
init,
expand(path: string) {
setStore("node", path, "expanded", true)
if (store.node[path].loaded) return
if (store.node[path]?.loaded) return
setStore("node", path, "loaded", true)
list(path)
},
@@ -387,8 +461,8 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
setChangeIndex(path: string, index: number | undefined) {
setStore("node", path, "selectedChange", index)
},
changes,
changed,
// changes,
// changed,
children(path: string) {
return Object.values(store.node).filter(
(x) =>
+127
View File
@@ -0,0 +1,127 @@
import { createStore } from "solid-js/store"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { useGlobalSDK } from "./global-sdk"
import { useGlobalSync } from "./global-sync"
import { Binary } from "@opencode-ai/util/binary"
import { EventSessionError } from "@opencode-ai/sdk/v2"
import { makeAudioPlayer } from "@solid-primitives/audio"
import idleSound from "@opencode-ai/ui/audio/staplebops-01.aac"
import errorSound from "@opencode-ai/ui/audio/nope-03.aac"
import { persisted } from "@/utils/persist"
type NotificationBase = {
directory?: string
session?: string
metadata?: any
time: number
viewed: boolean
}
type TurnCompleteNotification = NotificationBase & {
type: "turn-complete"
}
type ErrorNotification = NotificationBase & {
type: "error"
error: EventSessionError["properties"]["error"]
}
export type Notification = TurnCompleteNotification | ErrorNotification
export const { use: useNotification, provider: NotificationProvider } = createSimpleContext({
name: "Notification",
init: () => {
let idlePlayer: ReturnType<typeof makeAudioPlayer> | undefined
let errorPlayer: ReturnType<typeof makeAudioPlayer> | undefined
try {
idlePlayer = makeAudioPlayer(idleSound)
errorPlayer = makeAudioPlayer(errorSound)
} catch (err) {
console.log("Failed to load audio", err)
}
const globalSDK = useGlobalSDK()
const globalSync = useGlobalSync()
const [store, setStore, _, ready] = persisted(
"notification.v1",
createStore({
list: [] as Notification[],
}),
)
globalSDK.event.listen((e) => {
const directory = e.name
const event = e.details
const base = {
directory,
time: Date.now(),
viewed: false,
}
switch (event.type) {
case "session.idle": {
const sessionID = event.properties.sessionID
const [syncStore] = globalSync.child(directory)
const match = Binary.search(syncStore.session, sessionID, (s) => s.id)
const isChild = match.found && syncStore.session[match.index].parentID
if (isChild) break
try {
idlePlayer?.play()
} catch {}
setStore("list", store.list.length, {
...base,
type: "turn-complete",
session: sessionID,
})
break
}
case "session.error": {
const sessionID = event.properties.sessionID
if (sessionID) {
const [syncStore] = globalSync.child(directory)
const match = Binary.search(syncStore.session, sessionID, (s) => s.id)
const isChild = match.found && syncStore.session[match.index].parentID
if (isChild) break
}
try {
errorPlayer?.play()
} catch {}
setStore("list", store.list.length, {
...base,
type: "error",
session: sessionID ?? "global",
error: "error" in event.properties ? event.properties.error : undefined,
})
break
}
}
})
return {
ready,
session: {
all(session: string) {
return store.list.filter((n) => n.session === session)
},
unseen(session: string) {
return store.list.filter((n) => n.session === session && !n.viewed)
},
markViewed(session: string) {
setStore("list", (n) => n.session === session, "viewed", true)
},
},
project: {
all(directory: string) {
return store.list.filter((n) => n.directory === directory)
},
unseen(directory: string) {
return store.list.filter((n) => n.directory === directory && !n.viewed)
},
markViewed(directory: string) {
setStore("list", (n) => n.directory === directory, "viewed", true)
},
},
}
},
})
+44
View File
@@ -0,0 +1,44 @@
import { createSimpleContext } from "@opencode-ai/ui/context"
import { AsyncStorage, SyncStorage } from "@solid-primitives/storage"
export type Platform = {
/** Platform discriminator */
platform: "web" | "tauri"
/** App version */
version?: string
/** Open a URL in the default browser */
openLink(url: string): void
/** Restart the app */
restart(): Promise<void>
/** Open native directory picker dialog (Tauri only) */
openDirectoryPickerDialog?(opts?: { title?: string; multiple?: boolean }): Promise<string | string[] | null>
/** Open native file picker dialog (Tauri only) */
openFilePickerDialog?(opts?: { title?: string; multiple?: boolean }): Promise<string | string[] | null>
/** Save file picker dialog (Tauri only) */
saveFilePickerDialog?(opts?: { title?: string; defaultPath?: string }): Promise<string | null>
/** Storage mechanism, defaults to localStorage */
storage?: (name?: string) => SyncStorage | AsyncStorage
/** Check for updates (Tauri only) */
checkUpdate?(): Promise<{ updateAvailable: boolean; version?: string }>
/** Install updates (Tauri only) */
update?(): Promise<void>
/** Fetch override */
fetch?: typeof fetch
}
export const { use: usePlatform, provider: PlatformProvider } = createSimpleContext({
name: "Platform",
init: (props: { value: Platform }) => {
return props.value
},
})
+111
View File
@@ -0,0 +1,111 @@
import { createStore } from "solid-js/store"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { batch, createMemo } from "solid-js"
import { useParams } from "@solidjs/router"
import { TextSelection } from "./local"
import { persisted } from "@/utils/persist"
interface PartBase {
content: string
start: number
end: number
}
export interface TextPart extends PartBase {
type: "text"
}
export interface FileAttachmentPart extends PartBase {
type: "file"
path: string
selection?: TextSelection
}
export interface ImageAttachmentPart {
type: "image"
id: string
filename: string
mime: string
dataUrl: string
}
export type ContentPart = TextPart | FileAttachmentPart | ImageAttachmentPart
export type Prompt = ContentPart[]
export const DEFAULT_PROMPT: Prompt = [{ type: "text", content: "", start: 0, end: 0 }]
export function isPromptEqual(promptA: Prompt, promptB: Prompt): boolean {
if (promptA.length !== promptB.length) return false
for (let i = 0; i < promptA.length; i++) {
const partA = promptA[i]
const partB = promptB[i]
if (partA.type !== partB.type) return false
if (partA.type === "text" && partA.content !== (partB as TextPart).content) {
return false
}
if (partA.type === "file" && partA.path !== (partB as FileAttachmentPart).path) {
return false
}
if (partA.type === "image" && partA.id !== (partB as ImageAttachmentPart).id) {
return false
}
}
return true
}
function cloneSelection(selection?: TextSelection) {
if (!selection) return undefined
return { ...selection }
}
function clonePart(part: ContentPart): ContentPart {
if (part.type === "text") return { ...part }
if (part.type === "image") return { ...part }
return {
...part,
selection: cloneSelection(part.selection),
}
}
function clonePrompt(prompt: Prompt): Prompt {
return prompt.map(clonePart)
}
export const { use: usePrompt, provider: PromptProvider } = createSimpleContext({
name: "Prompt",
init: () => {
const params = useParams()
const name = createMemo(() => `${params.dir}/prompt${params.id ? "/" + params.id : ""}.v1`)
const [store, setStore, _, ready] = persisted(
name(),
createStore<{
prompt: Prompt
cursor?: number
}>({
prompt: clonePrompt(DEFAULT_PROMPT),
cursor: undefined,
}),
)
return {
ready,
current: createMemo(() => store.prompt),
cursor: createMemo(() => store.cursor),
dirty: createMemo(() => !isPromptEqual(store.prompt, DEFAULT_PROMPT)),
set(prompt: Prompt, cursorPosition?: number) {
const next = clonePrompt(prompt)
batch(() => {
setStore("prompt", next)
if (cursorPosition !== undefined) setStore("cursor", cursorPosition)
})
},
reset() {
batch(() => {
setStore("prompt", clonePrompt(DEFAULT_PROMPT))
setStore("cursor", 0)
})
},
}
},
})
@@ -1,18 +1,20 @@
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/client"
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { createGlobalEmitter } from "@solid-primitives/event-bus"
import { onCleanup } from "solid-js"
import { useGlobalSDK } from "./global-sdk"
import { usePlatform } from "./platform"
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
name: "SDK",
init: (props: { directory: string }) => {
const platform = usePlatform()
const globalSDK = useGlobalSDK()
const abort = new AbortController()
const sdk = createOpencodeClient({
baseUrl: globalSDK.url,
signal: abort.signal,
signal: AbortSignal.timeout(1000 * 60 * 10),
fetch: platform.fetch,
directory: props.directory,
throwOnError: true,
})
const emitter = createGlobalEmitter<{
@@ -23,10 +25,6 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
emitter.emit(event.type, event)
})
onCleanup(() => {
abort.abort()
})
return { directory: props.directory, client: sdk, event: emitter }
return { directory: props.directory, client: sdk, event: emitter, url: globalSDK.url }
},
})
+114
View File
@@ -0,0 +1,114 @@
import { produce } from "solid-js/store"
import { createMemo } from "solid-js"
import { Binary } from "@opencode-ai/util/binary"
import { retry } from "@opencode-ai/util/retry"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { useGlobalSync } from "./global-sync"
import { useSDK } from "./sdk"
import type { Message, Part } from "@opencode-ai/sdk/v2/client"
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
name: "Sync",
init: () => {
const globalSync = useGlobalSync()
const sdk = useSDK()
const [store, setStore] = globalSync.child(sdk.directory)
const absolute = (path: string) => (store.path.directory + "/" + path).replace("//", "/")
return {
data: store,
set: setStore,
get ready() {
return store.ready
},
get project() {
const match = Binary.search(globalSync.data.project, store.project, (p) => p.id)
if (match.found) return globalSync.data.project[match.index]
return undefined
},
session: {
get(sessionID: string) {
const match = Binary.search(store.session, sessionID, (s) => s.id)
if (match.found) return store.session[match.index]
return undefined
},
addOptimisticMessage(input: {
sessionID: string
messageID: string
parts: Part[]
agent: string
model: { providerID: string; modelID: string }
}) {
const message: Message = {
id: input.messageID,
sessionID: input.sessionID,
role: "user",
time: { created: Date.now() },
agent: input.agent,
model: input.model,
}
setStore(
produce((draft) => {
const messages = draft.message[input.sessionID]
if (!messages) {
draft.message[input.sessionID] = [message]
} else {
const result = Binary.search(messages, input.messageID, (m) => m.id)
messages.splice(result.index, 0, message)
}
draft.part[input.messageID] = input.parts.slice()
}),
)
},
async sync(sessionID: string, _isRetry = false) {
const [session, messages, todo, diff] = await Promise.all([
retry(() => sdk.client.session.get({ sessionID })),
retry(() => sdk.client.session.messages({ sessionID, limit: 100 })),
retry(() => sdk.client.session.todo({ sessionID })),
retry(() => sdk.client.session.diff({ sessionID })),
])
setStore(
produce((draft) => {
const match = Binary.search(draft.session, sessionID, (s) => s.id)
if (match.found) draft.session[match.index] = session.data!
if (!match.found) draft.session.splice(match.index, 0, session.data!)
draft.todo[sessionID] = todo.data ?? []
draft.message[sessionID] = messages
.data!.map((x) => x.info)
.slice()
.sort((a, b) => a.id.localeCompare(b.id))
for (const message of messages.data!) {
draft.part[message.info.id] = message.parts.slice().sort((a, b) => a.id.localeCompare(b.id))
}
draft.session_diff[sessionID] = diff.data ?? []
}),
)
},
fetch: async (count = 10) => {
setStore("limit", (x) => x + count)
await sdk.client.session.list().then((x) => {
const sessions = (x.data ?? [])
.slice()
.sort((a, b) => a.id.localeCompare(b.id))
.slice(0, store.limit)
setStore("session", sessions)
})
},
more: createMemo(() => store.session.length >= store.limit),
archive: async (sessionID: string) => {
await sdk.client.session.update({ sessionID, time: { archived: Date.now() } })
setStore(
produce((draft) => {
const match = Binary.search(draft.session, sessionID, (s) => s.id)
if (match.found) draft.session.splice(match.index, 1)
}),
)
},
},
absolute,
get directory() {
return store.path.directory
},
}
},
})
+121
View File
@@ -0,0 +1,121 @@
import { createStore, produce } from "solid-js/store"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { batch, createMemo } from "solid-js"
import { useParams } from "@solidjs/router"
import { useSDK } from "./sdk"
import { persisted } from "@/utils/persist"
export type LocalPTY = {
id: string
title: string
rows?: number
cols?: number
buffer?: string
scrollY?: number
}
export const { use: useTerminal, provider: TerminalProvider } = createSimpleContext({
name: "Terminal",
init: () => {
const sdk = useSDK()
const params = useParams()
const name = createMemo(() => `${params.dir}/terminal${params.id ? "/" + params.id : ""}.v1`)
const [store, setStore, _, ready] = persisted(
name(),
createStore<{
active?: string
all: LocalPTY[]
}>({
all: [],
}),
)
return {
ready,
all: createMemo(() => Object.values(store.all)),
active: createMemo(() => store.active),
new() {
sdk.client.pty
.create({ title: `Terminal ${store.all.length + 1}` })
.then((pty) => {
const id = pty.data?.id
if (!id) return
setStore("all", [
...store.all,
{
id,
title: pty.data?.title ?? "Terminal",
},
])
setStore("active", id)
})
.catch((e) => {
console.error("Failed to create terminal", e)
})
},
update(pty: Partial<LocalPTY> & { id: string }) {
setStore("all", (x) => x.map((x) => (x.id === pty.id ? { ...x, ...pty } : x)))
sdk.client.pty
.update({
ptyID: pty.id,
title: pty.title,
size: pty.cols && pty.rows ? { rows: pty.rows, cols: pty.cols } : undefined,
})
.catch((e) => {
console.error("Failed to update terminal", e)
})
},
async clone(id: string) {
const index = store.all.findIndex((x) => x.id === id)
const pty = store.all[index]
if (!pty) return
const clone = await sdk.client.pty
.create({
title: pty.title,
})
.catch((e) => {
console.error("Failed to clone terminal", e)
return undefined
})
if (!clone?.data) return
setStore("all", index, {
...pty,
...clone.data,
})
if (store.active === pty.id) {
setStore("active", clone.data.id)
}
},
open(id: string) {
setStore("active", id)
},
async close(id: string) {
batch(() => {
setStore(
"all",
store.all.filter((x) => x.id !== id),
)
if (store.active === id) {
const index = store.all.findIndex((f) => f.id === id)
const previous = store.all[Math.max(0, index - 1)]
setStore("active", previous?.id)
}
})
await sdk.client.pty.remove({ ptyID: id }).catch((e) => {
console.error("Failed to close terminal", e)
})
},
move(id: string, to: number) {
const index = store.all.findIndex((f) => f.id === id)
if (index === -1) return
setStore(
"all",
produce((all) => {
all.splice(to, 0, all.splice(index, 1)[0])
}),
)
},
}
},
})

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