Compare commits

...

976 Commits

Author SHA1 Message Date
opencode 7e2c543744 release: v1.0.23 2025-11-04 19:38:18 +00:00
Timo Clasen 2060182b25 feat(TUI): add autocomplete readline style keybinds (#3717)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-11-04 13:28:03 -06:00
Aiden Cline 3010a2212b fix: stop showing auto complete if user types a space 2025-11-04 13:22:18 -06:00
Dax Raad a122c9c1e1 tui: add ability to interrupt running sessions from command palette 2025-11-04 14:07:22 -05:00
opencode ef39bf549a release: v1.0.22 2025-11-04 18:41:38 +00:00
Dax Raad 643352d20e fix session performance issue from large diffs 2025-11-04 13:35:44 -05:00
opencode 4ca50dda89 release: v1.0.21 2025-11-04 17:47:34 +00:00
Sebastian Herrlinger 3479a5d39f Revert "upgrade opentui to address disappearing content issues #3776, #3697"
This reverts commit a21cf346d7.
2025-11-04 18:38:44 +01:00
Dax Raad f1bc996cf5 local web 2025-11-04 12:33:14 -05:00
Aiden Cline bd69345e7f respect: disable_paste_summary 2025-11-04 11:29:12 -06:00
Aiden Cline 464709c69c fix: /undo command 2025-11-04 10:59:34 -06:00
Christian Stewart 57dc67f27a fix(tui): worker path resolution in dev mode (#3778)
Signed-off-by: Christian Stewart <christian@cjs.zip>
Co-authored-by: Sebastian Herrlinger <hasta84@gmail.com>
2025-11-04 17:38:11 +01:00
Err 20a17386d1 fix: support scoped npm plugins (#3785)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-11-04 09:15:01 -06:00
Pranshu Raj f214076457 [FIX]: Refocus prompt after session delete (#3882) 2025-11-04 08:57:59 -06:00
frankdierolf 098cc000fe fix: correct history_next keybinding description (#3891) 2025-11-04 08:52:49 -06:00
arc-source-coder 05615b2929 tui: make /mcp an alias for /status (#3894) 2025-11-04 08:52:28 -06:00
Sebastian Herrlinger a21cf346d7 upgrade opentui to address disappearing content issues #3776, #3697 2025-11-04 14:57:15 +01:00
Adam d7bf177aa9 fix: lander specs 2025-11-04 06:15:12 -06:00
GitHub Action 86944cf792 ignore: update download stats 2025-11-04 2025-11-04 12:05:02 +00:00
Aiden Cline 7a45bf4e77 update brew handling 2025-11-04 00:52:26 -06:00
opencode 436640374e release: v1.0.20 2025-11-04 05:56:24 +00:00
Dax Raad abe25eda0e tui: fix tool permission lookup to use correct session ID 2025-11-04 00:50:12 -05:00
Dax Raad 9ebdccea03 fix: persist -m model when switching agents
Add initial model from command line to fallback chain so it persists
when switching agents with tab, matching behavior of config model.

Resolves #3863
2025-11-03 23:51:52 -05:00
Aiden Cline 03539a049f fix: piping 2025-11-03 22:36:10 -06:00
Dax Raad da1670a0c7 fix: handle parsePatch errors in TUI to prevent crashes
Wrap parsePatch calls in try-catch blocks to gracefully handle malformed
diffs that can occur when undoing after tool_use/tool_result errors or
cancelled prompts. Prevents TUI from crashing with 'Added line count did not
match for hunk' error.

Fixes #3700
2025-11-03 23:36:04 -05:00
Greg Pstrucha 30f3f7f6c8 Fix file tagging in multi line inputs (#3865) 2025-11-03 23:22:38 -05:00
Aiden Cline c5f902d7a3 ignore: delete unused code 2025-11-04 04:01:22 +00:00
Stephen Collings bbd2f1cebd fix: Provide OPENCODE & AGENT env vars (#3843)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-11-04 04:01:22 +00:00
Dax Raad 8c46a89732 tui: use keybind helper for history navigation to respect custom keybindings 2025-11-04 04:01:22 +00:00
opencode 7663b23420 release: v1.0.19 2025-11-04 04:01:22 +00:00
Dax Raad 3bd0c0899f tui: fix model selection for models with nested paths
Users can now select models with multiple slashes like 'openrouter/google/gemini-2.5-pro'
in the TUI. Previously the TUI would only parse the first two parts of the model
path, showing 'Invalid model openrouter/google' for nested models.
2025-11-03 19:52:43 -05:00
kaanmertkoc 6d17e71892 feat: implement stats command (#3832) 2025-11-03 18:41:30 -06:00
Mikhail Wahib baa9ef9e10 fix: print the modified keybind for command_list (#3859) 2025-11-03 18:29:23 -06:00
Jay V 3d17962fa2 ignore: lander 2025-11-03 18:17:32 -05:00
Frank 94b897eb5d wip: zen 2025-11-03 17:30:18 -05:00
Adi Yeroslav e949513444 fix: update logo (#3833) 2025-11-03 16:18:38 -06:00
Aiden Cline 0591f2f656 ci: auto label web 2025-11-03 16:17:53 -06:00
Dax Raad d943db71db fix: correct dirs parameter type in file search 2025-11-03 17:10:31 -05:00
Dax Raad 1fedcaf7ae tui: remove duplicate copy message command entry 2025-11-03 17:10:20 -05:00
Dax Raad 9076376ba6 ci: stuff 2025-11-03 22:01:30 +00:00
opencode b160abc23f release: v1.0.18 2025-11-03 22:01:30 +00:00
Dax Raad bf0997d2ea ci: tweak 2025-11-03 16:56:41 -05:00
Dax Raad 140189f0f2 tui: add copy last assistant message to session menu 2025-11-03 16:47:18 -05:00
Adam dda35712ba wip: desktop work 2025-11-03 15:42:39 -06:00
Adam 47a44d398b wip: desktop work 2025-11-03 15:42:10 -06:00
Adam 9bebcd594a wip: desktop work 2025-11-03 15:42:10 -06:00
Dax Raad 93da43ae2d fix dirs query param 2025-11-03 16:35:55 -05:00
Timo Clasen e7994ae57b feat(TUI): don't show /share hint if sharing is disabled (#3835) 2025-11-03 15:30:55 -06:00
Adi Yeroslav e7b753c659 fix: session rename functionality (#3840) 2025-11-03 15:26:30 -06:00
Dmytro Tiapukhin 1439d9f32a fix: better mcp sanitization (#3842)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-11-03 15:19:49 -06:00
Dax Raad 998576e6bc tui: add support for Ctrl+_ key combination in keybind parser 2025-11-03 16:16:09 -05:00
opencode 25ad7aa318 release: v1.0.17 2025-11-03 21:14:52 +00:00
Dax Raad dc8cda7911 web command 2025-11-03 16:10:23 -05:00
Aiden Cline 73c7b35ad7 fix: mcp error (#3847) 2025-11-03 15:04:53 -06:00
Frank 3aeb372df5 zen: wip 2025-11-03 15:44:06 -05:00
Tyler Gannon 13fcf0dd95 fix: wait for stdout to flush in generate command (#3821) 2025-11-03 14:05:48 -06:00
Frank 2245714cd2 zen: filter out alpha models 2025-11-03 15:04:59 -05:00
opencode 019948fdfb release: v1.0.16 2025-11-03 17:08:32 +00:00
Dax Raad dcbfa7ff86 regen sdk 2025-11-03 11:55:19 -05:00
Dax Raad c12135adde core: add optional dirs parameter to file search API
Allow users to exclude directories from file search results by setting dirs=false parameter in /find/file endpoint
2025-11-03 11:53:41 -05:00
Frank e31e3d0a1a zen: wip 2025-11-03 11:30:53 -05:00
Adam 5f4985c4c2 wip: desktop work 2025-11-03 08:29:13 -06:00
Adam 8cb10cafd5 wip: desktop work 2025-11-03 08:29:13 -06:00
James Alexander 49d6c7e146 Add unit tests for util functions: iife, lazy, timeout (#3791) 2025-11-03 09:24:45 -05:00
Haris Gušić 13c06b20b8 fix(tui): Show correct keybind in session delete confirmation message (#3805) 2025-11-03 09:22:05 -05:00
Alex Knight 418d2d82bd Log share link immediately after session creation (#3811) 2025-11-03 09:21:43 -05:00
frankdierolf 3ddb8556c6 fix: correct clipboard image encoding and binary handling (#3817) 2025-11-03 09:21:13 -05:00
Pranshu Raj 87d71450f6 docs: add session_child_cycle and session_child_cycle_reverse keybinds (#3807) 2025-11-03 09:20:35 -05:00
Sebastian Herrlinger 7854552497 use new opentui getTextRange method and Bun.stringWidth instead of value.length to mitigate issues like #3734 2025-11-03 15:15:55 +01:00
GitHub Action 12f651a939 ignore: update download stats 2025-11-03 2025-11-03 12:04:38 +00:00
Ivan Starkov 9c97b7e96b feat: Enhance task display with [subagent type] (#3772) 2025-11-03 01:09:31 -06:00
Aiden Cline 3003fb475e core: prevent TypeError when error handling encounters non-object errors
When API errors like token limit exceeded errors are passed as strings to error checking methods, the 'in' operator would throw a TypeError. This fix adds a type guard to check that the input is an object before attempting to access its properties, allowing proper error classification even when encountering unexpected error formats from providers.
2025-11-02 23:38:56 -06:00
Zeldris 7cf1291d40 docs: use brew official formula (#3733) 2025-11-02 21:00:23 -06:00
Dax Raad bfc182e530 docs 2025-11-03 01:57:36 +00:00
Dax Raad 06c4f7c700 ci: eventualy consistency 2025-11-03 01:57:36 +00:00
opencode 5be6b46f05 release: v1.0.15 2025-11-03 01:57:36 +00:00
opencode ba77e5510c release: v1.0.14 2025-11-03 00:12:08 +00:00
Dax Raad 5ed6efd0ac tui: fix focus management and dialog interactions 2025-11-02 19:07:22 -05:00
Dax Raad 3f80815674 DELETE GO BUBBLETEA CRAP HOORAY 2025-11-02 18:43:33 -05:00
opencode 7825993491 release: v1.0.13 2025-11-02 23:31:25 +00:00
Dax Raad d40233290f add back child session cycle 2025-11-02 18:26:38 -05:00
Dax Raad cb7ccb40b7 undo 2025-11-02 16:31:32 -05:00
Dax Raad e8f2ee7c41 move dialog select keybind to input 2025-11-02 15:47:04 -05:00
Dax Raad 3357fc5c84 ci: centralize Bun version to package.json to ensure consistent builds across CI and local development 2025-11-02 15:42:15 -05:00
Dax Raad 71a16c8081 upgrade to bun 1.3.1 2025-11-02 14:00:50 -05:00
Dax Raad 3c70e8fa17 tui: fix file path handling when pasting images with spaces in filename
- Fixes issue where files with spaces in their names couldn't be pasted as images
- Prevents default paste behavior to avoid conflicts with image insertion
- Improves error handling for file reading operations
2025-11-02 13:45:44 -05:00
Dax Raad 0cbab38aef tui: add /timeline command to quickly navigate to specific messages in session history 2025-11-02 18:27:42 +00:00
opencode a57ab7d996 release: v1.0.12 2025-11-02 18:27:42 +00:00
Dax Raad a5cbc2df49 fix duplicate plugin subscriptions 2025-11-02 13:22:58 -05:00
John Eismeier db0ae87266 fix: typos (#3757)
Signed-off-by: John E <jeis4wpi@outlook.com>
2025-11-02 09:56:40 -06:00
Aditya Mathur 7fa176f955 chore: update hono-openapi to version 1.1.1 (#3738) 2025-11-02 09:21:55 -06:00
GitHub Action 5018dccf37 ignore: update download stats 2025-11-02 2025-11-02 12:04:05 +00:00
kcrommett 16147ff6cc add nightowl theme back after opentui release (#3732) 2025-11-02 04:29:14 -05:00
opencode 8727d25ea9 release: v1.0.11 2025-11-02 08:18:59 +00:00
Dax Raad 550f84a581 fix dialog 2025-11-02 02:53:55 -05:00
Dax Raad 9e4172f08e tui: prevent default Enter key behavior when selecting dialog options to avoid conflicts 2025-11-02 01:19:30 -05:00
Aiden Cline 402b8099b5 ci: fix regex 2025-11-01 20:23:10 -05:00
opencode-agent[bot] dc6083e3cb github action: truncate PR titles to 256 chars to avoid GH api errors (#3727)
publish-github-action / publish (push) Waiting to run
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-11-01 20:09:35 -05:00
Aiden Cline b7b9c3e4c2 ci: stop auto assigning 2025-11-01 19:57:09 -05:00
Sebastian Herrlinger 25dec4fef5 upgrade to opentui 0.1.32, activates kitty keyboard 2025-11-02 01:45:38 +01:00
opencode 1c6c52c488 release: v1.0.10 2025-11-01 18:06:28 +00:00
Dax 2376fe4c9f Light mode (#3709) 2025-11-01 13:54:01 -04:00
Dax Raad 6b3a947504 docs update 2025-11-01 13:23:03 -04:00
Dax Raad b259b3dfae docs: update keybinds documentation with current defaults and remove deprecated bindings 2025-11-01 12:32:22 -04:00
opencode 940e3c8b34 release: v1.0.9 2025-11-01 16:22:49 +00:00
Dax Raad cae0189398 sync 2025-11-01 12:14:15 -04:00
Haris Gušić 32996e676f feat: tui: Port /exit command and all command aliases (#3665) 2025-11-01 12:13:10 -04:00
Aiden Cline 7d97f6f179 ignore: update contributing md 2025-11-01 11:08:07 -05:00
opencode 39e7e89e32 release: v1.0.8 2025-11-01 15:58:23 +00:00
Dax Raad b1b61e528d tui: change delete keybind to ctrl+d in session list dialog 2025-11-01 11:53:46 -04:00
Dax Raad 48ab88cd1f fix modified files being empty 2025-11-01 11:48:47 -04:00
Dax Raad 1ddbb7e11f remember sidebar position 2025-11-01 11:40:33 -04:00
Daniel van Strien 42212bce8a docs: add Hugging Face Inference Providers documentation (#3505)
Co-authored-by: célina <hanouticelina@gmail.com>
2025-11-01 10:33:17 -05:00
Haris Gušić d3f4699941 feat: tui: Add --prompt option (#3668) 2025-11-01 11:18:31 -04:00
Giuseppe Rota 4c77b1c9e4 fix: fix typo in commit message guidelines (#3702) 2025-11-01 10:14:53 -05:00
Yuku Kotani 187fa92c01 Add formatter status display to TUI status dialog (#3701) 2025-11-01 11:14:39 -04:00
Dax Raad ebd326355d ensure wl-copy is available 2025-11-01 11:10:39 -04:00
Haris Gušić 2c35ebef7e fix: tui: Handle Clipboard.copy errors properly (#3685) 2025-11-01 15:34:21 +01:00
Haris Gušić 554f6518a1 fix: tui: add toast for /share url copy (#3686) 2025-11-01 08:06:56 -05:00
GitHub Action 628484cc8a ignore: update download stats 2025-11-01 2025-11-01 12:04:18 +00:00
Aiden Cline 81d7976e27 make /init a default slash command on server side (#3677) 2025-11-01 01:14:09 -05:00
Haris Gušić 851ace6225 fix: tui: Fix /editor command (#3663) 2025-11-01 00:16:06 +00:00
opencode c0439c1cb9 release: v1.0.7 2025-11-01 00:16:05 +00:00
Dax Raad 5bd4895b15 tab to accept autocomplete 2025-10-31 20:10:01 -04:00
Dax Raad 0300bd404e tui: fix prompt text aggregation to exclude synthetic content 2025-10-31 20:01:27 -04:00
Dax Raad 4069d7ed52 docs: sync 2025-10-31 23:58:57 +00:00
opencode f2df82714d release: v1.0.6 2025-10-31 23:58:57 +00:00
Dax Raad da8830ce35 tui: prevent clipboard operations from throwing errors on process exit 2025-10-31 19:54:15 -04:00
opencode c92dd80f36 release: v1.0.5 2025-10-31 23:51:36 +00:00
Dax Raad 0bdc062646 tui: improve keyboard navigation and MCP server status display 2025-10-31 19:47:08 -04:00
Dax Raad ac9e8cdf90 tui: add keyboard shortcuts to cycle through recently used models
Users can now press F2 to cycle forward and Shift+F2 to cycle backward through their recently used models, making it faster to switch between commonly used AI models without opening the model selection dialog.
2025-10-31 19:42:41 -04:00
Dax Raad c033ccd261 sync 2025-10-31 23:05:11 +00:00
opencode e57c3f0abd release: v1.0.4 2025-10-31 23:05:10 +00:00
Aiden Cline 83ce9efa98 Revert "opentui: fix: Make worker.ts path independent from cwd (#3600)"
This reverts commit 047df383b3.
2025-10-31 17:57:56 -05:00
opencode 84c68319cb release: v1.0.3 2025-10-31 22:52:57 +00:00
Dax Raad 4d498fcb37 tui: add session rename functionality with /rename command
- Add /rename command to autocomplete when a session is active
- Add rename dialog component for changing session names
- Add rename option to session list dialog with 'r' keybind
- Add session rename command to command registry
2025-10-31 18:44:33 -04:00
Haris Gušić 047df383b3 opentui: fix: Make worker.ts path independent from cwd (#3600) 2025-10-31 17:37:31 -05:00
Haris Gušić e529308281 fix: grep failing when pattern started with a dash 2025-10-31 17:20:22 -05:00
Dax Raad d076bcedb2 tui: simplify theme selection API by renaming setSelectedTheme to set 2025-10-31 18:11:36 -04:00
Dax Raad d9299218fe respect theme in config 2025-10-31 18:04:38 -04:00
Haris Gušić d27f90349f fix: Remove conflicting "-h" aliases in TUI spawn and thread commands (#3651) 2025-10-31 16:59:59 -05:00
Aiden Cline aecede3032 fix: unsupported option 2025-10-31 16:53:08 -05:00
Dax Raad 3adfa280dc tui: add /editor command to autocomplete for opening external editor 2025-10-31 17:47:08 -04:00
Dax Raad 9278348822 add /theme 2025-10-31 17:44:41 -04:00
Aiden Cline a2f6b7409a Revert "fix: ensure flags & docs match (#3638)"
This reverts commit 7c06047c55.
2025-10-31 16:43:29 -05:00
opencode-agent[bot] 7c06047c55 fix: ensure flags & docs match (#3638)
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-10-31 16:33:46 -05:00
Nathan Thomas 47f89df1a0 feat: allow ctrl+d to exit the app (#3636) 2025-10-31 16:27:41 -05:00
Haris Gušić f181ebf772 feat: Improve error boundary add button to easily create issue in github (#3639) 2025-10-31 16:20:32 -05:00
Aiden Cline b29bc48559 ci: change model 2025-10-31 16:06:22 -05:00
Adam 78c4a75f08 wip: desktop work 2025-10-31 15:57:21 -05:00
Adam eff41c7083 wip: desktop work 2025-10-31 15:37:50 -05:00
Adam 24d6bfdd9a wip: desktop work 2025-10-31 15:37:50 -05:00
opencode a3c1602ad6 release: v1.0.2 2025-10-31 20:33:50 +00:00
Dax Raad ea7c132ce1 tui: update hello command with test content 2025-10-31 16:21:30 -04:00
Dax Raad 48d8dc38a9 tui: improve autocomplete component styling and update test command 2025-10-31 16:21:30 -04:00
Dax Raad 542507e75c ignore 2025-10-31 20:17:40 +00:00
opencode 43994da2a7 release: v1.0.1 2025-10-31 20:17:39 +00:00
Dax Raad 9aa05ccd0b tui: add persistent key-value storage for user preferences
- Add KVProvider context for storing user preferences like theme and warnings
- Update theme context to use KV storage instead of sync config
- Move openrouter warning to persistent KV storage
- Refactor theme selection to persist user choice across sessions
2025-10-31 16:13:02 -04:00
Aiden Cline 6987bfabb7 ci: auto assign 2025-10-31 14:58:18 -05:00
Aiden Cline 7dfb96b336 ci: autolabel action 2025-10-31 14:55:33 -05:00
Dax Raad 8fa2ad5ab7 tui: add /session command to list available sessions 2025-10-31 15:41:36 -04:00
opencode 4df9eac174 release: v1.0.0 2025-10-31 19:28:07 +00:00
Dax Raad c5cee57438 docs: add v1.0 upgrade guide with UX changes documentation 2025-10-31 15:15:52 -04:00
Frank e594dff9b9 wip: zen 2025-10-31 15:09:48 -04:00
Dax fd63884d0b OpenTUI is here (#2685) 2025-10-31 15:07:36 -04:00
opencode 1549f027e1 release: v0.15.31 2025-10-31 18:56:26 +00:00
Frank a8e58d47ed sync 2025-10-31 14:43:51 -04:00
Frank 7a0019ae3f zen: billing page layout 2025-10-31 14:43:51 -04:00
Haris Gušić 43c93878e3 fix: Missing @opencode-ai/plugin causes crash (#3615) 2025-10-31 13:41:04 -05:00
David Hill 09f5134ef1 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-10-31 17:02:53 +00:00
David Hill 01eee7f354 wip: Tweaking select styles
This ghost style is placeholder for now until we style this properly with an icon and all the states.
2025-10-31 17:02:44 +00:00
David Hill e0f8e52f48 wip: Tweaking button styles 2025-10-31 17:02:12 +00:00
Adam 0cbeadec08 wip: desktop work 2025-10-31 12:00:44 -05:00
Adam b8eaa629e7 wip: desktop work 2025-10-31 12:00:44 -05:00
Steven Martin 908c109303 tweak - normalise unix-like identifiers to support git bash for windows (#2100)
Co-authored-by: Steven Martin <smartin@clearcom.com>
2025-10-31 11:41:44 -05:00
David Hill e1b94be752 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-10-31 16:03:36 +00:00
David Hill fed19405fc Added border as shadow and updated button 2025-10-31 16:03:33 +00:00
Aiden Cline 70fd4929f3 update types 2025-10-31 10:49:52 -05:00
Aiden Cline 0dd2fd09ae tweak: filter out deprecated models 2025-10-31 10:48:51 -05:00
Adam 951b5a89d5 wip: desktop work 2025-10-31 10:08:24 -05:00
Adam b15a738f2f wip: desktop work 2025-10-31 09:45:57 -05:00
Adam a2c68abe95 wip: desktop work 2025-10-31 07:30:02 -05:00
Adam 33651e0438 wip: desktop work 2025-10-31 07:24:47 -05:00
GitHub Action df6615671c ignore: update download stats 2025-10-31 2025-10-31 12:04:46 +00:00
opencode c90091eab7 release: v0.15.30 2025-10-31 06:12:16 +00:00
Dax Raad 0df881b2d3 sync 2025-10-31 02:00:11 -04:00
Aiden Cline 89ab6fcdae fix: bash permission case 2025-10-31 00:52:46 -05:00
Haris Gušić ba2660ec3e fix: Opencode hangs after exit (#3481)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-10-30 23:57:58 -05:00
Filip 0c840433c8 add optional headers field to model config (#3546) 2025-10-30 23:35:26 -05:00
Jay V 6a8b6fc0d4 ignore: refactor header context menu to use CSS styling and router navigation 2025-10-30 20:24:29 -04:00
David Hill dc74e8a551 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-10-30 22:53:00 +00:00
David Hill b783f14cce Asset updates 2025-10-30 22:52:56 +00:00
David Hill 191425eb7a Fix download zip 2025-10-30 22:41:32 +00:00
David Hill 9d71004c8f Brand Assets Zip 2025-10-30 22:38:12 +00:00
David Hill eb602b3060 Configure png and svg downloads 2025-10-30 22:25:07 +00:00
David Hill 99b4965b9a Brand page 2025-10-30 22:13:48 +00:00
Ritoban Dutta 1557068185 fix: resolve hanging permission prompts in headless mode (#3522)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-10-30 15:37:41 -05:00
Adam 7d8536562d chore: sanitize tool parts locally 2025-10-30 14:57:38 -05:00
Jay V b661ebaeb5 ignore: update project stats to reflect current growth 2025-10-30 15:47:41 -04:00
Adam 6dbca6c967 wip: desktop work 2025-10-30 14:46:38 -05:00
Adam 97672749fd wip: desktop work 2025-10-30 14:41:15 -05:00
Frank b8879e8aa3 wip: fix 2025-10-30 15:15:46 -04:00
Frank 5b7231049d wip: zen 2025-10-30 15:10:29 -04:00
Frank b62e890f31 zen: allow byok requests w/o a balance 2025-10-30 15:10:29 -04:00
Adam 016b524a74 wip: desktop work 2025-10-30 13:54:52 -05:00
Adam b3b8481653 wip: desktop work 2025-10-30 13:49:29 -05:00
David Hill 2357de8e3d Adding links to legal 2025-10-30 17:37:30 +00:00
Adam 0de58b7b7b fix: lander space 2025-10-30 12:32:25 -05:00
Adam 6c66cb56dd wip: desktop work 2025-10-30 12:30:45 -05:00
Brandon 1e4b8d2e44 feat: Adds session id to Agent context metadata (#3559)
Co-authored-by: Brandon Wisnicki <bwisnicki@palantir.com>
2025-10-30 12:15:21 -05:00
Adam 84e617bdd7 wip: desktop work 2025-10-30 12:13:02 -05:00
Adam 732375b89b wip: desktop work 2025-10-30 12:02:51 -05:00
Adam 1827dad7d9 wip: desktop work 2025-10-30 12:02:50 -05:00
Adam bed8cc0398 wip: desktop work 2025-10-30 12:02:50 -05:00
Adam 0830a35b76 wip: desktop work 2025-10-30 12:02:50 -05:00
Adam 64b1c1c14c wip: desktop work 2025-10-30 12:02:50 -05:00
Adam 850228b311 wip: desktop work 2025-10-30 12:02:50 -05:00
Adam 239f99b75e wip: desktop work 2025-10-30 12:02:49 -05:00
Aiden Cline 992508fff7 tweak: adjust deno lsp (#3581) 2025-10-30 11:59:54 -05:00
David Hill 06c9f9305e Change position 2025-10-30 16:55:23 +00:00
Jay V 7cb3eaca7f ignore:lander 2025-10-30 12:40:07 -04:00
Haris Gušić ecc1f265ca tweak: move zod validation for tools to ensure it always runs (#3565) 2025-10-30 11:31:44 -05:00
Andrew Pashynnyk 0821602dc8 fix: preserve metadata from MCP tool results in tool.execute.after hook (#3573) 2025-10-30 11:10:42 -05:00
Aiden Cline 6350487417 fix: better frontmatter errors 2025-10-30 10:56:40 -05:00
oribi 5174dd4b78 fix: agent model selection priority issue (#3572) 2025-10-30 09:32:39 -05:00
David Hill 5f98e99993 Theme aware svgs to be copied 2025-10-30 13:01:48 +00:00
David Hill cb1f0a294b Icon size update 2025-10-30 12:46:34 +00:00
David Hill b09ac0db1b New assets 2025-10-30 12:36:41 +00:00
David Hill 361ebaea52 Close content menu with escape 2025-10-30 12:19:26 +00:00
David Hill 67466fcba9 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-10-30 12:16:15 +00:00
GitHub Action 77163d506d ignore: update download stats 2025-10-30 2025-10-30 12:04:36 +00:00
David Hill 20dcbe9bab Adding polish 2025-10-30 11:37:00 +00:00
opencode baf5899e00 release: v0.15.29 2025-10-30 06:49:34 +00:00
Aiden Cline 4c680c6d98 fix: tui not showing some errors 2025-10-30 01:41:28 -05:00
Matt Gillard 165c453fba add optional timeout field to mcp config to allow users to use responding servers (#3558)
Co-authored-by: Matt Gillard <matt-github@gillard.biz>
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-10-30 01:25:24 -05:00
Khang Ha (Kelvin) 1145802f0f Fix "bufio.Scanner token too long" error by replacing Scanner with Reader in SSE (#3531) 2025-10-30 01:04:06 -05:00
Aiden Cline 02fe9ea852 tweak: catch err 2025-10-30 00:29:30 -05:00
ElecTwix 8a8f6898c1 fix: add doom loop detection (#3445)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-10-30 00:13:18 -05:00
GitHub Action 4ce7a5875d ignore: update download stats 2025-10-30 2025-10-30 02:28:13 +00:00
David Hill 9e2d698f11 Add right click menu to logo 2025-10-30 00:40:24 +00:00
David Hill 76d994e5eb Brand page 2025-10-30 00:22:49 +00:00
Jay V 9f3e73f92a ignore: fix email formatting to ensure proper line breaks between message and signature 2025-10-29 19:19:28 -04:00
Jay V b60caaf50a docs: enterprise 2025-10-29 19:07:43 -04:00
Frank b59c615031 zen: show browser default time 2025-10-29 17:14:28 -04:00
Adam a2c913a436 wip: desktop work 2025-10-29 16:04:34 -05:00
Adam 67172ec85c wip: desktop work 2025-10-29 16:04:34 -05:00
rienkim 1d0418a88c feat: add positional argument support to slash commands (#3456)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-10-29 14:54:24 -05:00
Frank 25540f2814 zen: fix unified endpoint for codex 2025-10-29 15:06:37 -04:00
opencode be2671c70b release: v0.15.28 2025-10-29 19:00:56 +00:00
Aiden Cline 8893b80506 fix: parcel watcher issue (#3544) 2025-10-29 13:52:38 -05:00
opencode df3a8ffaea release: v0.15.27 2025-10-29 18:41:24 +00:00
Dax Raad 573d4aa39f only generate user message summary if no diffs 2025-10-29 14:28:43 -04:00
Adam fc1af72603 wip: desktop work 2025-10-29 16:59:45 +00:00
opencode 6cbae5b90b release: v0.15.26 2025-10-29 16:59:45 +00:00
David Hill d5796e643c Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-10-29 16:16:56 +00:00
David Hill 7bd3e6ed9c Enron update 2025-10-29 16:16:48 +00:00
Aiden Cline fcc27b40e1 ignore: add label 2025-10-29 11:08:46 -05:00
David Hill 15ca5f8811 testimonial and other polish 2025-10-29 16:02:25 +00:00
opencode 03b9579861 release: v0.15.25 2025-10-29 16:01:53 +00:00
Dax Raad 39dde1d979 autoupgrade latest major only 2025-10-29 11:53:25 -04:00
Aiden Cline 7d7b445025 fix: ensure tool inputs are zod validated 2025-10-29 10:46:57 -05:00
Adam 03ac92f6d8 fix: desktop error 2025-10-29 10:43:34 -05:00
opencode 1fb2e2dae0 release: v0.15.24 2025-10-29 15:38:55 +00:00
David Hill 99c3d2f2cb Adding polish 2025-10-29 15:34:45 +00:00
David Hill 2007dbfd8f Fix form submission 2025-10-29 15:34:39 +00:00
Dax Raad b8cb5c20fb remove log 2025-10-29 11:27:51 -04:00
Dax Raad 7851f58079 session diff only include modified files 2025-10-29 11:26:21 -04:00
Aiden Cline 7770cde426 ignore: rm 2025-10-29 10:19:02 -05:00
David Hill 80bdf5e3ac First pass at adding an enterprise page 2025-10-29 15:16:17 +00:00
Adam dcfb17f4da wip: desktop work 2025-10-29 07:32:01 -05:00
Adam cc52b1a422 wip: desktop work 2025-10-29 07:32:00 -05:00
GitHub Action 68179924fb ignore: update download stats 2025-10-29 2025-10-29 12:04:57 +00:00
Aiden Cline 3223892d5d zen: fix models endpoint to be openai compatible 2025-10-28 22:48:57 -05:00
Aiden Cline 26ca04cb9b ignore: rename type 2025-10-28 22:39:57 -05:00
Tyler Gannon 2e1489819f add OpenAPI annotations to tui.ts control endpoints (#3519) 2025-10-28 22:39:22 -05:00
opencode 888968401b release: v0.15.23 2025-10-29 01:35:27 +00:00
Dax Raad d035090129 ci: stuff 2025-10-28 21:28:44 -04:00
Dax Raad f292d30e26 sync 2025-10-28 20:35:18 -04:00
Dax Raad ed41504ffd tag majors 2025-10-28 20:28:30 -04:00
Kevin King 17e26a5796 ignore: python sdk (#2779)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-10-28 18:32:45 -05:00
Aiden Cline 28acb94b3f fix: ensure timeout param passed to bash tool is positive 2025-10-28 17:32:39 -05:00
kcrommett 7caa64ea82 mcp: fix status() to not overwrite connected with failed (#3514) 2025-10-28 16:16:03 -05:00
Adam 5bdb9e885e wip: desktop work 2025-10-28 15:39:41 -05:00
Haris Gušić bc8678c56e fix: Use process.stdout.write instead of console.log (#3508) 2025-10-28 15:38:08 -05:00
GitHub Action 717e7a1c5b chore: format code 2025-10-28 20:29:47 +00:00
Adam 4e34967969 wip: desktop work 2025-10-28 15:29:16 -05:00
Adam ac6724b489 wip: desktop work 2025-10-28 15:29:16 -05:00
Adam 1a8d537f70 wip: desktop work 2025-10-28 15:29:16 -05:00
Adam 1ee8419abd wip: desktop work 2025-10-28 15:29:16 -05:00
Adam 8cf67b76a5 wip: desktop work 2025-10-28 15:29:16 -05:00
Adam 36e7f5a557 wip: desktop work 2025-10-28 15:29:15 -05:00
Adam 928e550a60 wip: desktop work 2025-10-28 15:29:15 -05:00
Frank 3d6f24d86e zen: provider affinity 2025-10-28 15:55:03 -04:00
Aiden Cline 945fb316bc add --title flag to opencode run (#3507) 2025-10-28 13:32:36 -05:00
Dax Raad 63a1c0c1f6 ignore 2025-10-28 14:13:47 -04:00
Dax Raad 247d68cdfe core: fix additions and deletions counting in edit tool filediff 2025-10-28 14:08:10 -04:00
Danilo Favato e585b4da07 tweak: add experimental chatMaxRetries to config (#2116)
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-10-28 12:09:41 -05:00
oribi 624bb0093c add OPENCODE_CONFIG_DIR to allow loading a custom config directory (#3504)
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-10-28 11:50:09 -05:00
Aiden Cline 8d4391341f add catch for mcp tool execution 2025-10-28 10:23:04 -05:00
Aiden Cline 36cd83bbf6 add catch for mcp tool execution 2025-10-28 10:23:03 -05:00
opencode dd4d898a5e release: v0.15.20 2025-10-28 15:12:37 +00:00
Aiden Cline 215518f491 fix: blank version issue 2025-10-28 10:03:53 -05:00
GitHub Action a000e24c5a ignore: update download stats 2025-10-28 2025-10-28 12:04:31 +00:00
opencode 01b23b9e5d release: v0.15.19 2025-10-28 08:12:32 +00:00
Dax Raad 6c481803d1 feat: add OPENCODE_FAKE_VCS flag for VCS testing and update todo tracking instructions 2025-10-28 02:54:29 -04:00
Aiden Cline f1e170f324 chore: rm unused import 2025-10-28 00:43:29 -05:00
Dax Raad 5d8d65bedc do not set temperature for claude models 2025-10-28 01:14:25 -04:00
Aiden Cline e172fa9e20 feat (acp): mcp server support, file diffs, some default slash commands (/init, /compact), show todos properly (#3490)
The mcp server support does not mean acp didn't allow u to use mcp servers previously, it means that now you can connect new servers via ACP instead of relying on the opencode defined ones
2025-10-28 00:08:30 -05:00
Frank 2f884f24f4 acp: fix type error 2025-10-27 21:40:08 -04:00
Frank 7ebb909621 zen: support 1M claude context 2025-10-27 21:36:10 -04:00
Frank afeb5a205b wip: zen doc 2025-10-27 21:25:56 -04:00
Frank 152188b2c2 wip: zen 2025-10-27 19:26:28 -04:00
kcrommett a151c93312 fix: editor paste functionality for text attachments (#3489) 2025-10-27 17:51:33 -05:00
Jay V 1a16709564 docs: edit 2025-10-27 18:16:48 -04:00
Dax Raad 8bc4e02190 Remove typecheck script from desktop package 2025-10-27 18:03:32 -04:00
Jay V 97bc53cdac Add missing dependencies for desktop package 2025-10-27 17:49:31 -04:00
Jay V 8d7dd2ee9c Merge branch 'doc-acp' into dev 2025-10-27 17:49:24 -04:00
Jay V 027ab1d693 docs: edit 2025-10-27 17:48:17 -04:00
Aiden Cline 3cc07c17f8 chore: rm comment 2025-10-27 16:15:13 -05:00
Aiden Cline 83beb584b6 chore: delete unused file 2025-10-27 15:44:12 -05:00
Adam fa267bab49 wip: desktop work 2025-10-27 15:37:07 -05:00
Adam 93535d3d64 wip: desktop work 2025-10-27 15:37:07 -05:00
Adam 9771ca57e3 wip: desktop work 2025-10-27 15:37:06 -05:00
Adam 0ffc129106 wip: desktop work 2025-10-27 15:37:06 -05:00
Aiden Cline 2f3ee94954 fix: litellm error tool= param must be specified 2025-10-27 14:03:42 -05:00
Aurelien Ribon e6c7aeb3c1 fix: dont set reasoning effort to medium for gpt-5-pro (#3474) 2025-10-27 10:50:57 -05:00
Aiden Cline 4b695b1d3b update sdk 2025-10-27 10:47:04 -05:00
Aiden Cline 7232137360 tweak: rename event 2025-10-27 10:42:47 -05:00
Bernat Pericàs 940eabd8d2 feat: add session.started event that triggers when a new session is created (#3413) 2025-10-27 07:18:23 -05:00
GitHub Action d767eddad5 ignore: update download stats 2025-10-27 2025-10-27 12:04:50 +00:00
Aiden Cline 977339adab Revert "fix: Explicitly exit CLI to prevent hanging subprocesses (#3083)"
This reverts commit 5f0e46c51c.
2025-10-27 01:30:13 -05:00
Aiden Cline 6cf035ef92 docs: acp 2025-10-27 00:56:00 -05:00
Haris Gušić 5f0e46c51c fix: Explicitly exit CLI to prevent hanging subprocesses (#3083)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-10-27 00:03:10 -05:00
Aiden Cline 2f44113800 wip 2025-10-26 23:49:56 -05:00
Denys Rybalka cae8fe1386 chore: use stable URLs in PKGBUILD (#3448) 2025-10-26 19:50:45 -05:00
Aiden Cline e5dfcaa0cb docs: update agent frontmatter permission example 2025-10-26 15:04:48 -05:00
Jérôme Benoit 0787f01d1d chore: cleanup versioned zod imports (#3460)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.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-10-26 14:50:41 -05:00
Dan McGuirk 630ff6c3ec docs: fix typos (#3454) 2025-10-26 14:37:25 -05:00
GitHub Action 8bb9b81c98 ignore: update download stats 2025-10-26 2025-10-26 12:04:22 +00:00
Joscha Götzer 27ebcec545 fix: make build script work cross-platform (#3430)
Co-authored-by: JosXa <info@josxa.dev>
2025-10-26 01:40:17 -05:00
Aiden Cline 0d0bc44f9e Remove claude-haiku-4.5 from default priority for GitHub Copilot session title generation 2025-10-26 01:21:34 -05:00
opencode 6c221defb6 release: v0.15.18 2025-10-26 03:49:21 +00:00
Aiden Cline 067b2f92c0 make title gen more reliable 2025-10-25 22:14:29 -05:00
Aiden Cline bc4761ab42 fix: $ invocation not .quiet() (#3449) 2025-10-25 16:27:09 -05:00
Dax Raad 366bb86b4f update anthropic prompt 2025-10-25 17:26:27 -04:00
Mohammad Alhashemi e0c9828270 feat: add noReply parameter (#3433)
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-10-25 14:56:54 -05:00
Paulo Edgar Castro a3b2d34a5f fix: timeout param that allows user to disable provider timeout (#3443) 2025-10-25 12:08:27 -05:00
GitHub Action 7f37cb2fea ignore: update download stats 2025-10-25 2025-10-25 12:04:18 +00:00
opencode 796b6b00ab release: v0.15.17 2025-10-25 06:45:23 +00:00
Aiden Cline bab6e06c31 ignore: rm change 2025-10-25 01:35:43 -05:00
Aiden Cline 5aed111138 acp: slash commands, agents, permissions, @ references, code cleanup (#3403)
Co-authored-by: yetone <yetoneful@gmail.com>
2025-10-25 01:32:46 -05:00
Aiden Cline d11ed02f1e fix: bedrock reasoning 2025-10-24 17:35:08 -05:00
Adam 40b693e6a9 wip: desktop work 2025-10-24 16:04:44 -05:00
Adam 97edf5c319 wip: desktop work 2025-10-24 15:51:31 -05:00
Adam 85b9b43cee wip: desktop work 2025-10-24 15:43:47 -05:00
Adam 2b22df5446 wip: desktop work 2025-10-24 15:02:31 -05:00
Adam acee1e14a0 wip: accordion css not going to keep me down 2025-10-24 15:01:28 -05:00
Dax Raad 5a1146e1c7 ignore: improve session timeline debugging and message display functionality 2025-10-24 15:45:37 -04:00
Aiden Cline f15835c7b5 make plan agent whitelist more conservative (#3424) 2025-10-24 14:40:36 -05:00
Dax Raad f0405b4211 ignore: fix new session message loading with retry logic to handle server processing delays 2025-10-24 15:35:53 -04:00
Dax Raad e777005788 ignore: highlight active session in sidebar to improve visual feedback 2025-10-24 15:27:20 -04:00
Dax Raad 42674f6740 ignore: fix session activation after creation to ensure proper state management 2025-10-24 15:24:17 -04:00
Adam 1c089e9861 fix: pierre dep 2025-10-24 13:51:24 -05:00
Aiden Cline bda7fd9ad6 ignore: reword 2025-10-24 13:46:47 -05:00
Adam 4520352b71 wip: desktop work 2025-10-24 13:01:22 -05:00
Dax Raad 1bb95cb7d1 add missing dep 2025-10-24 13:43:33 -04:00
Adam ab05a47587 wip: desktop work 2025-10-24 12:38:00 -05:00
Adam 5bccc3563b wip: desktop work 2025-10-24 12:32:02 -05:00
Adam b5888565aa wip: desktop work 2025-10-24 12:16:33 -05:00
Adam 8adb50dc5a wip: desktop work 2025-10-24 12:16:33 -05:00
Adam 60477012fb wip: desktop work 2025-10-24 12:16:33 -05:00
Adam 7e7f75021c wip: desktop work 2025-10-24 12:16:32 -05:00
Adam 8a3218877e wip: desktop work 2025-10-24 12:16:32 -05:00
Adam a9a3646538 wip: desktop work 2025-10-24 12:16:32 -05:00
Adam f4e09d2acc wip: ui package demo page 2025-10-24 12:16:32 -05:00
Aiden Cline 8aac578b8d adjust edit tool multiple matches error wording (#3418) 2025-10-24 12:14:49 -05:00
Dax Raad 84094fe37c mroe summary tweaks 2025-10-24 12:37:23 -04:00
Dax Raad 33d214f75d track finish reason 2025-10-24 12:23:24 -04:00
Dax Raad 347761cd8f generate user message title and body 2025-10-24 11:50:42 -04:00
Hieu Nguyen 56ea985113 feat: support lua lsp (#3402) 2025-10-24 10:37:11 -05:00
Andrew Pashynnyk 6544e86e29 fix: respect local config for autoupdate settings (#3408)
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-24 09:50:09 -05:00
Dax Raad 703bd6ddcd potentially fix fetch failed timeout errors 2025-10-24 09:46:28 -04:00
Dax Raad bfd17abe16 test cleanup 2025-10-24 09:29:38 -04:00
Dax Raad 57cf2ea159 fix summary 2025-10-24 09:24:58 -04:00
Dax Raad 21c789a37d fix failing migration 2025-10-24 09:20:15 -04:00
GitHub Action 2d438354e1 ignore: update download stats 2025-10-24 2025-10-24 12:05:02 +00:00
opencode baa50e94a7 release: v0.15.16 2025-10-24 04:52:27 +00:00
Dax Raad 69128ae829 codex does not have reasoning effort set 2025-10-24 00:29:34 -04:00
opencode b43ecefac5 release: v0.15.15 2025-10-24 03:51:17 +00:00
Dax Raad ba1c0b5f3a update codex prompt 2025-10-23 23:36:32 -04:00
Haris Gušić 6d5fe56b87 fix: Opencode fails with ENOENT posix_spawn '/usr/bin/rg' (#3396) 2025-10-23 18:05:01 -05:00
Bernat Pericàs fe4d87744f refactor: whitelist some safe bash tools in Plan agent (#3288)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-10-23 17:11:08 -05:00
Aiden Cline cceea248ee adjust changelog generation 2025-10-23 16:16:11 -05:00
Dax Raad b93468488e regen sdk 2025-10-23 16:33:00 -04:00
Dax Raad e5f9dcf4f3 session summaries in data 2025-10-23 16:28:20 -04:00
Dax Raad 343032ee8d ignore: diff stuff 2025-10-23 16:04:58 -04:00
Aiden Cline 02b8363659 add flag wildcard parsing support for bash tool (#3390) 2025-10-23 13:35:09 -05:00
Aiden Cline ed227d3d75 test: rm flaky test 2025-10-23 12:52:08 -05:00
Aiden Cline d8b89edd19 tweak: split out title before newline 2025-10-23 12:44:43 -05:00
Thierry Delafontaine 1fd449bc78 fix: move zod-to-json-schema to dependencies (#3387) 2025-10-23 12:16:03 -05:00
Aiden Cline 6a1a0961de docs: fix numbers 2025-10-23 11:53:21 -05:00
Aiden Cline 70b3b366f6 docs: tweak google vertex 2025-10-23 11:49:53 -05:00
Yuku Kotani 2852672acb docs: Add Google Vertex AI provider documentation (#3349) 2025-10-23 11:44:06 -05:00
Andrew Pashynnyk c82fc6daa2 fix: allow tool.execute.after hook to modify MCP tool output (#3381) 2025-10-23 10:38:55 -05:00
Mani Sundararajan 709dafcb13 docs: rm winget as a recommended installation method under windows (#3382)
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-10-23 09:25:42 -05:00
GitHub Action eb9e16355a ignore: update download stats 2025-10-23 2025-10-23 12:04:55 +00:00
opencode c1549ef1ee release: v0.15.14 2025-10-23 05:55:28 +00:00
Yesh Yendamuri 2cfa6a3d52 feat: add model management to ACP sessions (#3358) 2025-10-23 00:43:28 -05:00
Aiden Cline fc7d25ca8b regen sdk 2025-10-22 23:00:03 -05:00
Aiden Cline 18eee3b3cf feat: retry parts (#3369) 2025-10-22 18:31:36 -05:00
Dax Raad 7b39b4124f summary tweaks 2025-10-22 19:03:08 -04:00
geril07 005b126b13 add option to allow agent switches to not change model (#3356) 2025-10-22 17:51:46 -05:00
Adam 8907c5e0ca wip: desktop work 2025-10-22 17:51:16 -05:00
Dax Raad a9a2dc580d sync 2025-10-22 18:49:57 -04:00
Dax Raad a2bf70e7ec core: add experimental turn summarization to compact conversation history 2025-10-22 18:33:46 -04:00
Adam 6dd8d3ac72 wip: desktop work 2025-10-22 17:33:08 -05:00
Adam bc04bf2685 wip: desktop work 2025-10-22 17:31:49 -05:00
Aiden Cline 1177ab786a vscode: eslint fix 2025-10-22 17:17:47 -05:00
Aiden Cline ee07ec9e77 vscode: fix script
publish-vscode / publish (push) Waiting to run
2025-10-22 17:11:51 -05:00
Aiden Cline 6d051499bb vscode: adjust tsconfig
publish-vscode / publish (push) Waiting to run
2025-10-22 16:43:06 -05:00
Aiden Cline c685c5e5a0 ignore: cleanup bun.lock w/ bun i 2025-10-22 15:34:41 -05:00
Dax Raad 58c94cf635 add parent id to assistant messages
publish-vscode / publish (push) Waiting to run
2025-10-22 15:01:13 -04:00
Kyle Galbraith ad254b3138 docs: fix typo in SDK documentation (#3355) 2025-10-22 11:43:28 -05:00
theVinchi decab36f66 add Amazon Nova models to us-* prefix requirement list (#3357) 2025-10-22 11:41:38 -05:00
Aiden Cline b6ae1914bc tweak 2025-10-22 11:29:10 -05:00
Aiden Cline 16dbe312a8 tweak 2025-10-22 11:28:06 -05:00
Aiden Cline fba5a7a0e7 tweak: model priority 2025-10-22 11:12:32 -05:00
Aiden Cline 7420e5b6e1 change default title model 2025-10-22 10:41:08 -05:00
GitHub Action 583aebe406 ignore: update download stats 2025-10-22 2025-10-22 12:04:58 +00:00
Affaan Mustafa 3fd056741b feat: Improve editor detection with auto-discovery and better error messages (#3155)
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-10-21 20:49:42 -05:00
Haris Gušić ef2f094cdc fix: opencode run shouldn't print to stderr (#3341) 2025-10-21 19:53:09 -05:00
Aiden Cline 49567ce75e ignore: fix acp test (#3339) 2025-10-21 15:27:59 -05:00
opencode 82c3edc12d release: v0.15.13 2025-10-21 20:24:20 +00:00
Aiden Cline 710f0f5422 ignore: reword 2025-10-21 14:43:34 -05:00
Dax Raad 616d9676cf fix sourcemapping so errors show proper stack trace 2025-10-21 15:32:24 -04:00
Dax Raad 98fa0e16cd refactor acp args 2025-10-21 15:24:09 -04:00
Aiden Cline dcdfa02584 ignore: reword contributing.md 2025-10-21 14:12:33 -05:00
opencode 75bb03a82d release: v0.15.12 2025-10-21 17:31:56 +00:00
Aiden Cline 05289602c6 ignore: add label 2025-10-21 11:12:52 -05:00
Aiden Cline e25a4ed732 fix: provider option transforms (#3331) 2025-10-21 11:08:21 -05:00
Dax Raad 3e1f625289 core: provide line-level statistics in file diffs to help users understand the scale of changes 2025-10-21 11:54:41 -04:00
opencode 98a5fbd752 release: v0.15.11 2025-10-21 15:04:22 +00:00
Dax Raad 0acecdf64f core: improve error handling with console output for debugging 2025-10-21 10:55:10 -04:00
Giuseppe Rota 91a536fd40 fix(acp): use newer acp package (#3317) 2025-10-21 09:35:09 -05:00
GitHub Action d3d69dc316 ignore: update download stats 2025-10-21 2025-10-21 12:06:20 +00:00
kcrommett bb7dd9391b tweak: adjust file api to encode images (#3292) 2025-10-21 00:52:39 -05:00
zWing b75c7a9a40 fix: openai options when using @ai-sdk/azure (#3315) 2025-10-20 23:30:13 -05:00
Cheol Kang 5a41b3ff7a feat: add -f/--file flag to opencode run command (#3295)
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-10-20 23:25:54 -05:00
opencode a3a514b696 release: v0.15.10 2025-10-20 22:21:03 +00:00
Dax Raad a4b31ad404 core: add session diff API to show file changes between snapshots 2025-10-20 17:59:26 -04:00
Joe Schmitt 577ffecb29 feat: Add ACP (Agent Client Protocol) support (#2947)
Co-authored-by: opencode-bot <devnull@opencode.local>
Co-authored-by: Dax Raad <d@ironbay.co>
Co-authored-by: GitHub Action <action@github.com>
2025-10-20 16:55:22 -05:00
opencode 2cfcc85abc release: v0.15.9 2025-10-20 21:16:57 +00:00
Aiden Cline 6c2dc65d04 ignore: link labels 2025-10-20 16:02:23 -05:00
Frank 08772d81e3 wip: share 2025-10-20 16:17:51 -04:00
Aiden Cline 28bcb47ad5 fix: bash tool permission merges (#3302) 2025-10-20 15:15:14 -05:00
Frank 816504cbdd wip: zen 2025-10-20 13:22:43 -04:00
Rui Ferrão 52b1631dcf docs: clarify the effects of the subtask command option with primary … (#3299) 2025-10-20 12:16:09 -05:00
GitHub Action e8c0aa8c7d ignore: update download stats 2025-10-20 2025-10-20 12:04:59 +00:00
Hieu Nguyen db35e15726 feat: support astro lsp (#3242) 2025-10-19 22:49:06 -05:00
Squibid cc6c2a9d2f theme: add mellow theme (#3198) 2025-10-19 22:34:04 -05:00
Joscha Götzer 4f266df2b9 feat(theme): Add Night Owl theme (#3269) 2025-10-19 22:30:04 -05:00
Walter 3260c0c188 fix(tui): Allow modals to handle ESC key before force closing (#3279) 2025-10-19 22:16:54 -05:00
Zeldris bb986e2bcb docs: add scoop & winget to readme (#3215) 2025-10-19 21:50:51 -05:00
Aiden Cline 37898d1358 hide invalid option 2025-10-19 20:51:33 -05:00
Aiden Cline 9a4ee2d8a7 fix: snapshot undo bug (#3290) 2025-10-19 18:42:34 -05:00
Ivan Uhalin 9b4b28afc0 docs: fix Grep by Vercel link in documentation (#3280) 2025-10-19 11:51:57 -05:00
GitHub Action 6cf099820e ignore: update download stats 2025-10-19 2025-10-19 12:04:24 +00:00
Aiden Cline 980f840fc4 ignore: adjust template 2025-10-18 23:45:01 -05:00
Aiden Cline 79d19e3ca0 replace all $ARGUMENTS (#3276) 2025-10-18 23:37:17 -05:00
Aiden Cline 69ac899d10 ignore: add version to bug template 2025-10-18 14:56:06 -05:00
Aiden Cline 0f76406b8c docs: contributing.md (#3248)
Co-authored-by: Jay <air@live.ca>
2025-10-18 14:10:14 -05:00
opencode e3b7b6090f release: v0.15.8 2025-10-18 18:36:52 +00:00
Aiden Cline 2f32fde62d make compact interruptable (#3251) 2025-10-18 11:49:29 -05:00
GitHub Action 663aa334de ignore: update download stats 2025-10-18 2025-10-18 12:04:14 +00:00
Adam 58f403dc3c wip: desktop work 2025-10-17 15:22:11 -05:00
Haris Gušić c6f7228363 Improve typo directory error (#3247) 2025-10-17 14:34:37 -05:00
Jay V 36aa1949c0 ignore: clarify that workspaces are free for teams during beta 2025-10-17 15:19:19 -04:00
Jay V 1c2feba0dc ignore: help users discover that teams feature is free during beta 2025-10-17 14:33:21 -04:00
Jay V 12eeff807a docs: clarify team collaboration features and pricing 2025-10-17 13:58:10 -04:00
Adam 1cc13ea1b8 wip: desktop work 2025-10-17 12:07:58 -05:00
Adam 284dc640ea wip: desktop work 2025-10-17 12:06:36 -05:00
mgrachten ee17705cf3 set 755 permissions (#3237) 2025-10-17 10:42:42 -05:00
GitHub Action e1a4f0de40 ignore: update download stats 2025-10-17 2025-10-17 12:04:43 +00:00
Frank c2632adef7 wip: zen 2025-10-17 02:23:45 -04:00
Frank 8bd23655c6 wip: zen 2025-10-17 01:15:25 -04:00
Matt Gillard 17a55b1f28 added AU inference for bedrock haiku 4.5 (#3206)
Co-authored-by: Matt Gillard <matt-github@gillard.biz>
2025-10-16 23:48:41 -05:00
Frank b25a4610a2 wip: zen 2025-10-17 00:26:00 -04:00
Frank 74b4118758 wip: zen 2025-10-16 23:43:49 -04:00
Frank 84795cc840 wip: zen 2025-10-16 23:19:25 -04:00
Frank 27b7fee042 wip: zen 2025-10-17 03:17:07 +00:00
opencode 6c10295824 release: v0.15.7 2025-10-17 03:17:07 +00:00
Aiden Cline 72d7738458 Revert "try to avoid persisting empty thinking/text blocks"
This reverts commit 2a49448e46.
2025-10-16 22:04:55 -05:00
Frank 378e5a7c13 wip: zen 2025-10-16 22:28:34 -04:00
Frank d409ed6714 wip: zen 2025-10-16 22:28:09 -04:00
Hieu Nguyen c0be652d4c feat: deno lsp (#3210)
Co-authored-by: hiunguynx <hieu.nm1@teko.vn>
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
2025-10-16 18:08:36 -05:00
opencode 45742b3ef6 release: v0.15.6 2025-10-16 23:02:59 +00:00
Aiden Cline 8183017e9b sync 2025-10-16 17:49:02 -05:00
Aiden Cline 39c6b40610 fix: timeout option (#3229) 2025-10-16 17:47:41 -05:00
Frank 0b24c935dd wip: zen 2025-10-16 17:50:32 -04:00
Haris Gušić a629a81c30 docs: fix Ctrl+K styling in documentation (#3021) 2025-10-16 15:30:28 -05:00
Frank 8034e13f02 ci: fix 2025-10-16 16:04:25 -04:00
Frank 7779270252 wip: zen 2025-10-16 15:58:51 -04:00
GitHub Action 6527cb83a5 chore: format code 2025-10-16 19:55:30 +00:00
Adam 35ae9d47e3 wip: css/ui and desktop work 2025-10-16 14:53:44 -05:00
Aiden Cline 38f1364b4f fix: bash hangs & orphans (#3225) 2025-10-16 14:39:36 -05:00
Dax Raad f70fff2fb1 ci: fix 2025-10-16 15:36:12 -04:00
GitHub Action a6486eb91b chore: format code 2025-10-16 19:08:21 +00:00
Adam d5dc521a77 wip: css/ui and desktop work 2025-10-16 14:07:37 -05:00
Aiden Cline b1cea8b703 fix: connection closed
publish-github-action / publish (push) Waiting to run
2025-10-16 14:00:07 -05:00
Frank 87d347d1ae zen: support stripe link 2025-10-16 14:59:46 -04:00
Aiden Cline eb6b86c2c6 adjust action
publish-github-action / publish (push) Waiting to run
2025-10-16 13:15:27 -05:00
Adam 8f2667f395 wip: css/ui work 2025-10-16 18:06:59 +00:00
opencode 0e2bfb8a68 release: v0.15.5 2025-10-16 18:06:59 +00:00
Aiden Cline 9fd29cd301 ignore: tweak permissions 2025-10-16 12:48:39 -05:00
Aiden Cline 7e15176513 fix: github action (#3223) 2025-10-16 12:33:33 -05:00
Aiden Cline cc9f894b6a ci: fix changelog generation 2025-10-16 11:29:58 -05:00
Moisès Macià de19dd1d0a docs: fix misspelled word (#3211) 2025-10-16 10:04:11 -05:00
Dax Raad 2a49448e46 try to avoid persisting empty thinking/text blocks 2025-10-16 10:54:10 -04:00
opencode 20d03f40bb release: v0.15.4 2025-10-16 14:53:32 +00:00
GitHub Action dbb74ee4e0 ignore: update download stats 2025-10-16 2025-10-16 12:04:48 +00:00
seridescent c58da112ab fix: use ai-sdk openai chat language model instead of completion language model (#3204) 2025-10-16 00:59:49 -05:00
Aiden Cline 4973f281ff Revert "fix: Text content blocks must contain non-whitespace text" (#3200) 2025-10-15 20:02:17 -05:00
Dax Raad fa3cf830cb core: filter out alpha status models from provider list 2025-10-15 20:12:37 -04:00
Dax Raad fddfa2df68 ci: stuff 2025-10-15 19:59:46 -04:00
Dax Raad 093e6d7f88 snapshot publish everything 2025-10-15 19:53:14 -04:00
Dax Raad 1947cbd391 sync 2025-10-15 19:48:57 -04:00
Aiden Cline 01f635b7ba ignore: update readme 2025-10-15 16:06:11 -05:00
Aiden Cline 11d1eb5ccf Revert "fix: spawns hanging (#3192)"
This reverts commit 18fb335e6d.
2025-10-15 15:20:14 -05:00
Fabian Kukuck 5e394521d4 feat: make compact feature use streaming API (#3079)
Co-authored-by: fku <fabian.kukuck@ipt.ch>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-10-15 13:44:16 -05:00
Aiden Cline 18fb335e6d fix: spawns hanging (#3192) 2025-10-15 13:01:54 -05:00
Aiden Cline ac1b7b63b1 fix: Text content blocks must contain non-whitespace text (#3194) 2025-10-15 13:00:26 -05:00
Frank 5e074568b9 zen: support haiku 4.5 2025-10-15 13:53:00 -04:00
Matt Gillard e594d5a1ae updated bedrock provider for the new Australian sonnet 4.5 cross region inference (#3050)
Co-authored-by: Matt Gillard <matt-github@gillard.biz>
2025-10-15 11:09:22 -05:00
Haris Gušić a859ac532f Improve http error codes (#3186) 2025-10-15 10:53:09 -05:00
GitHub Action 4c3a4805e7 ignore: update download stats 2025-10-15 2025-10-15 12:04:51 +00:00
Frank 06a41df87b wip: zen 2025-10-15 02:55:01 -04:00
Aiden Cline 45caf66145 tweak: consolidate session lock logic (#3185) 2025-10-15 01:12:51 -05:00
Frank 557729caa0 wip: zen 2025-10-15 01:07:57 -04:00
Frank 2469578db8 wip: zen 2025-10-15 00:02:38 -04:00
Frank 471b915050 wip: zen 2025-10-14 23:45:06 -04:00
Frank f2698c4c4a wip: zen 2025-10-14 23:38:21 -04:00
Frank 333aadf23d wip: zen 2025-10-14 23:04:41 -04:00
Frank f5b00aa060 wip: zen 2025-10-14 23:03:13 -04:00
Dax Raad 38a2a69743 ci: run typecheck before tests to catch type errors early 2025-10-14 18:36:03 -04:00
Dax Raad 8aa4ea0eb1 try tsgo 2025-10-14 18:30:32 -04:00
Jay V bc10aca1d8 ignore: prompt 2025-10-14 17:45:10 -04:00
Jay V 6fef731af0 web: colocate copy button styles with components that use them 2025-10-14 17:41:17 -04:00
Frank 7052fff09f ci: fix 2025-10-14 17:22:29 -04:00
Frank 682a658819 wip: zen 2025-10-14 17:18:39 -04:00
Frank 3ab1bcd89b wip: zen 2025-10-14 17:13:21 -04:00
Frank 0a7e4d3320 wip: zen 2025-10-14 17:06:49 -04:00
Frank a4e0b35b9c wip: zen 2025-10-14 17:06:49 -04:00
Jay V fcf70461ac docs: improve MCP server configuration guidance with examples and caveats 2025-10-14 16:43:59 -04:00
Dax Raad 4170660b41 ci: channels 2025-10-14 15:09:18 -04:00
Dax Raad 926b8e37d7 sync 2025-10-14 14:57:34 -04:00
GitHub Action 889bafc911 chore: format code 2025-10-14 18:56:54 +00:00
Dax Raad d338407078 ci: test 2025-10-14 14:56:21 -04:00
Dax Raad d1aa28f417 ci: version stuff 2025-10-14 14:52:05 -04:00
Dax Raad 38bcff5998 ci: fix 2025-10-14 18:44:22 +00:00
opencode 00c74dbb72 release: v0.15.3 2025-10-14 18:44:21 +00:00
Dax Raad befa103109 ci: stuff 2025-10-14 14:35:04 -04:00
Aiden Cline 7520515273 fix: false positive package manager detection in upgrade (#3181) 2025-10-14 13:18:40 -05:00
Frank d15110074c ci: fix 2025-10-14 13:58:54 -04:00
Frank e91a051982 wip: zen 2025-10-14 13:52:30 -04:00
GitHub Action 1aa3dd2b56 chore: format code 2025-10-14 17:08:18 +00:00
Adam cf479404c3 wip: css/ui work 2025-10-14 12:07:45 -05:00
Adam 6b88402f08 wip: css/ui work 2025-10-14 12:06:18 -05:00
Adam b8cd2b62ca wip: desktop work 2025-10-14 12:06:17 -05:00
Netanel Draiman 97c4179bc5 feat(cli): add session option to attach command (#3167) 2025-10-14 11:04:32 -05:00
Adam afc475f9a4 wip: css and ui packages 2025-10-14 07:16:24 -05:00
Adam 4af079fd59 wip: desktop work 2025-10-14 07:15:08 -05:00
Adam 17f1cb3664 wip: desktop work 2025-10-14 07:15:08 -05:00
GitHub Action b4911ef1f1 ignore: update download stats 2025-10-14 2025-10-14 12:04:50 +00:00
Dax cacb086356 feat: use realtime events for live tool call updates in Slack (#3163) 2025-10-14 03:13:48 -04:00
Dax Raad 15409fc218 fix: update tsconfig for Slack package 2025-10-14 02:55:21 -04:00
Dax Raad de634a3f13 fix: export trimDiff function from edit tool 2025-10-14 02:55:02 -04:00
Dax Raad 07ac47ac3d fix: resolve TypeScript errors in SDK and Slack package 2025-10-14 02:54:37 -04:00
Dax Raad c64b823198 feat: add Slack integration package with Bolt framework 2025-10-14 02:53:55 -04:00
Dax Raad 87ff866318 core: improve dependency management and error handling for more reliable builds 2025-10-14 01:33:25 -04:00
Dax Raad b03301a967 core: standardize dependency versions for better reliability
Ensures consistent versions across packages by using workspace catalog for
tailwindcss and tsconfig dependencies, reducing potential conflicts and
installation issues.
2025-10-14 01:27:17 -04:00
Dax Raad 6e591ce3cb core: reduce dependency conflicts by standardizing package versions through catalog
This eliminates duplicate package versions that were causing build issues and
inconsistent behavior across the monorepo. Dependencies now resolve to single
versions through the workspace catalog, making installs faster and more reliable.
2025-10-14 01:23:54 -04:00
opencode 08840f2f32 release: v0.15.2 2025-10-14 04:56:00 +00:00
Alberto Fanton 17f1bb96f4 fix: disable GPG signing in snapshot tests (#3102) 2025-10-13 23:40:41 -05:00
Aiden Cline b22df18e0f fix: config dir overrides (#3160) 2025-10-13 23:25:53 -05:00
Aiden Cline 0beda25798 fix: custom model (#3156) 2025-10-13 19:58:19 -05:00
pancake 841135c435 fix: clang formatter name (#3042)
Co-authored-by: pancake <pancake@nopcode.org>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-10-13 19:55:18 -05:00
Haris Gušić d4bc87f3c1 fix: make shell more robust (#3051) 2025-10-13 17:37:35 -05:00
maple 67e5252734 docs: typo in custom-tools.mdx (#3152) 2025-10-13 17:19:20 -05:00
Aiden Cline a0600e82d9 fix: typecheck (#3149) 2025-10-13 14:51:12 -05:00
AB 15d5c03f64 fix: add useCompletionUrls option to fix certain azure setups (#2528)
Co-authored-by: andreas.blass <andreas.blass@outlook.com>
Co-authored-by: GitHub Action <action@github.com>
2025-10-13 14:16:21 -05:00
Tommy D. Rossi e6373142c1 tweak: include stack trace in server error responses (#3134) 2025-10-13 14:10:35 -05:00
opencode 65b9b7acf0 release: v0.15.1 2025-10-13 18:14:52 +00:00
Dax Raad 6cc97ede65 fix: disable workspace symbol lookup to prevent LSP performance issues 2025-10-13 14:05:54 -04:00
Aiden Cline f8132999b6 fix: pass options to compact (#3136) 2025-10-13 10:42:39 -05:00
Aiden Cline 40f77361d2 docs: update recommended models list (#3121) 2025-10-12 21:35:31 -05:00
Aiden Cline 85bd863914 fix: adjust list tool prompt to handle cwd better (#3115) 2025-10-12 16:48:03 -05:00
Ravshan Samandarov 7122edc44f docs: Update README.md (#3100) 2025-10-12 16:23:25 -04:00
Jay 864a53775a docs: Fix formatting of num_ctx in providers.mdx 2025-10-12 16:01:32 -04:00
Aiden Cline 083fb7075f docs: ollama num_ctx (#3111) 2025-10-12 10:40:51 -05:00
GitHub Action 5c26d08fe7 ignore: update download stats 2025-10-12 2025-10-12 12:04:09 +00:00
OpeOginni 2e870df2b7 fix: add timeout to fetch models.dev refresh request (#3059) 2025-10-12 00:20:22 -05:00
opencode 8680e728d7 release: v0.15.0 2025-10-12 05:12:15 +00:00
Dax Raad e025027d20 ci: centralize bun setup to reduce duplication and improve caching 2025-10-12 00:46:37 -04:00
Dax Raad bac8e924be ci: tweak 2025-10-12 00:40:59 -04:00
Tommy D. Rossi c015ced81f fix: rg hanging forever when run in bash, waiting for stdin (#3103)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-10-11 23:24:48 -05:00
Dax Raad 4951bc5515 ci: re-enable aur 2025-10-12 00:22:16 -04:00
Frank 8f37e46b49 wip: zen 2025-10-11 16:17:39 -04:00
Frank b5114e1aea ci: fix 2025-10-11 15:08:45 -04:00
Frank 47a76e92d5 wip: zen 2025-10-11 15:07:06 -04:00
Frank 59a3df970f wip: zen 2025-10-11 14:48:34 -04:00
Frank cbe402ca4a ci: fix 2025-10-11 14:41:52 -04:00
Frank 39262874ec wip: zen 2025-10-11 14:38:53 -04:00
Frank 45e55b730b Bump sst to latest 2025-10-11 14:38:45 -04:00
Frank cfc3b20826 wip: zen 2025-10-11 10:51:17 -04:00
Frank a1f7dc98fa wip: zen 2025-10-11 09:29:26 -04:00
Frank eb20c411b9 wip: zen 2025-10-11 08:27:32 -04:00
Frank c8f39d6065 wip: zen 2025-10-11 08:27:32 -04:00
Frank b7478b7be3 wip: zen 2025-10-11 08:27:32 -04:00
GitHub Action a31601dd37 ignore: update download stats 2025-10-11 2025-10-11 12:04:02 +00:00
Frank 7e8c7c329a wip: zen refactor selector 2025-10-11 07:54:57 -04:00
Frank 64d857aa08 wip: zen fix drop down style 2025-10-11 07:15:19 -04:00
Frank 0d7543c5b4 wip: zen fix template 2025-10-11 06:33:47 -04:00
Dax Raad 0271b3847f ci: fixes 2025-10-11 00:24:35 -04:00
Dax Raad a2e1a61cfb ci: stuff 2025-10-11 00:06:46 -04:00
Dax Raad 5685689512 ci: fix 2025-10-10 23:43:32 -04:00
Frank d180adb7fc wip: zen 2025-10-10 21:26:16 -04:00
Frank efa75c9697 Merge branch 'console-workspaces' into dev 2025-10-10 21:24:05 -04:00
Frank dfd154e760 wip: zen 2025-10-10 21:21:55 -04:00
Frank 70bd275955 wip: zen 2025-10-10 20:32:28 -04:00
Frank 77c0b1b724 wip: zen 2025-10-10 20:30:13 -04:00
Frank 2222036bd2 wip: zen 2025-10-10 20:16:44 -04:00
Frank d93dcc7a0a wip: zen 2025-10-10 20:02:17 -04:00
Frank 3026dafb3d wip: zen 2025-10-10 19:56:40 -04:00
Frank 3b30bdf8ec wip: zen 2025-10-10 19:49:59 -04:00
Frank 2a9f5bbd38 wip: zen 2025-10-10 19:39:01 -04:00
Dax Raad 171c591728 ci: fixes 2025-10-10 18:17:10 -04:00
Dax Raad fd928f3692 ci: fix 2025-10-10 18:11:19 -04:00
Dax Raad a35544f5a2 ci: stuff 2025-10-10 18:04:08 -04:00
Dax Raad d574e62490 ci: stuff 2025-10-10 17:56:33 -04:00
Dax Raad 93c84b47ae upgrade to bun 1.3.0 2025-10-10 17:53:32 -04:00
Frank b0a6e71560 wip: zen 2025-10-10 16:42:27 -04:00
Frank b97f117628 wip: zen 2025-10-10 16:34:07 -04:00
Frank 9e482a74ae wip: zen 2025-10-10 16:04:06 -04:00
Frank 2aa7d6469a wip: zen 2025-10-10 14:54:49 -04:00
Frank c278117d31 wip: zen 2025-10-10 14:19:06 -04:00
Frank 5f04fe3783 wip: zen 2025-10-10 14:11:48 -04:00
Frank 82c0dff231 wip: zen 2025-10-10 13:52:54 -04:00
Frank 5722df88b8 wip: zen style members 2025-10-10 13:48:56 -04:00
Frank 8fa81a2f30 wip: zen style api keys 2025-10-10 13:45:06 -04:00
Frank dcc0d265d8 wip: zen 2025-10-10 13:26:39 -04:00
Frank df3df34f58 wip: zen 2025-10-10 13:21:51 -04:00
Frank e216501503 wip: zen 2025-10-10 12:46:42 -04:00
Rustafarian Dev b2f35389fb fix: perl6 file extension (#3066) 2025-10-10 11:28:49 -05:00
Frank 218e6f1b3d wip: zen 2025-10-10 12:22:36 -04:00
Frank d397a4e236 wip: sync 2025-10-10 12:16:57 -04:00
Frank 7bbb3a4dcf wip: zen settings 2025-10-10 12:04:02 -04:00
Frank c9ab1ab8aa wip: zen 2025-10-10 09:03:49 -04:00
GitHub Action c5e080fc6f ignore: update download stats 2025-10-10 2025-10-10 12:04:18 +00:00
Frank bff8338a97 wip: zen style byok 2025-10-10 03:15:55 -04:00
Frank 09dbfd3e28 wip: zen style byok 2025-10-10 02:53:05 -04:00
Frank 2bf5d8d823 wip: zen style byok 2025-10-10 02:37:50 -04:00
Frank 931dc855ce wip: style byok 2025-10-10 02:34:06 -04:00
Frank 5de6ade823 wip: zen 2025-10-10 01:36:15 -04:00
Frank 95f3c7fda5 wip: zen style nav bar 2025-10-10 00:56:16 -04:00
Frank 36c73b3046 wip: zen style model 2025-10-10 00:02:04 -04:00
Dax Raad f7d8b41976 core: improve directory validation error messages to help users fix invalid directory names 2025-10-09 22:40:23 -04:00
Frank d4f0b8b483 wip: zen style header 2025-10-09 22:38:42 -04:00
Dax Raad 367cf7eac3 ensure @opencode-ai/plugin is available in .opencode folder 2025-10-09 21:18:49 -04:00
opencode 7ddc833674 release: v0.14.7 2025-10-10 01:02:37 +00:00
Dax Raad 1076fc244c drop excess dependency in opencode sdk 2025-10-09 20:55:25 -04:00
Dax Raad a0844a6b85 core: use platform-specific watcher backends for better file system monitoring 2025-10-09 18:29:18 -04:00
Dax Raad 21e2f45f77 core: replace chokidar with @parcel/watcher for better performance and cross-platform support 2025-10-09 18:21:38 -04:00
Frank 000ed1a066 wip: zen 2025-10-09 17:18:55 -04:00
Dax Raad c50d7a4275 ignore: add common build and framework directories to ignore list 2025-10-09 16:35:56 -04:00
Frank 9e50700b50 wip: zen nav bar 2025-10-09 16:01:52 -04:00
Dax Raad 92f106f740 ignore: improve file ignore performance and cross-platform support
- Replace glob patterns with Set lookup for common folders to speed up matching
- Use path.sep for cross-platform compatibility on Windows/Unix systems
- Add comprehensive test coverage for nested and non-nested folder matching
- Simplify implementation by removing unnecessary caching complexity
2025-10-09 15:54:01 -04:00
Adam 611a719fb5 feat: experimental skip bootstrap 2025-10-09 14:51:11 -05:00
Aiden Cline bf447bd5ae docs: fix bugged example (#3068) 2025-10-09 12:21:44 -05:00
Jay V 84a75f3d93 docs: adding tools doc 2025-10-09 13:19:51 -04:00
Jay V b407c00c84 docs: document ripgrep .ignore file override in tools 2025-10-09 13:19:51 -04:00
Jay V 38aeafea30 docs: adding spellcheck command 2025-10-09 13:19:51 -04:00
Aiden Cline 73cc3e1a95 feat: allow read tool to handle images (#3052) 2025-10-09 09:05:11 -05:00
GitHub Action 330e5c3386 ignore: update download stats 2025-10-09 2025-10-09 12:04:27 +00:00
Dax Raad 03d8a3f6a2 lsp: fix root detection to use instance directory instead of worktree 2025-10-09 04:30:30 -04:00
Dax Raad 73c7dacc09 lsp: simplify language server root detection to use lock files
Improves project boundary detection by focusing on package manager lock files instead of config files, providing more reliable workspace identification across different project types.
2025-10-09 04:22:38 -04:00
Haris Gušić bf2382d900 fix: process.stdout.write instead of console.log for export cmd (#3049) 2025-10-09 00:46:19 -05:00
Frank f5fe5252d7 wip: zen 2025-10-08 22:33:20 -04:00
Frank ceca472e8c wip: zen 2025-10-08 18:59:41 -04:00
Frank 28335d273b wip: zen 2025-10-08 17:03:42 -04:00
Frank 5df90e43df wip: zen 2025-10-08 15:20:50 -04:00
Frank 9f5111d21f wip: zen 2025-10-08 13:31:15 -04:00
Jay V d02ff9c5b1 docs: fix permission docs 2025-10-08 12:13:42 -04:00
GitHub Action 7717e3f355 ignore: update download stats 2025-10-08 2025-10-08 12:04:31 +00:00
Adam cfe96f6551 fix(tui): suggestions gap on home page 2025-10-08 06:56:18 -05:00
Dax Raad 8fcf52a3cd add free workspace 2025-10-08 02:27:01 -04:00
Frank 861fdc01cf wip: zen 2025-10-08 01:14:39 -04:00
Frank df53506975 wip: zen 2025-10-08 00:03:36 -04:00
opencode 7cae3cc86a release: v0.14.6 2025-10-07 19:59:08 +00:00
Dax Raad b47f5ac785 fix issue with blank new version popup 2025-10-07 15:51:59 -04:00
Frank 9071c94b13 wip: zen 2025-10-07 13:37:38 -04:00
Aiden Cline 21d4459d5e docs: troubleshooting ProviderModelNotFoundError (#3016) 2025-10-07 11:50:37 -05:00
Sai 6f4e3f519e docs: add agent specific permission example (#3009) 2025-10-07 10:08:52 -05:00
Frank cdd652e830 wip: zen 2025-10-07 09:17:08 -04:00
GitHub Action a6491a2f4e ignore: update download stats 2025-10-07 2025-10-07 12:04:53 +00:00
Dax Raad 06fc3bc28f core: improve MCP reliability and add status monitoring
- Added 5-second timeout to MCP client verification to prevent hanging connections
- New GET /mcp endpoint to monitor server connection status
- Automatically removes unresponsive MCP clients during initialization
2025-10-07 04:04:19 -04:00
opencode b5ff5ae621 release: v0.14.5 2025-10-07 06:21:31 +00:00
Aiden Cline cbf11adac7 fix: mcp error (#3006) 2025-10-07 00:45:46 -05:00
Aiden Cline 546c4667a6 tweak: grep tool to handle single file better (#3004) 2025-10-06 23:24:00 -05:00
Frank 83253ffabf wip: zen 2025-10-06 23:57:55 -04:00
Frank e52712ca97 wip: zen 2025-10-06 23:57:54 -04:00
opencode 9389c710f9 release: v0.14.4 2025-10-07 03:32:10 +00:00
Dax Raad ac12364c27 ignore: 2025-10-06 23:25:01 -04:00
Dax Raad cb40e8c622 core: restore bash command security validation to prevent accidental directory traversal
The permission validation that prevents commands from accessing paths outside the project directory was accidentally disabled, which could allow commands like 'cd ../' to escape the workspace. This restores the security check that keeps your commands safely contained within your project boundaries.
2025-10-06 23:24:18 -04:00
Dax Raad 87f71e2183 core: improve session API reliability with proper input validation 2025-10-06 19:37:44 -04:00
Dax Raad fb6713c2b8 Update todo tool to use centralized Todo module 2025-10-06 18:54:05 -04:00
Dax Raad b7e953c077 Add missing files and fix type aliases for opentui features 2025-10-06 18:53:35 -04:00
Dax Raad a06cba9d2e Add todo list and session forking API endpoints 2025-10-06 18:51:57 -04:00
Dax Raad 389bc1f8da Add session forking functionality and simplify remove logic 2025-10-06 18:50:56 -04:00
Frank eeff55f681 wip: zen 2025-10-06 17:23:10 -04:00
Frank 176069efe8 wip: zen 2025-10-06 17:17:02 -04:00
Frank 994a0842eb wip: zen 2025-10-06 17:13:19 -04:00
Frank 687d68eee6 wip: zen 2025-10-06 17:13:19 -04:00
Jay V 81faff475f docs: update footer 2025-10-06 17:05:45 -04:00
Jay V ac88e0937c docs: fix styles and zen doc, closes #2912 2025-10-06 17:00:10 -04:00
Jay V e9729e4b8c docs: share page css 2025-10-06 16:13:21 -04:00
GitHub Action a9230df6b8 ignore: update download stats 2025-10-06 2025-10-06 12:04:17 +00:00
Aiden Cline 140f1a0600 fix: add timeout message if command times out (#2986) 2025-10-05 23:55:01 -05:00
Mani Sundararajan 6d6fc43bb5 fix: file references & grep tool for windows (#2980) 2025-10-05 14:32:07 -05:00
GitHub Action 181c38c9b6 ignore: update download stats 2025-10-05 2025-10-05 12:03:55 +00:00
opencode 601b282459 release: v0.14.3 2025-10-05 11:22:57 +00:00
Dax Raad b57897e377 ci: publish 2025-10-05 07:14:52 -04:00
Dax Raad 62a52c5539 ci: disable aur 2025-10-05 07:12:33 -04:00
Dax Raad fd61b7dbc0 sdk: simplify getting started with single createOpencode function
Makes it easier for developers to get started by providing a single function that creates both server and client, removing the need to manually coordinate separate server and client creation
2025-10-05 07:01:32 -04:00
Dax Raad b59be49095 sdk: simplify getting started with single createOpencode function
Makes it easier for developers to get started by providing a single function that creates both server and client, removing the need to manually coordinate separate server and client creation
2025-10-05 07:00:29 -04:00
Aiden Cline 431312634c fix: max output tokens when using large thinking budget (#2976) 2025-10-04 23:38:41 -05:00
Dax Raad 9348fdec34 sync 2025-10-04 21:33:47 -04:00
Aiden Cline 8f6c63c017 fix: allow LSP filename matching when extension is missing (#2975) 2025-10-04 20:30:53 -05:00
iwauo 4603820839 docs: java-lsp support (#2958) 2025-10-04 11:28:09 -05:00
GitHub Action 7b359f8cd8 ignore: update download stats 2025-10-04 2025-10-04 12:03:53 +00:00
Frank a357abefe1 wip: zen 2025-10-04 01:12:32 -04:00
Yuku Kotani 4c60276d10 feat: Vertex AI support; add google-vertex and google-vertex-anthropic providers (#2347) 2025-10-04 01:10:38 -04:00
Aiden Cline 929ef83d3d add command to debug config (#2962) 2025-10-03 23:07:58 -05:00
Frank 7b627b7610 wip: zen 2025-10-03 23:48:34 -04:00
Dax Raad a61ca2e5bf ci: try regional hostname again 2025-10-03 19:05:32 -04:00
Rovshan Muradov 247f9f8e10 docs: Update models.mdx (#2916) 2025-10-03 17:06:20 -04:00
Frank e55a161f4d wip: zen 2025-10-03 16:32:53 -04:00
Jay V b7fc1a16f9 docs: rename opencode to OpenCode 2025-10-03 13:46:56 -04:00
Frank 31534913a4 wip: zen 2025-10-03 12:54:52 -04:00
Adam 165959715e fix: github stats 2025-10-03 09:34:17 -05:00
Adam bc9d3b308a fix: user-agent 2025-10-03 09:30:51 -05:00
Adam 4ac32d5932 chore: user-agent header 2025-10-03 09:27:12 -05:00
Adam 18d58612ad chore: logging errors 2025-10-03 09:19:54 -05:00
Adam 28bb12bae2 chore: logging errors 2025-10-03 09:10:33 -05:00
Adam 9cad9ffc23 chore: app -> desktop 2025-10-03 09:04:28 -05:00
Frank 44e295600a wip: zen 2025-10-03 08:25:51 -04:00
GitHub Action 7cf05a027b ignore: update download stats 2025-10-03 2025-10-03 12:04:07 +00:00
Frank 0fe315d210 wip: zen 2025-10-03 07:36:16 -04:00
Frank c688305dfc wip: zen 2025-10-03 07:36:16 -04:00
Frank 97a825a8b6 wip: zen 2025-10-03 07:36:16 -04:00
Dax Raad fbfaf15bfc ci: stuff 2025-10-03 10:53:10 +00:00
opencode e4c33da6d4 release: v0.14.1 2025-10-03 10:53:10 +00:00
Dax Raad 2cb3255556 turn on codex medium reasoning again 2025-10-03 06:46:07 -04:00
David Hill cc62b8d8b6 Firefox email input fix 2025-10-02 23:50:51 +01:00
Jay 61003ff55d docs: update theme (#2929)
Co-authored-by: David Hill <iamdavidhill@gmail.com>
2025-10-02 23:20:30 +01:00
David Hill 95daf247d3 Update index.css 2025-10-02 23:19:53 +01:00
Jay 1952e5f46f ignore: Workspace updates (#2930)
Co-authored-by: David Hill <iamdavidhill@gmail.com>
2025-10-02 23:18:57 +01:00
Frank 68fd879f76 wip: zen 2025-10-02 18:16:29 -04:00
Frank af83f14bb7 wip: zen 2025-10-02 17:55:54 -04:00
Frank b58c12d47a wip: zen 2025-10-02 16:09:42 -04:00
Frank 9168c974c5 wip: zen 2025-10-02 13:58:40 -04:00
Jay cc4e4d37d1 docs: README 2025-10-02 12:33:32 -04:00
Jay V c31b0ec014 docs: update README 2025-10-02 12:31:58 -04:00
David Hill 4659e0ed35 Copy tweaks 2025-10-02 17:02:33 +01:00
David Hill 215943e39f Border top only on safari fix 2025-10-02 16:55:48 +01:00
David Hill 493d9194d7 Update favicon.svg 2025-10-02 16:55:29 +01:00
Aiden Cline f58fe4e866 fix: run cmd json format when running command (#2926) 2025-10-02 10:37:42 -05:00
David Hill 8831a57e5f Style fixes 2025-10-02 16:35:23 +01:00
David Hill e1e0015be4 Update dock.png 2025-10-02 16:24:14 +01:00
David Hill fdbdf5ef20 Update dock.png 2025-10-02 16:14:52 +01:00
David Hill a3711c0c81 Email input color fix 2025-10-02 16:11:55 +01:00
David Hill 7b23a64e82 Update dock.png 2025-10-02 15:50:56 +01:00
David Hill 2164190b93 Testimonial tweaks 2025-10-02 15:37:36 +01:00
David Hill 78cac1a32c Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-10-02 15:37:13 +01:00
David Hill f1a916d788 faq icon fix 2025-10-02 15:37:10 +01:00
Frank 50f2bf2432 ignore: fix 2025-10-02 10:10:07 -04:00
David Hill 08fe1472a3 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-10-02 15:03:59 +01:00
David Hill f3ca14cff4 Body background fix 2025-10-02 15:03:56 +01:00
David Hill b614a6f03a Mobile nav icon fix 2025-10-02 15:02:57 +01:00
David Hill a69708f3f3 Mobile nav icon fix 2025-10-02 15:02:44 +01:00
David Hill 5c2fea2b30 Mobile nav fix 2025-10-02 15:01:22 +01:00
Adam cc58dbe6b4 fix: broken links 2025-10-02 09:00:33 -05:00
David Hill db10eac89e Update posters for videos 2025-10-02 14:55:38 +01:00
David Hill fcded3dc6e Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-10-02 14:54:16 +01:00
David Hill 38b587153e Testimonial tweak 2025-10-02 14:53:54 +01:00
Adam 14ad4e4318 chore: add email signup back 2025-10-02 08:46:25 -05:00
David Hill ad4d778e31 Update faq.tsx 2025-10-02 14:42:49 +01:00
David Hill e1f0b4d247 Faq icon color fix 2025-10-02 14:41:57 +01:00
Adam decfbeee01 fix: testimonial link 2025-10-02 08:34:22 -05:00
Adam 99e438c806 wip: desktop work 2025-10-02 08:34:01 -05:00
GitHub Action 5f1ac0af9b ignore: update download stats 2025-10-02 2025-10-02 12:04:06 +00:00
opencode dc159ecfc1 release: v0.14.0 2025-10-02 05:33:58 +00:00
Dax Raad 802334c3e9 ci: fix 2025-10-02 01:25:27 -04:00
Dax Raad 5e390b15d3 ci: publsih 2025-10-02 01:24:33 -04:00
Dax Raad 3195f8eb80 ci: test 2025-10-02 01:19:53 -04:00
Dax Raad 0ee8e1b05f ci: install opencode 2025-10-02 01:19:53 -04:00
Dax Raad 5dfb03ea0a ci: fix 2025-10-02 05:18:01 +00:00
opencode 93b3607250 release: v0.13.9 2025-10-02 05:18:01 +00:00
Dax Raad 9f73e3c9b6 ci: sync 2025-10-02 01:09:58 -04:00
Dax Raad 43c7b7a765 ci: stuff 2025-10-02 01:08:29 -04:00
Aiden Cline b842cc5e7c docs: fix install fmt (#2914) 2025-10-01 23:32:39 -05:00
Aiden Cline 8fb3c65eee docs: fix more links (#2913) 2025-10-01 23:22:17 -05:00
Sandip Wane cf363fe67a chore: rm empty try-catch block (#2769)
Co-authored-by: Sandip Wane <sandip.wane@cloudhedge.io>
2025-10-01 23:21:57 -05:00
Dax Raad d0008024f1 core: fix file search limit handling and ensure File module initialization 2025-10-02 00:18:18 -04:00
opencode f7c5281d47 release: v0.13.8 2025-10-02 04:16:48 +00:00
Dax Raad 8cd3078df4 core: improve file search reliability and performance 2025-10-02 00:09:02 -04:00
Aiden Cline 628a15d382 docs: fix (#2910) 2025-10-01 22:20:35 -05:00
Jay V ab51603d1b docs: hide email signup 2025-10-01 20:16:02 -04:00
Jay V f3d895f652 docs: testimonials 2025-10-01 19:57:17 -04:00
Dax Raad e220fa841a wip: lander 2025-10-01 19:54:58 -04:00
Jay V 30feacb006 docs: fix link 2025-10-01 19:52:31 -04:00
Dax Raad cb4c962d71 wip: lander 2025-10-01 19:49:00 -04:00
Dax Raad c3416825d8 ignore: fix 2025-10-01 19:40:56 -04:00
Dax dcce094f1e Zen lander (#2907)
Co-authored-by: David Hill <iamdavidhill@gmail.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
Co-authored-by: Jay V <air@live.ca>
2025-10-01 19:38:15 -04:00
Frank 3cdb7b2d6f wip: zen 2025-10-01 19:34:37 -04:00
Frank 69d504f4be doc: update zen pricing 2025-10-01 17:48:12 -04:00
GitHub Action f84e912391 ignore: update download stats 2025-10-01 2025-10-01 12:04:26 +00:00
Dax Raad 7810be5885 core: remove redundant patch integration test
The integration test was duplicating coverage already provided by the comprehensive
patch namespace tests. Users benefit from faster test runs without losing any
coverage of patch functionality. The remaining tests provide complete validation
of patch parsing, application, and tool integration.
2025-10-01 06:49:19 -04:00
Dax Raad c338173f7a core: make patch tool more reliable and consistent with other editing tools
The patch tool now works seamlessly alongside other file editing tools with improved
error handling and a more intuitive permission system. Users will experience:

- More reliable patch application with better error messages
- Consistent permission prompts that match other editing tools
- Smoother integration when applying complex multi-file changes
- Better feedback on what changes are being made before applying patches

This refactoring leverages the robust patch parsing engine while making the tool
feel native to the opencode workflow, reducing friction when making bulk changes
to your codebase.
2025-10-01 06:45:43 -04:00
opencode 71da1ec86a release: v0.13.7 2025-10-01 09:13:16 +00:00
Dax Raad 282c1e1d9d codex should not have reasoning effort 2025-10-01 05:06:37 -04:00
Dax Raad ee4e201045 core: prevent file deletion when reverting changes to existing files 2025-10-01 05:06:37 -04:00
opencode 2e1ed2fc65 release: v0.13.6 2025-10-01 07:44:18 +00:00
Dax Raad 8ad4813f46 overhaul file search and support @ mentioning directories 2025-10-01 03:37:01 -04:00
Aiden Cline af71bc7084 fix: adjust model dialog to handle same model id but different names (#2881) 2025-09-30 11:43:57 -05:00
GitHub Action c59928a840 ignore: update download stats 2025-09-30 2025-09-30 12:04:27 +00:00
Dax Raad a82a7f8bd9 update anthropic system prompts 2025-09-30 04:41:36 -04:00
Aiden Cline 64b3b456ff fix: tui stuck saying generating... even when it is done (#2872) 2025-09-29 23:55:47 -05:00
Aiden Cline 539d438d97 fix: worktree cmd (#2870) 2025-09-29 22:21:54 -05:00
Frank abbae010da wip: zen 2025-09-29 14:17:53 -04:00
opencode 6700d00543 release: v0.13.5 2025-09-29 14:12:58 +00:00
Aiden Cline 08b8ff2671 fix: bash regex (#2858) 2025-09-29 08:51:46 -05:00
GitHub Action 4e21bfce23 ignore: update download stats 2025-09-29 2025-09-29 12:04:29 +00:00
Giuseppe Rota edf274ff9d docs: document model id behavior (#2856) 2025-09-29 06:52:26 -05:00
opencode 83484d52de release: v0.13.4 2025-09-29 05:53:07 +00:00
Dax Raad 370a9572ed ci: fix homebrew 2025-09-29 01:40:27 -04:00
Dax Raad 3f2da89007 release: v0.13.2 2025-09-29 01:38:53 -04:00
Dax Raad 112a3685fb ci: publish 2025-09-29 01:24:35 -04:00
Dax Raad 3cb9218b98 ci: fix 2025-09-29 01:22:39 -04:00
Dax Raad 3542e6b051 ci: fix 2025-09-29 01:06:12 -04:00
Aiden Cline 2a41938ad8 ignore: go mod tidy (#2851) 2025-09-28 23:05:56 -05:00
Frank 75d871fd49 wip: zen 2025-09-28 19:55:40 -04:00
Frank 12740d1d17 wip: zen 2025-09-28 19:55:40 -04:00
Dax Raad adab4bcddf core: improve webfetch tool content negotiation and format handling 2025-09-28 17:57:50 -04:00
Frank f6406c2a60 wip: zen 2025-09-28 12:49:28 -04:00
GitHub Action 4be998a4f5 ignore: update download stats 2025-09-28 2025-09-28 12:03:57 +00:00
Frank 27c0709de8 wip: zen 2025-09-27 21:53:50 -04:00
Aiden Cline e1e0b58991 respect model id in opencode.json (#2833) 2025-09-27 17:09:21 -05:00
Aurélien Tollard e98b50d197 Fix: Set OPENCODE_CALLER env in a more portable way for vscode extension (#2230) 2025-09-27 10:36:24 -04:00
Frank be0a55d146 wip: zen 2025-09-27 10:19:58 -04:00
Frank c012dbc85a wip: zen 2025-09-27 10:05:19 -04:00
GitHub Action 3913040ea1 ignore: update download stats 2025-09-27 2025-09-27 12:04:01 +00:00
Dax Raad 6de9e29979 ci: tweaks 2025-09-27 04:12:55 -04:00
Dax Raad f5218e2577 sync 2025-09-27 04:10:56 -04:00
Arjun Singh 72295261e6 Include step-start and step-finish for cost tracking (#2810) 2025-09-27 03:45:36 -04:00
Dax Raad 8eee14c19c ci: wtf 2025-09-27 03:20:08 -04:00
Dax Raad 96e5bfdd4f ci: fix 2025-09-27 03:19:34 -04:00
Dax Raad 139113f9c3 core: configure turbo to avoid building opencode for web tests 2025-09-27 03:19:29 -04:00
Dax Raad d69068dabd ci: fix 2025-09-27 03:18:12 -04:00
Dax Raad 3a20194084 ci: test 2025-09-27 03:17:22 -04:00
Dax Raad 1908ed7a49 core: fix config test by removing model field expectation 2025-09-27 03:10:01 -04:00
Dax Raad 29541c54ce ci: fix git identity in test workflow 2025-09-27 03:07:37 -04:00
Dax Raad fc1cf01a26 ci: improve test coverage 2025-09-27 03:04:42 -04:00
Dax Raad 56d0e8acbf sync 2025-09-27 02:53:20 -04:00
Dax Raad ffe37d6d98 ci: format 2025-09-27 02:22:03 -04:00
Dax Raad 4855f3133d wip: bun test improvements 2025-09-27 02:17:08 -04:00
Dax Raad 7eba61cd25 ci: give up 2025-09-27 01:51:54 -04:00
Dax Raad b24319fe80 ci: add regional hostname 2025-09-27 01:28:11 -04:00
Dax Raad 7bb0ff2fc6 fix zen cookie 2025-09-27 01:12:25 -04:00
opencode cec0844cd6 release: v0.12.1 2025-09-26 21:18:45 +00:00
Frank 27bbecd0e1 zen: accept tax id 2025-09-26 17:10:45 -04:00
Timo Clasen 76f06b08cb fix(tool): follow symlinks when looking for tools (#2809) 2025-09-26 16:01:08 -05:00
opencode 5c58cd7b3f release: v0.12.0 2025-09-26 20:55:00 +00:00
Frank 95313b660e zen: model management helper 2025-09-26 15:18:24 -04:00
sonsulee fb9ca7162c docs: add TUI configuration options and examples (#2212)
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Jay <air@live.ca>
2025-09-26 13:09:46 -04:00
Yihui Khuu cfb031681e fix: config loading not considering symlinks (#2800) 2025-09-26 09:46:49 -05:00
GitHub Action a053b93ac8 ignore: update download stats 2025-09-26 2025-09-26 12:04:26 +00:00
Dax Raad 05767142ca ci: fix deploy 2025-09-26 11:57:49 +00:00
opencode f669e88e2a release: v0.11.8 2025-09-26 11:57:49 +00:00
Dax Raad eb38e0b40e disable aggressive config validation 2025-09-26 07:51:53 -04:00
opencode 76fc05e0e3 release: v0.11.7 2025-09-26 11:19:15 +00:00
Adam d9bd7de976 fix(tui): cursor position 2025-09-26 06:13:23 -05:00
Dax Raad 0e65537c97 core: add themes to allowed config directories 2025-09-26 06:40:41 -04:00
opencode 48d6bbc90c release: v0.11.6 2025-09-26 09:52:23 +00:00
Dax Raad 180f493ac0 tui: fix cursor position offset in home screen 2025-09-26 05:46:16 -04:00
opencode ca1622aef9 release: v0.11.5 2025-09-26 09:40:16 +00:00
Dax Raad 766ca1b32a fix erroring on custom tool folder 2025-09-26 05:33:38 -04:00
Dax Raad 9afee84247 validate config directory 2025-09-26 03:23:25 -04:00
Dax Raad 6b4c0076e8 core: add config update endpoint and functionality 2025-09-26 02:37:19 -04:00
Dax Raad e314b5a3b8 ci: ignore 2025-09-26 02:01:19 -04:00
Dax Raad ef37a4fe55 ci: setup husky pre-push hook to run typecheck 2025-09-26 02:01:19 -04:00
opencode f4b2b4934b release: v0.11.4 2025-09-26 05:56:03 +00:00
Dax Raad 8c44ba84cd tui: fix opencode logo spacing in home view 2025-09-26 01:40:32 -04:00
Dax Raad fd44632de8 turn reasoning summaries back on by default for zen 2025-09-26 01:37:44 -04:00
Dax Raad 50fd5fc640 core: enable reasoning.encrypted_content and reasoningSummary for opencode provider\ntui: adjust editorY position calculation 2025-09-26 01:27:53 -04:00
Aiden Cline 428b443795 fix: openai reasoning issue (#2780) 2025-09-26 01:23:30 -04:00
Aiden Cline d467db43b0 tweak: include usage by default for openai compatible providers (#2788) 2025-09-25 21:06:58 -05:00
David Hill d11ad9226a Update opencode logo 2025-09-25 23:22:50 +01:00
Frank fc262ad255 zen: track tps 2025-09-25 17:56:41 -04:00
Frank cfbe5fd908 zen: check balance on enable billing 2025-09-25 17:47:50 -04:00
Frank 42263fe0c5 zen: fix parsing cache write tokens 2025-09-25 17:02:23 -04:00
Adam 22b70d6510 wip: better desktop file status state and timeline 2025-09-25 14:41:31 -05:00
Adam 0d05ab5be7 wip: watch select .git files in watcher 2025-09-25 14:41:31 -05:00
GitHub Action fd322e28e8 ignore: update download stats 2025-09-25 2025-09-25 12:04:27 +00:00
Frank f4e79269fc zen: billing 2025-09-24 21:26:04 -04:00
GitHub Action 0db03c4b4e chore: format code 2025-09-24 23:10:17 +00:00
Frank 91ac1783d9 zen: billing 2025-09-24 19:09:28 -04:00
Adam d8089f9bbd fix(app): changes view 2025-09-24 15:49:40 -05:00
Adam f66689254e feat(app): changes view 2025-09-24 15:46:33 -05:00
Adam a3eea40128 fix: select dialog hover 2025-09-24 15:08:58 -05:00
Adam 595e3d48f7 feat: fuzzy file open 2025-09-24 12:40:54 -05:00
Filip 89cb0f9a01 feat(app): added command palette (#2630)
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
2025-09-24 11:05:15 -05:00
GitHub Action c394c696b2 ignore: update download stats 2025-09-24 2025-09-24 12:04:37 +00:00
opencode e28486d5cd release: v0.11.3 2025-09-24 03:43:46 +00:00
Dax Raad df7abea5fe update copilot auth 2025-09-23 23:36:27 -04:00
GitHub Action 6789cac430 chore: format code 2025-09-23 21:59:09 +00:00
Frank 4cd86236a1 zen: refund 2025-09-23 17:58:27 -04:00
GitHub Action e41a9b47fe chore: format code 2025-09-23 21:37:20 +00:00
Jay V 8e0e09e163 docs: add codex to zen 2025-09-23 17:36:40 -04:00
Jay V f0eb23c4d5 docs: edit 2025-09-23 17:34:52 -04:00
Jay V 7590ee788e docs: edits 2025-09-23 17:34:52 -04:00
Jay V 3bb2db5de3 docs: edit 2025-09-23 17:34:52 -04:00
Siddhant Choudhary e83e6c595c feat: output-format flag to stream json output (#2471)
Co-authored-by: Siddhant Choudhary <sid@treaps.com>
Co-authored-by: rekram1-node <aidenpcline@gmail.com>
2025-09-23 16:19:32 -05:00
opencode fde0faf6d8 release: v0.11.2 2025-09-23 21:03:59 +00:00
Dax Raad f8ba5e9572 fix codex errors 2025-09-23 16:56:40 -04:00
Dax Raad ef1f2943a5 fix: remove file existence check from LSP debug and format storage code 2025-09-23 16:49:57 -04:00
Alain Schlesser f9b415839b ignore: more reliable install script, handle non prettified json responses (#2745)
Co-authored-by: rekram1-node <aidenpcline@gmail.com>
2025-09-23 14:58:04 -05:00
Frank 815d6401c8 wip: remove 2025-09-23 14:21:39 -04:00
Frank 7231c6848e zen: update logged endpoints 2025-09-23 14:21:39 -04:00
Adam 3415dfda3b fix: open text files 2025-09-23 12:43:24 -05:00
David Hill f1f980c642 Merge branch 'dev' of https://github.com/sst/opencode into dev 2025-09-23 18:36:27 +01:00
David Hill 7c9616bf25 ignore: Copy update 2025-09-23 18:35:42 +01:00
David Hill 8be86f4b38 ignore: Footer update 2025-09-23 18:30:06 +01:00
Adam af202caf23 fix: exclude generated css file 2025-09-23 12:08:49 -05:00
GitHub Action 93cc1f11c9 chore: format code 2025-09-23 17:05:32 +00:00
Adam e20d12de23 fix: theme css format 2025-09-23 12:04:50 -05:00
GitHub Action ce87bc3592 chore: format code 2025-09-23 16:41:57 +00:00
Adam 0425aa413a wip: desktop file updates 2025-09-23 11:41:15 -05:00
Grégoire Paris c83dbb56dd docs: fix grammar mistake in lsp docs(#2744) 2025-09-23 08:12:46 -05:00
GitHub Action 52f88c0a88 ignore: update download stats 2025-09-23 2025-09-23 12:04:24 +00:00
Aiden Cline ed8369cd6b ignore: lsp debug file check (#2743) 2025-09-22 22:16:03 -05:00
Aiden Cline 0bff3e9c21 docs: github copilot model enable note (#2741) 2025-09-22 21:29:16 -05:00
Aiden Cline 5af4c7e066 fix: lsp extension undefined handle (#2739) 2025-09-22 21:14:55 -05:00
Joseph Hanson cf44950c18 docs: fix permissions link (#2738) 2025-09-22 20:28:54 -05:00
Adam Hosker 7232c702d6 docs: Add editor setup tip (#2638) 2025-09-22 16:01:27 -04:00
GitHub Action acd8819aca chore: format code 2025-09-22 19:59:29 +00:00
Jay V c55af547cf docs: edit tools doc 2025-09-22 15:58:48 -04:00
Aiden Cline e1265fb91f fix: enforce extensions requirement for custom lsp servers (#2734) 2025-09-22 11:45:47 -05:00
GitHub Action 98d111d551 ignore: update download stats 2025-09-22 2025-09-22 12:04:25 +00:00
Dax Raad b93a997ebd ci: bump 2025-09-22 01:47:28 -04:00
Dax Raad 7a32db138c ci: snapshot builds 2025-09-22 01:45:23 -04:00
iwauo dbfb523fd7 feat: add Java LSP server support (#2547)
Co-authored-by: rekram1-node <aidenpcline@gmail.com>
2025-09-21 23:55:15 -05:00
Aiden Cline 112cf7b59d fix: lsp spawn logic (#2723) 2025-09-21 11:25:47 -05:00
GitHub Action 375d7e1369 ignore: update download stats 2025-09-21 2025-09-21 12:04:06 +00:00
opencode 25d5454434 release: v0.11.1 2025-09-21 08:51:11 +00:00
Dax Raad a38b99b21c core: filter models without keys in opencode provider 2025-09-21 04:43:32 -04:00
opencode eb57274696 release: v0.11.0 2025-09-21 08:04:21 +00:00
GitHub Action 4f0f4f94b7 chore: format code 2025-09-21 04:23:40 +00:00
Frank 069baca7db wip: zen 2025-09-21 00:23:05 -04:00
Frank b1a72b006d wip: zen 2025-09-21 00:06:50 -04:00
Aiden Cline 1851bec769 ignore: lsp log (#2715) 2025-09-20 23:02:06 -05:00
opencode 31b4157b82 release: v0.10.4 2025-09-20 23:00:32 +00:00
Dax Raad da362e9460 turn back on compaction summaries 2025-09-20 18:52:30 -04:00
Osinachi Okpara 22e4625fb3 Enhance theme documentation with links (#2707)
Co-authored-by: rekram1-node <aidenpcline@gmail.com>
2025-09-20 11:25:12 -05:00
GitHub Action f68cbb7c6a ignore: update download stats 2025-09-20 2025-09-20 12:04:03 +00:00
Aiden Cline ca0b378090 fix: no payment method (#2706) 2025-09-20 04:15:44 -04:00
Jay V 59d536f010 docs: zen 2025-09-19 20:13:57 -04:00
2191 changed files with 94993 additions and 43138 deletions
+59
View File
@@ -0,0 +1,59 @@
name: Bug report
description: Report an issue that should be fixed
labels: ["bug"]
body:
- type: textarea
id: description
attributes:
label: Description
description: Describe the bug you encountered
placeholder: What happened?
validations:
required: true
- type: input
id: opencode-version
attributes:
label: OpenCode version
description: What version of OpenCode are you using?
validations:
required: false
- type: textarea
id: reproduce
attributes:
label: Steps to reproduce
description: How can we reproduce this issue?
placeholder: |
1.
2.
3.
validations:
required: false
- type: textarea
id: screenshot-or-link
attributes:
label: Screenshot and/or share link
description: Run `/share` to get a share link, or attach a screenshot
placeholder: Paste link or drag and drop screenshot here
validations:
required: false
- type: input
id: os
attributes:
label: Operating System
description: what OS are you using?
placeholder: e.g., macOS 26.0.1, Ubuntu 22.04, Windows 11
validations:
required: false
- type: input
id: terminal
attributes:
label: Terminal
description: what terminal are you using?
placeholder: e.g., iTerm2, Ghostty, Alacritty, Windows Terminal
validations:
required: false
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: 💬 Discord Community
url: https://discord.gg/opencode
about: For quick questions or real-time discussion. Note that issues are searchable and help others with the same question.
@@ -0,0 +1,20 @@
name: 🚀 Feature Request
description: Suggest an idea, feature, or enhancement
labels: [discussion]
title: "[FEATURE]:"
body:
- type: checkboxes
id: verified
attributes:
label: Feature hasn't been suggested before.
options:
- label: I have verified this feature I'm about to request hasn't been suggested before.
required: true
- type: textarea
attributes:
label: Describe the enhancement you want to request
description: What do you want to change or add? What are the benefits of implementing this? Try to be detailed so we can understand your request better :)
validations:
required: true
+11
View File
@@ -0,0 +1,11 @@
name: Question
description: Ask a question
labels: ["question"]
body:
- type: textarea
id: question
attributes:
label: Question
description: What's your question?
validations:
required: true
+22
View File
@@ -0,0 +1,22 @@
name: "Setup Bun"
description: "Setup Bun with caching and install dependencies"
runs:
using: "composite"
steps:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- name: Cache ~/.bun
id: cache-bun
uses: actions/cache@v4
with:
path: ~/.bun
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lockb', 'bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-
- name: Install dependencies
run: bun install
shell: bash
+71
View File
@@ -0,0 +1,71 @@
#
# This file is intentionally in the wrong dir, will move and add later....
#
# name: publish-python-sdk
# on:
# release:
# types: [published]
# workflow_dispatch:
# jobs:
# publish:
# runs-on: ubuntu-latest
# permissions:
# contents: read
# steps:
# - name: Checkout repository
# uses: actions/checkout@v4
# - name: Setup Bun
# uses: oven-sh/setup-bun@v1
# with:
# bun-version: 1.2.21
# - name: Install dependencies (JS/Bun)
# run: bun install
# - name: Install uv
# shell: bash
# run: curl -LsSf https://astral.sh/uv/install.sh | sh
# - name: Generate Python SDK from OpenAPI (CLI)
# shell: bash
# run: |
# ~/.local/bin/uv run --project packages/sdk/python python packages/sdk/python/scripts/generate.py --source cli
# - name: Sync Python dependencies
# shell: bash
# run: |
# ~/.local/bin/uv sync --dev --project packages/sdk/python
# - name: Set version from release tag
# shell: bash
# run: |
# TAG="${GITHUB_REF_NAME:-}"
# if [ -z "$TAG" ]; then
# TAG="$(git describe --tags --abbrev=0 || echo 0.0.0)"
# fi
# echo "Using version: $TAG"
# VERSION="$TAG" ~/.local/bin/uv run --project packages/sdk/python python - <<'PY'
# import os, re, pathlib
# root = pathlib.Path('packages/sdk/python')
# pt = (root / 'pyproject.toml').read_text()
# version = os.environ.get('VERSION','0.0.0').lstrip('v')
# pt = re.sub(r'(?m)^(version\s*=\s*")[^"]+("\s*)$', f"\\1{version}\\2", pt)
# (root / 'pyproject.toml').write_text(pt)
# # Also update generator config override for consistency
# cfgp = root / 'openapi-python-client.yaml'
# if cfgp.exists():
# cfg = cfgp.read_text()
# cfg = re.sub(r'(?m)^(package_version_override:\s*)\S+$', f"\\1{version}", cfg)
# cfgp.write_text(cfg)
# PY
# - name: Build and publish to PyPI
# env:
# PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
# shell: bash
# run: |
# ~/.local/bin/uv run --project packages/sdk/python python packages/sdk/python/scripts/publish.py
+55
View File
@@ -0,0 +1,55 @@
name: Auto-label TUI Issues
on:
issues:
types: [opened]
jobs:
auto-label:
runs-on: ubuntu-latest
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 web/desktop keywords
const webPattern = /\b(web|desktop)\b/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);
if (isWebRelated) {
// Add web label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: ['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
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: ['opentui']
});
}
+1 -5
View File
@@ -15,11 +15,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v1
with:
bun-version: 1.2.21
- run: bun install
- uses: ./.github/actions/setup-bun
- run: bun sst deploy --stage=${{ github.ref_name }}
env:
+1 -4
View File
@@ -20,13 +20,10 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: 1.2.21
uses: ./.github/actions/setup-bun
- name: run
run: |
bun install
./script/format.ts
env:
CI: true
+4 -2
View File
@@ -8,7 +8,9 @@ jobs:
opencode:
if: |
contains(github.event.comment.body, ' /oc') ||
contains(github.event.comment.body, ' /opencode')
startsWith(github.event.comment.body, '/oc') ||
contains(github.event.comment.body, ' /opencode') ||
startsWith(github.event.comment.body, '/opencode')
runs-on: ubuntu-latest
permissions:
id-token: write
@@ -24,4 +26,4 @@ jobs:
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
with:
model: opencode/sonic
model: opencode/glm-4.6
+5 -4
View File
@@ -19,16 +19,17 @@ jobs:
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.2.21
- uses: ./.github/actions/setup-bun
- run: git fetch --force --tags
- run: bun install -g @vscode/vsce
- name: Install extension dependencies
run: bun install
working-directory: ./sdks/vscode
- name: Publish
run: |
bun install
./script/publish
working-directory: ./sdks/vscode
env:
+11 -15
View File
@@ -35,18 +35,7 @@ jobs:
cache: true
cache-dependency-path: go.sum
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.2.21
- name: Cache ~/.bun
id: cache-bun
uses: actions/cache@v3
with:
path: ~/.bun
key: ${{ runner.os }}-bun-1-2-21-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-1-2-21-
- uses: ./.github/actions/setup-bun
- name: Install makepkg
run: |
@@ -60,14 +49,21 @@ jobs:
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 dependencies
run: bun install
- 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
- name: Publish
run: |
./script/publish.ts
env:
OPENCODE_BUMP: ${{ inputs.bump }}
OPENCODE_CHANNEL: latest
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
AUR_KEY: ${{ secrets.AUR_KEY }}
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
+35
View File
@@ -0,0 +1,35 @@
name: snapshot
on:
push:
branches:
- dev
- opentui
- v0
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
publish:
runs-on: ubuntu-latest
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 }}
+1 -3
View File
@@ -16,9 +16,7 @@ jobs:
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
uses: ./.github/actions/setup-bun
- name: Run stats script
run: bun script/stats.ts
+30
View File
@@ -0,0 +1,30 @@
name: test
on:
push:
branches-ignore:
- production
pull_request:
branches-ignore:
- production
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Bun
uses: ./.github/actions/setup-bun
- name: run
run: |
git config --global user.email "bot@opencode.ai"
git config --global user.name "opencode"
bun turbo typecheck
bun turbo test
env:
CI: true
+1 -6
View File
@@ -13,12 +13,7 @@ jobs:
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: 1.2.21
- name: Install dependencies
run: bun install
uses: ./.github/actions/setup-bun
- name: Run typecheck
run: bun typecheck
+3
View File
@@ -5,8 +5,11 @@ node_modules
.env
.idea
.vscode
*~
openapi.json
playground
tmp
dist
.turbo
**/.serena
.serena/
+2
View File
@@ -0,0 +1,2 @@
#!/bin/sh
bun typecheck
+1 -1
View File
@@ -13,7 +13,7 @@ avoid repeating the title of the page, should be 5-10 words long
Chunks of text should not be more than 2 sentences long
Each section is spearated by a divider of 3 dashes
Each section is separated by a divider of 3 dashes
The section titles are short with only the first letter of the word capitalized
+14
View File
@@ -1,3 +1,7 @@
---
description: Git commit and push
---
commit and push
make sure it includes a prefix like
@@ -7,3 +11,13 @@ core:
ci:
ignore:
wip:
For anything in the packages/web use the docs: prefix.
For anything in the packages/app use the ignore: prefix.
prefer to explain WHY something was done from an end user perspective instead of
WHAT was done.
do not do generic messages like "improved agent experience" be very specific
about what user facing changes were made
+1 -1
View File
@@ -1,5 +1,5 @@
---
description: hello world
description: hello world iaosd ioasjdoiasjd oisadjoisajd osiajd oisaj dosaij dsoajsajdaijdoisa jdoias jdoias jdoia jois jo jdois jdoias jdoias j djoasdj
---
hey there $ARGUMENTS
+5
View File
@@ -0,0 +1,5 @@
---
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.
-11
View File
@@ -1,11 +0,0 @@
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "call this tool when you want to give up",
args: {
message: tool.schema.string().describe("give up message"),
},
async execute(args) {
return "Hey fuck you!"
},
})
+31
View File
@@ -14,3 +14,34 @@
## 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"
}
}
]
}
}
+68
View File
@@ -0,0 +1,68 @@
# Contributing to OpenCode
We want to make it easy for you to contribute to OpenCode. Here are the most common type of changes that get merged:
- Bug fixes
- Additional LSPs / Formatters
- Improvements to LLM performance
- Support for new providers
- Fixes for environment-specific quirks
- Missing standard behavior
- Documentation improvements
However, any UI or core product feature must go through a design review with the core team before implementation.
If you are unsure if a PR would be accepted, feel free to ask a maintainer or look for issues with any of the following labels:
- [`help wanted`](https://github.com/sst/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Ahelp-wanted)
- [`good first issue`](https://github.com/sst/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22)
- [`bug`](https://github.com/sst/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug)
- [`perf`](https://github.com/sst/opencode/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22perf%22)
> [!NOTE]
> PRs that ignore these guardrails will likely be closed.
Want to take on an issue? Leave a comment and a maintainer may assign it to you unless it is something we are already working on.
## Developing OpenCode
- Requirements: Bun 1.3+
- Install dependencies and start the dev server from the repo root:
```bash
bun install
bun dev
```
- Core pieces:
- `packages/opencode`: OpenCode core business logic & server.
- `packages/opencode/src/cli/cmd/tui/`: The TUI code, written in SolidJS with [opentui](https://github.com/sst/opentui)
- `packages/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.
## Pull Request Expectations
- Try to keep pull requests small and focused.
- Link relevant issue(s) in the description
- Explain the issue and why your change fixes it
- Avoid having verbose LLM generated PR descriptions
- Before adding new functions or functionality, ensure that such behavior doesn't already exist elsewhere in the codebase.
### Style Preferences
These are not strictly enforced, they are just general guidelines:
- **Functions:** Keep logic within a single function unless breaking it out adds clear reuse or composition benefits.
- **Destructuring:** Do not do unnecessary destructuring of variables.
- **Control flow:** Avoid `else` statements.
- **Error handling:** Prefer `.catch(...)` instead of `try`/`catch` when possible.
- **Types:** Reach for precise types and avoid `any`.
- **Variables:** Stick to immutable patterns and avoid `let`.
- **Naming:** Choose concise single-word identifiers when they remain descriptive.
- **Runtime APIs:** Use Bun helpers such as `Bun.file()` when they fit the use case.
## Feature Requests
For net-new functionality, start with a design conversation. Open an issue describing the problem, your proposed approach (optional), and why it belongs in OpenCode. The core team will help decide whether it should move forward; please wait for that approval instead of opening a feature PR directly.
+14 -45
View File
@@ -1,20 +1,20 @@
<p align="center">
<a href="https://opencode.ai">
<picture>
<source srcset="packages/web/src/assets/logo-ornate-dark.svg" media="(prefers-color-scheme: dark)">
<source srcset="packages/web/src/assets/logo-ornate-light.svg" media="(prefers-color-scheme: light)">
<img src="packages/web/src/assets/logo-ornate-light.svg" alt="opencode logo">
<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, built for the terminal.</p>
<p align="center">The AI coding agent built for the terminal.</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)
[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai)
---
@@ -26,7 +26,9 @@ curl -fsSL https://opencode.ai/install | bash
# Package managers
npm i -g opencode-ai@latest # or bun/pnpm/yarn
brew install sst/tap/opencode # macOS and Linux
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
```
@@ -50,45 +52,11 @@ XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash
### Documentation
For more info on how to configure opencode [**head over to our docs**](https://opencode.ai/docs).
For more info on how to configure OpenCode [**head over to our docs**](https://opencode.ai/docs).
### Contributing
opencode is an opinionated tool so any fundamental feature needs to go through a
design process with the core team.
> [!IMPORTANT]
> We do not accept PRs for core features.
However we still merge a ton of PRs - you can contribute:
- Bug fixes
- Improvements to LLM performance
- Support for new providers
- Fixes for env specific quirks
- Missing standard behavior
- Documentation
Take a look at the git history to see what kind of PRs we end up merging.
> [!NOTE]
> If you do not follow the above guidelines we might close your PR.
To run opencode locally you need.
- Bun
- Golang 1.24.x
And run.
```bash
$ bun install
$ bun dev
```
#### Development Notes
**API Client**: After making changes to the TypeScript API endpoints in `packages/opencode/src/server/server.ts`, you will need the opencode team to generate a new stainless sdk for the clients.
If you're interested in contributing to OpenCode, please read our [contributing docs](./CONTRIBUTING.md) before submitting a pull request.
### FAQ
@@ -97,9 +65,10 @@ $ bun dev
It's very similar to Claude Code in terms of capability. Here are the key differences:
- 100% open source
- Not coupled to any provider. Although Anthropic is recommended, opencode can be used with OpenAI, Google or even local models. As models evolve the gaps between them will close and pricing will drop so being provider-agnostic is important.
- A focus on TUI. opencode is built by neovim users and the creators of [terminal.shop](https://terminal.shop); we are going to push the limits of what's possible in the terminal.
- A client/server architecture. This for example can allow opencode to run on your computer, while you can drive it remotely from a mobile app. Meaning that the TUI frontend is just one of the possible clients.
- Not coupled to any provider. Although Anthropic is recommended, OpenCode can be used with OpenAI, Google or even local models. As models evolve the gaps between them will close and pricing will drop so being provider-agnostic is important.
- Out of the box LSP support
- A focus on TUI. OpenCode is built by neovim users and the creators of [terminal.shop](https://terminal.shop); we are going to push the limits of what's possible in the terminal.
- A client/server architecture. This for example can allow OpenCode to run on your computer, while you can drive it remotely from a mobile app. Meaning that the TUI frontend is just one of the possible clients.
#### What's the other repo?
+130 -84
View File
@@ -1,86 +1,132 @@
# 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) |
| 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) |
+1767 -794
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -104,7 +104,7 @@ To test locally:
- `MODEL`: The model used by opencode. Same as the `MODEL` defined in the GitHub workflow.
- `ANTHROPIC_API_KEY`: Your model provider API key. Same as the keys defined in the GitHub workflow.
- `GITHUB_RUN_ID`: Dummy value to emulate GitHub action environment.
- `MOCK_TOKEN`: A GitHub persontal access token. This token is used to verify you have `admin` or `write` access to the test repo. Generate a token [here](https://github.com/settings/personal-access-tokens).
- `MOCK_TOKEN`: A GitHub personal access token. This token is used to verify you have `admin` or `write` access to the test repo. Generate a token [here](https://github.com/settings/personal-access-tokens).
- `MOCK_EVENT`: Mock GitHub event payload (see templates below).
- `/path/to/opencode`: Path to your cloned opencode repo. `bun /path/to/opencode/github/index.ts` runs your local version of `opencode`.
@@ -118,7 +118,7 @@ Replace:
- `"owner":"sst"` with repo owner
- `"repo":"hello-world"` with repo name
- `"actor":"fwang"` with the GitHub username of commentor
- `"actor":"fwang"` with the GitHub username of commenter
- `"number":4` with the GitHub issue id
- `"body":"hey opencode, summarize thread"` with comment body
+4 -18
View File
@@ -6,15 +6,11 @@ branding:
inputs:
model:
description: "The model to use with opencode. Takes the format of `provider/model`."
description: "Model to use"
required: true
share:
description: "Whether to share the opencode session. Defaults to true for public repositories."
required: false
token:
description: "Optional GitHub access token for performing operations such as creating comments, committing changes, and opening pull requests. Defaults to the installation access token from the opencode GitHub App."
description: "Share the opencode session (defaults to true for public repos)"
required: false
runs:
@@ -24,20 +20,10 @@ runs:
shell: bash
run: curl -fsSL https://opencode.ai/install | bash
- name: Install bun
shell: bash
run: npm install -g bun
- name: Install dependencies
shell: bash
run: |
cd ${GITHUB_ACTION_PATH}
bun install
- name: Run opencode
shell: bash
run: bun ${GITHUB_ACTION_PATH}/index.ts
id: run_opencode
run: opencode github run
env:
MODEL: ${{ inputs.model }}
SHARE: ${{ inputs.share }}
TOKEN: ${{ inputs.token }}
+48 -15
View File
@@ -152,6 +152,9 @@ try {
return session.id.slice(-8)
})()
console.log("opencode session", session.id)
if (shareId) {
console.log("Share link:", `${useShareUrl()}/s/${shareId}`)
}
// Handle 3 cases
// 1. Issue
@@ -168,7 +171,9 @@ try {
const summary = await summarize(response)
await pushToLocalBranch(summary)
}
const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${useShareUrl()}/s/${shareId}`))
const hasShared = prData.comments.nodes.some((c) =>
c.body.includes(`${useShareUrl()}/s/${shareId}`),
)
await updateComment(`${response}${footer({ image: !hasShared })}`)
}
// Fork PR
@@ -180,7 +185,9 @@ try {
const summary = await summarize(response)
await pushToForkBranch(summary, prData)
}
const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${useShareUrl()}/s/${shareId}`))
const hasShared = prData.comments.nodes.some((c) =>
c.body.includes(`${useShareUrl()}/s/${shareId}`),
)
await updateComment(`${response}${footer({ image: !hasShared })}`)
}
}
@@ -361,7 +368,9 @@ async function getAccessToken() {
if (!response.ok) {
const responseJson = (await response.json()) as { error?: string }
throw new Error(`App token exchange failed: ${response.status} ${response.statusText} - ${responseJson.error}`)
throw new Error(
`App token exchange failed: ${response.status} ${response.statusText} - ${responseJson.error}`,
)
}
const responseJson = (await response.json()) as { token: string }
@@ -402,8 +411,12 @@ async function getUserPrompt() {
// ie. <img alt="Image" src="https://github.com/user-attachments/assets/xxxx" />
// ie. [api.json](https://github.com/user-attachments/files/21433810/api.json)
// ie. ![Image](https://github.com/user-attachments/assets/xxxx)
const mdMatches = prompt.matchAll(/!?\[.*?\]\((https:\/\/github\.com\/user-attachments\/[^)]+)\)/gi)
const tagMatches = prompt.matchAll(/<img .*?src="(https:\/\/github\.com\/user-attachments\/[^"]+)" \/>/gi)
const mdMatches = prompt.matchAll(
/!?\[.*?\]\((https:\/\/github\.com\/user-attachments\/[^)]+)\)/gi,
)
const tagMatches = prompt.matchAll(
/<img .*?src="(https:\/\/github\.com\/user-attachments\/[^"]+)" \/>/gi,
)
const matches = [...mdMatches, ...tagMatches].sort((a, b) => a.index - b.index)
console.log("Images", JSON.stringify(matches, null, 2))
@@ -430,7 +443,8 @@ async function getUserPrompt() {
// Replace img tag with file path, ie. @image.png
const replacement = `@${filename}`
prompt = prompt.slice(0, start + offset) + replacement + prompt.slice(start + offset + tag.length)
prompt =
prompt.slice(0, start + offset) + replacement + prompt.slice(start + offset + tag.length)
offset += replacement.length - tag.length
const contentType = res.headers.get("content-type")
@@ -498,7 +512,12 @@ async function subscribeSessionEvents() {
? JSON.stringify(part.state.input)
: "Unknown"
console.log()
console.log(color + `|`, "\x1b[0m\x1b[2m" + ` ${tool.padEnd(7, " ")}`, "", "\x1b[0m" + title)
console.log(
color + `|`,
"\x1b[0m\x1b[2m" + ` ${tool.padEnd(7, " ")}`,
"",
"\x1b[0m" + title,
)
}
if (part.type === "text") {
@@ -710,7 +729,8 @@ async function assertPermissions() {
throw new Error(`Failed to check permissions for user ${actor}: ${error}`)
}
if (!["admin", "write"].includes(permission)) throw new Error(`User ${actor} does not have write permissions`)
if (!["admin", "write"].includes(permission))
throw new Error(`User ${actor} does not have write permissions`)
}
async function updateComment(body: string) {
@@ -730,12 +750,13 @@ async function updateComment(body: string) {
async function createPR(base: string, branch: string, title: string, body: string) {
console.log("Creating pull request...")
const { repo } = useContext()
const truncatedTitle = title.length > 256 ? title.slice(0, 253) + "..." : title
const pr = await octoRest.rest.pulls.create({
owner: repo.owner,
repo: repo.repo,
head: branch,
base,
title,
title: truncatedTitle,
body,
})
return pr.data.number
@@ -753,7 +774,9 @@ function footer(opts?: { image?: boolean }) {
return `<a href="${useShareUrl()}/s/${shareId}"><img width="200" alt="${titleAlt}" src="https://social-cards.sst.dev/opencode-share/${title64}.png?model=${providerID}/${modelID}&version=${session.version}&id=${shareId}" /></a>\n`
})()
const shareUrl = shareId ? `[opencode session](${useShareUrl()}/s/${shareId})&nbsp;&nbsp;|&nbsp;&nbsp;` : ""
const shareUrl = shareId
? `[opencode session](${useShareUrl()}/s/${shareId})&nbsp;&nbsp;|&nbsp;&nbsp;`
: ""
return `\n\n${image}${shareUrl}[github run](${useEnvRunUrl()})`
}
@@ -936,9 +959,13 @@ function buildPromptDataForPR(pr: GitHubPullRequest) {
})
.map((c) => `- ${c.author.login} at ${c.createdAt}: ${c.body}`)
const files = (pr.files.nodes || []).map((f) => `- ${f.path} (${f.changeType}) +${f.additions}/-${f.deletions}`)
const files = (pr.files.nodes || []).map(
(f) => `- ${f.path} (${f.changeType}) +${f.additions}/-${f.deletions}`,
)
const reviewData = (pr.reviews.nodes || []).map((r) => {
const comments = (r.comments.nodes || []).map((c) => ` - ${c.path}:${c.line ?? "?"}: ${c.body}`)
const comments = (r.comments.nodes || []).map(
(c) => ` - ${c.path}:${c.line ?? "?"}: ${c.body}`,
)
return [
`- ${r.author.login} at ${r.submittedAt}:`,
` - Review body: ${r.body}`,
@@ -960,9 +987,15 @@ function buildPromptDataForPR(pr: GitHubPullRequest) {
`Deletions: ${pr.deletions}`,
`Total Commits: ${pr.commits.totalCount}`,
`Changed Files: ${pr.files.nodes.length} files`,
...(comments.length > 0 ? ["<pull_request_comments>", ...comments, "</pull_request_comments>"] : []),
...(files.length > 0 ? ["<pull_request_changed_files>", ...files, "</pull_request_changed_files>"] : []),
...(reviewData.length > 0 ? ["<pull_request_reviews>", ...reviewData, "</pull_request_reviews>"] : []),
...(comments.length > 0
? ["<pull_request_comments>", ...comments, "</pull_request_comments>"]
: []),
...(files.length > 0
? ["<pull_request_changed_files>", ...files, "</pull_request_changed_files>"]
: []),
...(reviewData.length > 0
? ["<pull_request_reviews>", ...reviewData, "</pull_request_reviews>"]
: []),
"</pull_request>",
].join("\n")
}
+1 -1
View File
@@ -14,6 +14,6 @@
"@actions/github": "6.0.1",
"@octokit/graphql": "9.0.1",
"@octokit/rest": "22.0.0",
"@opencode-ai/sdk": "0.5.4"
"@opencode-ai/sdk": "workspace:*"
}
}
+1 -1
View File
@@ -6,4 +6,4 @@
/// <reference path="../sst-env.d.ts" />
import "sst"
export {}
export {}
+3 -1
View File
@@ -2,6 +2,8 @@ import { domain } from "./stage"
const GITHUB_APP_ID = new sst.Secret("GITHUB_APP_ID")
const GITHUB_APP_PRIVATE_KEY = new sst.Secret("GITHUB_APP_PRIVATE_KEY")
export const EMAILOCTOPUS_API_KEY = new sst.Secret("EMAILOCTOPUS_API_KEY")
const ADMIN_SECRET = new sst.Secret("ADMIN_SECRET")
const bucket = new sst.cloudflare.Bucket("Bucket")
export const api = new sst.cloudflare.Worker("Api", {
@@ -11,7 +13,7 @@ export const api = new sst.cloudflare.Worker("Api", {
WEB_DOMAIN: domain,
},
url: true,
link: [bucket, GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY],
link: [bucket, GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY, ADMIN_SECRET],
transform: {
worker: (args) => {
args.logpush = true
+26 -8
View File
@@ -1,5 +1,5 @@
import { WebhookEndpoint } from "pulumi-stripe"
import { domain } from "./stage"
import { EMAILOCTOPUS_API_KEY } from "./app"
////////////////
// DATABASE
@@ -61,20 +61,27 @@ export const auth = new sst.cloudflare.Worker("AuthApi", {
domain: `auth.${domain}`,
handler: "packages/console/function/src/auth.ts",
url: true,
link: [database, authStorage, GITHUB_CLIENT_ID_CONSOLE, GITHUB_CLIENT_SECRET_CONSOLE, GOOGLE_CLIENT_ID],
link: [
database,
authStorage,
GITHUB_CLIENT_ID_CONSOLE,
GITHUB_CLIENT_SECRET_CONSOLE,
GOOGLE_CLIENT_ID,
],
})
////////////////
// GATEWAY
////////////////
export const stripeWebhook = new WebhookEndpoint("StripeWebhookEndpoint", {
export const stripeWebhook = new stripe.WebhookEndpoint("StripeWebhookEndpoint", {
url: $interpolate`https://${domain}/stripe/webhook`,
enabledEvents: [
"checkout.session.async_payment_failed",
"checkout.session.async_payment_succeeded",
"checkout.session.completed",
"checkout.session.expired",
"charge.refunded",
"customer.created",
"customer.deleted",
"customer.updated",
@@ -93,13 +100,11 @@ export const stripeWebhook = new WebhookEndpoint("StripeWebhookEndpoint", {
"customer.subscription.resumed",
"customer.subscription.trial_will_end",
"customer.subscription.updated",
"customer.tax_id.created",
"customer.tax_id.deleted",
"customer.tax_id.updated",
],
})
const ZEN_MODELS = new sst.Secret("ZEN_MODELS")
const ZEN_MODELS1 = new sst.Secret("ZEN_MODELS1")
const ZEN_MODELS2 = new sst.Secret("ZEN_MODELS2")
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
properties: { value: auth.url.apply((url) => url!) },
@@ -112,6 +117,9 @@ const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", {
// CONSOLE
////////////////
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")
let logProcessor
if ($app.stage === "production" || $app.stage === "frank") {
const HONEYCOMB_API_KEY = new sst.Secret("HONEYCOMB_API_KEY")
@@ -124,7 +132,17 @@ if ($app.stage === "production" || $app.stage === "frank") {
new sst.cloudflare.x.SolidStart("Console", {
domain,
path: "packages/console/app",
link: [database, AUTH_API_URL, STRIPE_WEBHOOK_SECRET, STRIPE_SECRET_KEY, ZEN_MODELS],
link: [
database,
AUTH_API_URL,
STRIPE_WEBHOOK_SECRET,
STRIPE_SECRET_KEY,
ZEN_MODELS1,
ZEN_MODELS2,
EMAILOCTOPUS_API_KEY,
AWS_SES_ACCESS_KEY_ID,
AWS_SES_SECRET_ACCESS_KEY,
],
environment: {
//VITE_DOCS_URL: web.url.apply((url) => url!),
//VITE_API_URL: gateway.url.apply((url) => url!),
+1 -1
View File
@@ -2,7 +2,7 @@ import { domain } from "./stage"
new sst.cloudflare.StaticSite("Desktop", {
domain: "desktop." + domain,
path: "packages/app",
path: "packages/desktop",
build: {
command: "bun turbo build",
output: "./dist",
+8
View File
@@ -3,3 +3,11 @@ export const domain = (() => {
if ($app.stage === "dev") return "dev.opencode.ai"
return `${$app.stage}.dev.opencode.ai`
})()
export const zoneID = "430ba34c138cfb5360826c4909f99be8"
new cloudflare.RegionalHostname("RegionalHostname", {
hostname: domain,
regionKey: "us",
zoneId: zoneID,
})
+10 -5
View File
@@ -10,10 +10,14 @@ NC='\033[0m' # No Color
requested_version=${VERSION:-}
os=$(uname -s | tr '[:upper:]' '[:lower:]')
if [[ "$os" == "darwin" ]]; then
os="darwin"
fi
raw_os=$(uname -s)
os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]')
# Normalize various Unix-like identifiers
case "$raw_os" in
Darwin*) os="darwin" ;;
Linux*) os="linux" ;;
MINGW*|MSYS*|CYGWIN*) os="windows" ;;
esac
arch=$(uname -m)
if [[ "$arch" == "aarch64" ]]; then
@@ -46,7 +50,7 @@ mkdir -p "$INSTALL_DIR"
if [ -z "$requested_version" ]; then
url="https://github.com/sst/opencode/releases/latest/download/$filename"
specific_version=$(curl -s https://api.github.com/repos/sst/opencode/releases/latest | awk -F'"' '/"tag_name": "/ {gsub(/^v/, "", $4); print $4}')
specific_version=$(curl -s https://api.github.com/repos/sst/opencode/releases/latest | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
if [[ $? -ne 0 || -z "$specific_version" ]]; then
echo -e "${RED}Failed to fetch version information${NC}"
@@ -96,6 +100,7 @@ download_and_install() {
curl -# -L -o "$filename" "$url"
unzip -q "$filename"
mv opencode "$INSTALL_DIR"
chmod 755 "${INSTALL_DIR}/opencode"
cd .. && rm -rf opencodetmp
}
@@ -0,0 +1,15 @@
{
"keep": {
"days": true,
"amount": 14
},
"auditLog": "/home/thdxr/dev/projects/sst/opencode/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json",
"files": [
{
"date": 1759827172859,
"name": "/home/thdxr/dev/projects/sst/opencode/logs/mcp-puppeteer-2025-10-07.log",
"hash": "a3d98b26edd793411b968a0d24cfeee8332138e282023c3b83ec169d55c67f16"
}
],
"hashType": "sha256"
}
+48
View File
@@ -0,0 +1,48 @@
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:52.879"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:52.880"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:56.191"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:56.192"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:59.267"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:59.268"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:20.276"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:20.277"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:30.838"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:30.839"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:42.452"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:42.452"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:46.499"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:46.500"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:54:02.295"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:54:02.295"}
{"arguments":{"url":"https://google.com"},"level":"debug","message":"Tool call received","service":"mcp-puppeteer","timestamp":"2025-10-07 04:54:37.150","tool":"puppeteer_navigate"}
{"0":"n","1":"p","2":"x","level":"info","message":"Launching browser with config:","service":"mcp-puppeteer","timestamp":"2025-10-07 04:54:37.150"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:55:08.488"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:55:08.489"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:11.815"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:11.816"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:21.934"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:21.935"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:32.544"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:32.544"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:41.154"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:41.155"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:55.426"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:55.427"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:15.715"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:15.716"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:25.063"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:25.064"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:48.567"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:48.568"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:25:08.937"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:25:08.938"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:38:37.120"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:38:37.121"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:38:52.490"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:38:52.491"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:39:25.524"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:39:25.525"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:40:57.126"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:40:57.127"}
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:42:24.175"}
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:42:24.176"}
+15 -1
View File
@@ -1,3 +1,17 @@
{
"$schema": "https://opencode.ai/config.json"
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-openai-codex-auth"],
"mcp": {
"weather": {
"type": "local",
"command": ["bun", "x", "@h1deya/mcp-server-weather"]
},
"context7": {
"type": "remote",
"url": "https://mcp.context7.com/mcp",
"headers": {
"CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}"
}
}
}
}
+36 -12
View File
@@ -1,44 +1,64 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "opencode",
"description": "AI-powered development tool",
"private": true,
"type": "module",
"packageManager": "bun@1.2.21",
"packageManager": "bun@1.3.1",
"scripts": {
"dev": "bun run --conditions=development packages/opencode/src/index.ts",
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
"typecheck": "bun turbo typecheck",
"generate": "(cd packages/sdk && ./js/script/generate.ts) && (cd packages/sdk/stainless && ./generate.ts)",
"postinstall": "./script/hooks"
"prepare": "husky",
"random": "echo 'Random script'"
},
"workspaces": {
"packages": [
"packages/*",
"packages/console/*",
"packages/sdk/js"
"packages/sdk/js",
"packages/slack"
],
"catalog": {
"@types/bun": "1.2.21",
"@types/bun": "1.3.0",
"@hono/zod-validator": "0.4.2",
"ulid": "3.0.1",
"@kobalte/core": "0.13.11",
"@types/node": "22.13.9",
"@tsconfig/node22": "22.0.2",
"@tsconfig/bun": "1.0.9",
"@cloudflare/workers-types": "4.20251008.0",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@pierre/precision-diffs": "0.4.1",
"@solidjs/meta": "0.29.4",
"@tailwindcss/vite": "4.1.11",
"diff": "8.0.2",
"ai": "5.0.8",
"hono": "4.7.10",
"fuzzysort": "3.1.0",
"luxon": "3.6.1",
"typescript": "5.8.2",
"@typescript/native-preview": "7.0.0-dev.20251014.1",
"zod": "4.1.8",
"remeda": "2.26.0",
"solid-js": "1.9.9"
"solid-js": "1.9.9",
"solid-list": "0.3.0",
"tailwindcss": "4.1.11",
"virtua": "0.42.3",
"vite": "7.1.4",
"vite-plugin-solid": "2.11.8"
}
},
"dependencies": {
"pulumi-stripe": "0.0.24"
},
"devDependencies": {
"@tsconfig/bun": "catalog:",
"husky": "9.1.7",
"prettier": "3.6.2",
"sst": "3.17.13",
"sst": "3.17.19",
"turbo": "2.5.6"
},
"dependencies": {
"@opencode-ai/script": "workspace:*",
"@opencode-ai/sdk": "workspace:*"
},
"repository": {
"type": "git",
"url": "https://github.com/sst/opencode"
@@ -46,7 +66,7 @@
"license": "MIT",
"prettier": {
"semi": false,
"printWidth": 120
"printWidth": 100
},
"trustedDependencies": [
"esbuild",
@@ -58,5 +78,9 @@
],
"patchedDependencies": {
"@solidjs/start@1.1.7": "patches/@solidjs%2Fstart@1.1.7.patch"
},
"overrides": {
"@types/bun": "catalog:",
"@types/node": "catalog:"
}
}
-24
View File
@@ -1,24 +0,0 @@
<!doctype html>
<html lang="en" class="h-full bg-background">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.svg" />
<link rel="stylesheet" href="/src/assets/theme.css" />
<title>opencode</title>
</head>
<body class="h-full overscroll-none select-none">
<script>
;(function () {
const savedTheme = localStorage.getItem("theme") || "opencode"
const savedDarkMode = localStorage.getItem("darkMode") !== "false"
document.documentElement.setAttribute("data-theme", savedTheme)
document.documentElement.setAttribute("data-dark", savedDarkMode.toString())
})()
</script>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="/src/index.tsx" type="module"></script>
</body>
</html>
-163
View File
@@ -1,163 +0,0 @@
import type { Plugin } from "vite"
import { readdir, readFile, writeFile } from "fs/promises"
import { join, resolve } from "path"
interface ThemeDefinition {
$schema?: string
defs?: Record<string, string>
theme: Record<string, any>
}
interface ResolvedThemeColor {
dark: string
light: string
}
class ColorResolver {
private colors: Map<string, any> = new Map()
private visited: Set<string> = new Set()
constructor(defs: Record<string, string> = {}, theme: Record<string, any> = {}) {
Object.entries(defs).forEach(([key, value]) => {
this.colors.set(key, value)
})
Object.entries(theme).forEach(([key, value]) => {
this.colors.set(key, value)
})
}
resolveColor(key: string, value: any): ResolvedThemeColor {
if (this.visited.has(key)) {
throw new Error(`Circular reference detected for color ${key}`)
}
this.visited.add(key)
try {
if (typeof value === "string") {
if (value === "none") return { dark: value, light: value }
if (value.startsWith("#")) {
return { dark: value.toUpperCase(), light: value.toUpperCase() }
}
const resolved = this.resolveReference(value)
return { dark: resolved, light: resolved }
}
if (typeof value === "object" && value !== null) {
const dark = this.resolveColorValue(value.dark || value.light || "#000000")
const light = this.resolveColorValue(value.light || value.dark || "#FFFFFF")
return { dark, light }
}
return { dark: "#000000", light: "#FFFFFF" }
} finally {
this.visited.delete(key)
}
}
private resolveColorValue(value: any): string {
if (typeof value === "string") {
if (value === "none") return value
if (value.startsWith("#")) {
return value.toUpperCase()
}
return this.resolveReference(value)
}
return value
}
private resolveReference(ref: string): string {
const colorValue = this.colors.get(ref)
if (colorValue === undefined) {
throw new Error(`Color reference '${ref}' not found`)
}
if (typeof colorValue === "string") {
if (colorValue === "none") return colorValue
if (colorValue.startsWith("#")) {
return colorValue.toUpperCase()
}
return this.resolveReference(colorValue)
}
return colorValue
}
}
function kebabCase(str: string): string {
return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase()
}
function parseTheme(themeData: ThemeDefinition): Record<string, ResolvedThemeColor> {
const resolver = new ColorResolver(themeData.defs, themeData.theme)
const colors: Record<string, ResolvedThemeColor> = {}
Object.entries(themeData.theme).forEach(([key, value]) => {
colors[key] = resolver.resolveColor(key, value)
})
return colors
}
async function loadThemes(): Promise<Record<string, Record<string, ResolvedThemeColor>>> {
const themesDir = resolve(__dirname, "../../tui/internal/theme/themes")
const files = await readdir(themesDir)
const themes: Record<string, Record<string, ResolvedThemeColor>> = {}
for (const file of files) {
if (!file.endsWith(".json")) continue
const themeName = file.replace(".json", "")
const themeData: ThemeDefinition = JSON.parse(await readFile(join(themesDir, file), "utf-8"))
themes[themeName] = parseTheme(themeData)
}
return themes
}
function generateCSS(themes: Record<string, Record<string, ResolvedThemeColor>>): string {
let css = `/* Auto-generated theme CSS - Do not edit manually */\n:root {\n`
const defaultTheme = themes["opencode"] || Object.values(themes)[0]
if (defaultTheme) {
Object.entries(defaultTheme).forEach(([key, color]) => {
const cssVar = `--theme-${kebabCase(key)}`
css += ` ${cssVar}: ${color.light};\n`
})
}
css += `}\n\n`
Object.entries(themes).forEach(([themeName, colors]) => {
css += `[data-theme="${themeName}"][data-dark="false"] {\n`
Object.entries(colors).forEach(([key, color]) => {
const cssVar = `--theme-${kebabCase(key)}`
css += ` ${cssVar}: ${color.light};\n`
})
css += `}\n\n`
css += `[data-theme="${themeName}"][data-dark="true"] {\n`
Object.entries(colors).forEach(([key, color]) => {
const cssVar = `--theme-${kebabCase(key)}`
css += ` ${cssVar}: ${color.dark};\n`
})
css += `}\n\n`
})
return css
}
export function generateThemeCSS(): Plugin {
return {
name: "generate-theme-css",
async buildStart() {
try {
console.log("Generating theme CSS...")
const themes = await loadThemes()
const css = generateCSS(themes)
const outputPath = resolve(__dirname, "../src/assets/theme.css")
await writeFile(outputPath, css)
console.log(`✅ Generated theme CSS with ${Object.keys(themes).length} themes`)
console.log(` Output: ${outputPath}`)
} catch (error) {
throw new Error(`Theme CSS generation failed: ${error}`)
}
},
}
}
-5
View File
@@ -1,5 +0,0 @@
<svg width="600" height="600" viewBox="0 0 600 600" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="600" height="600" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M115 180H300V420H115V180ZM253.75 229.044H161.25V370.405H253.75V229.044Z" fill="white"/>
<path d="M346.25 180H485V229.044H392.5V370.405H485V419.449H346.25V180Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 377 B

File diff suppressed because it is too large Load Diff
-601
View File
@@ -1,601 +0,0 @@
import { transformerNotationDiff } from "@shikijs/transformers"
import { marked } from "marked"
import markedShiki from "marked-shiki"
import { codeToHtml } from "shiki"
import { createResource } from "solid-js"
const markedWithShiki = marked.use(
markedShiki({
highlight(code, lang) {
return codeToHtml(code, {
// structure: "inline",
lang: lang || "text",
tabindex: false,
theme: {
colors: {
"actionBar.toggledBackground": "var(--theme-background-element)",
"activityBarBadge.background": "var(--theme-accent)",
"checkbox.border": "var(--theme-border)",
"editor.background": "transparent",
"editor.foreground": "var(--theme-text)",
"editor.inactiveSelectionBackground": "var(--theme-background-element)",
"editor.selectionHighlightBackground": "var(--theme-border-active)",
"editorIndentGuide.activeBackground1": "var(--theme-border-subtle)",
"editorIndentGuide.background1": "var(--theme-border-subtle)",
"input.placeholderForeground": "var(--theme-text-muted)",
"list.activeSelectionIconForeground": "var(--theme-text)",
"list.dropBackground": "var(--theme-background-element)",
"menu.background": "var(--theme-background-panel)",
"menu.border": "var(--theme-border)",
"menu.foreground": "var(--theme-text)",
"menu.selectionBackground": "var(--theme-primary)",
"menu.separatorBackground": "var(--theme-border)",
"ports.iconRunningProcessForeground": "var(--theme-success)",
"sideBarSectionHeader.background": "transparent",
"sideBarSectionHeader.border": "var(--theme-border-subtle)",
"sideBarTitle.foreground": "var(--theme-text-muted)",
"statusBarItem.remoteBackground": "var(--theme-success)",
"statusBarItem.remoteForeground": "var(--theme-text)",
"tab.lastPinnedBorder": "var(--theme-border-subtle)",
"tab.selectedBackground": "var(--theme-background-element)",
"tab.selectedForeground": "var(--theme-text-muted)",
"terminal.inactiveSelectionBackground": "var(--theme-background-element)",
"widget.border": "var(--theme-border)",
},
displayName: "opencode",
name: "opencode",
semanticHighlighting: true,
semanticTokenColors: {
customLiteral: "var(--theme-syntax-function)",
newOperator: "var(--theme-syntax-operator)",
numberLiteral: "var(--theme-syntax-number)",
stringLiteral: "var(--theme-syntax-string)",
},
tokenColors: [
{
scope: [
"meta.embedded",
"source.groovy.embedded",
"string meta.image.inline.markdown",
"variable.legacy.builtin.python",
],
settings: {
foreground: "var(--theme-text)",
},
},
{
scope: "emphasis",
settings: {
fontStyle: "italic",
},
},
{
scope: "strong",
settings: {
fontStyle: "bold",
},
},
{
scope: "header",
settings: {
foreground: "var(--theme-markdown-heading)",
},
},
{
scope: "comment",
settings: {
foreground: "var(--theme-syntax-comment)",
},
},
{
scope: "constant.language",
settings: {
foreground: "var(--theme-syntax-keyword)",
},
},
{
scope: [
"constant.numeric",
"variable.other.enummember",
"keyword.operator.plus.exponent",
"keyword.operator.minus.exponent",
],
settings: {
foreground: "var(--theme-syntax-number)",
},
},
{
scope: "constant.regexp",
settings: {
foreground: "var(--theme-syntax-operator)",
},
},
{
scope: "entity.name.tag",
settings: {
foreground: "var(--theme-syntax-keyword)",
},
},
{
scope: ["entity.name.tag.css", "entity.name.tag.less"],
settings: {
foreground: "var(--theme-syntax-operator)",
},
},
{
scope: "entity.other.attribute-name",
settings: {
foreground: "var(--theme-syntax-variable)",
},
},
{
scope: [
"entity.other.attribute-name.class.css",
"source.css entity.other.attribute-name.class",
"entity.other.attribute-name.id.css",
"entity.other.attribute-name.parent-selector.css",
"entity.other.attribute-name.parent.less",
"source.css entity.other.attribute-name.pseudo-class",
"entity.other.attribute-name.pseudo-element.css",
"source.css.less entity.other.attribute-name.id",
"entity.other.attribute-name.scss",
],
settings: {
foreground: "var(--theme-syntax-operator)",
},
},
{
scope: "invalid",
settings: {
foreground: "var(--theme-error)",
},
},
{
scope: "markup.underline",
settings: {
fontStyle: "underline",
},
},
{
scope: "markup.bold",
settings: {
fontStyle: "bold",
foreground: "var(--theme-markdown-strong)",
},
},
{
scope: "markup.heading",
settings: {
fontStyle: "bold",
foreground: "var(--theme-markdown-heading)",
},
},
{
scope: "markup.italic",
settings: {
fontStyle: "italic",
},
},
{
scope: "markup.strikethrough",
settings: {
fontStyle: "strikethrough",
},
},
{
scope: "markup.inserted",
settings: {
foreground: "var(--theme-diff-added)",
},
},
{
scope: "markup.deleted",
settings: {
foreground: "var(--theme-diff-removed)",
},
},
{
scope: "markup.changed",
settings: {
foreground: "var(--theme-diff-context)",
},
},
{
scope: "punctuation.definition.quote.begin.markdown",
settings: {
foreground: "var(--theme-markdown-block-quote)",
},
},
{
scope: "punctuation.definition.list.begin.markdown",
settings: {
foreground: "var(--theme-markdown-list-enumeration)",
},
},
{
scope: "markup.inline.raw",
settings: {
foreground: "var(--theme-markdown-code)",
},
},
{
scope: "punctuation.definition.tag",
settings: {
foreground: "var(--theme-syntax-punctuation)",
},
},
{
scope: ["meta.preprocessor", "entity.name.function.preprocessor"],
settings: {
foreground: "var(--theme-syntax-keyword)",
},
},
{
scope: "meta.preprocessor.string",
settings: {
foreground: "var(--theme-syntax-string)",
},
},
{
scope: "meta.preprocessor.numeric",
settings: {
foreground: "var(--theme-syntax-number)",
},
},
{
scope: "meta.structure.dictionary.key.python",
settings: {
foreground: "var(--theme-syntax-variable)",
},
},
{
scope: "meta.diff.header",
settings: {
foreground: "var(--theme-diff-hunk-header)",
},
},
{
scope: "storage",
settings: {
foreground: "var(--theme-syntax-keyword)",
},
},
{
scope: "storage.type",
settings: {
foreground: "var(--theme-syntax-keyword)",
},
},
{
scope: ["storage.modifier", "keyword.operator.noexcept"],
settings: {
foreground: "var(--theme-syntax-keyword)",
},
},
{
scope: ["string", "meta.embedded.assembly"],
settings: {
foreground: "var(--theme-syntax-string)",
},
},
{
scope: "string.tag",
settings: {
foreground: "var(--theme-syntax-string)",
},
},
{
scope: "string.value",
settings: {
foreground: "var(--theme-syntax-string)",
},
},
{
scope: "string.regexp",
settings: {
foreground: "var(--theme-syntax-operator)",
},
},
{
scope: [
"punctuation.definition.template-expression.begin",
"punctuation.definition.template-expression.end",
"punctuation.section.embedded",
],
settings: {
foreground: "var(--theme-syntax-keyword)",
},
},
{
scope: ["meta.template.expression"],
settings: {
foreground: "var(--theme-text)",
},
},
{
scope: [
"support.type.vendored.property-name",
"support.type.property-name",
"source.css variable",
"source.coffee.embedded",
],
settings: {
foreground: "var(--theme-syntax-variable)",
},
},
{
scope: "keyword",
settings: {
foreground: "var(--theme-syntax-keyword)",
},
},
{
scope: "keyword.control",
settings: {
foreground: "var(--theme-syntax-keyword)",
},
},
{
scope: "keyword.operator",
settings: {
foreground: "var(--theme-syntax-operator)",
},
},
{
scope: [
"keyword.operator.new",
"keyword.operator.expression",
"keyword.operator.cast",
"keyword.operator.sizeof",
"keyword.operator.alignof",
"keyword.operator.typeid",
"keyword.operator.alignas",
"keyword.operator.instanceof",
"keyword.operator.logical.python",
"keyword.operator.wordlike",
],
settings: {
foreground: "var(--theme-syntax-keyword)",
},
},
{
scope: "keyword.other.unit",
settings: {
foreground: "var(--theme-syntax-number)",
},
},
{
scope: ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"],
settings: {
foreground: "var(--theme-syntax-keyword)",
},
},
{
scope: "support.function.git-rebase",
settings: {
foreground: "var(--theme-syntax-variable)",
},
},
{
scope: "constant.sha.git-rebase",
settings: {
foreground: "var(--theme-syntax-number)",
},
},
{
scope: [
"storage.modifier.import.java",
"variable.language.wildcard.java",
"storage.modifier.package.java",
],
settings: {
foreground: "var(--theme-text)",
},
},
{
scope: "variable.language",
settings: {
foreground: "var(--theme-syntax-keyword)",
},
},
{
scope: [
"entity.name.function",
"support.function",
"support.constant.handlebars",
"source.powershell variable.other.member",
"entity.name.operator.custom-literal",
],
settings: {
foreground: "var(--theme-syntax-function)",
},
},
{
scope: [
"support.class",
"support.type",
"entity.name.type",
"entity.name.namespace",
"entity.other.attribute",
"entity.name.scope-resolution",
"entity.name.class",
"storage.type.numeric.go",
"storage.type.byte.go",
"storage.type.boolean.go",
"storage.type.string.go",
"storage.type.uintptr.go",
"storage.type.error.go",
"storage.type.rune.go",
"storage.type.cs",
"storage.type.generic.cs",
"storage.type.modifier.cs",
"storage.type.variable.cs",
"storage.type.annotation.java",
"storage.type.generic.java",
"storage.type.java",
"storage.type.object.array.java",
"storage.type.primitive.array.java",
"storage.type.primitive.java",
"storage.type.token.java",
"storage.type.groovy",
"storage.type.annotation.groovy",
"storage.type.parameters.groovy",
"storage.type.generic.groovy",
"storage.type.object.array.groovy",
"storage.type.primitive.array.groovy",
"storage.type.primitive.groovy",
],
settings: {
foreground: "var(--theme-syntax-type)",
},
},
{
scope: [
"meta.type.cast.expr",
"meta.type.new.expr",
"support.constant.math",
"support.constant.dom",
"support.constant.json",
"entity.other.inherited-class",
"punctuation.separator.namespace.ruby",
],
settings: {
foreground: "var(--theme-syntax-type)",
},
},
{
scope: [
"keyword.control",
"source.cpp keyword.operator.new",
"keyword.operator.delete",
"keyword.other.using",
"keyword.other.directive.using",
"keyword.other.operator",
"entity.name.operator",
],
settings: {
foreground: "var(--theme-syntax-operator)",
},
},
{
scope: [
"variable",
"meta.definition.variable.name",
"support.variable",
"entity.name.variable",
"constant.other.placeholder",
],
settings: {
foreground: "var(--theme-syntax-variable)",
},
},
{
scope: ["variable.other.constant", "variable.other.enummember"],
settings: {
foreground: "var(--theme-syntax-variable)",
},
},
{
scope: ["meta.object-literal.key"],
settings: {
foreground: "var(--theme-syntax-variable)",
},
},
{
scope: [
"support.constant.property-value",
"support.constant.font-name",
"support.constant.media-type",
"support.constant.media",
"constant.other.color.rgb-value",
"constant.other.rgb-value",
"support.constant.color",
],
settings: {
foreground: "var(--theme-syntax-string)",
},
},
{
scope: [
"punctuation.definition.group.regexp",
"punctuation.definition.group.assertion.regexp",
"punctuation.definition.character-class.regexp",
"punctuation.character.set.begin.regexp",
"punctuation.character.set.end.regexp",
"keyword.operator.negation.regexp",
"support.other.parenthesis.regexp",
],
settings: {
foreground: "var(--theme-syntax-string)",
},
},
{
scope: [
"constant.character.character-class.regexp",
"constant.other.character-class.set.regexp",
"constant.other.character-class.regexp",
"constant.character.set.regexp",
],
settings: {
foreground: "var(--theme-syntax-operator)",
},
},
{
scope: ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"],
settings: {
foreground: "var(--theme-syntax-operator)",
},
},
{
scope: "keyword.operator.quantifier.regexp",
settings: {
foreground: "var(--theme-syntax-operator)",
},
},
{
scope: ["constant.character", "constant.other.option"],
settings: {
foreground: "var(--theme-syntax-keyword)",
},
},
{
scope: "constant.character.escape",
settings: {
foreground: "var(--theme-syntax-operator)",
},
},
{
scope: "entity.name.label",
settings: {
foreground: "var(--theme-text-muted)",
},
},
],
type: "dark",
},
transformers: [transformerNotationDiff()],
})
},
}),
)
function strip(text: string): string {
const wrappedRe = /^\s*<([A-Za-z]\w*)>\s*([\s\S]*?)\s*<\/\1>\s*$/
const match = text.match(wrappedRe)
return match ? match[2] : text
}
export default function Markdown(props: { text: string; class?: string }) {
const [html] = createResource(
() => strip(props.text),
async (markdown) => {
return markedWithShiki.parse(markdown)
},
)
return (
<div
class={`min-w-0 max-w-full text-xs overflow-auto no-scrollbar prose ${props.class ?? ""}`}
innerHTML={html()}
/>
)
}
-188
View File
@@ -1,188 +0,0 @@
import { Select as KobalteSelect } from "@kobalte/core/select"
import { createEffect, createMemo, Show } from "solid-js"
import type { ComponentProps } from "solid-js"
import { Icon } from "@/ui/icon"
import fuzzysort from "fuzzysort"
import { pipe, groupBy, entries, map } from "remeda"
import { createStore } from "solid-js/store"
export interface SelectProps<T> {
variant?: "default" | "outline"
size?: "sm" | "md" | "lg"
placeholder?: string
filter?:
| false
| {
placeholder?: string
keys: string[]
}
options: T[]
current?: T
value?: (x: T) => string
label?: (x: T) => string
groupBy?: (x: T) => string
onFilter?: (query: string) => void
onSelect?: (value: T | undefined) => void
class?: ComponentProps<"div">["class"]
classList?: ComponentProps<"div">["classList"]
}
export function Select<T>(props: SelectProps<T>) {
let inputRef: HTMLInputElement | undefined = undefined
let listboxRef: HTMLUListElement | undefined = undefined
const [store, setStore] = createStore({
filter: "",
})
const grouped = createMemo(() => {
const needle = store.filter.toLowerCase()
const result = pipe(
props.options,
(x) =>
!needle || !props.filter
? x
: fuzzysort.go(needle, x, { keys: props.filter && props.filter.keys }).map((x) => x.obj),
groupBy((x) => (props.groupBy ? props.groupBy(x) : "")),
// mapValues((x) => x.sort((a, b) => a.title.localeCompare(b.title))),
entries(),
map(([k, v]) => ({ category: k, options: v })),
)
return result
})
// const flat = createMemo(() => {
// return pipe(
// grouped(),
// flatMap(({ options }) => options),
// )
// })
createEffect(() => {
store.filter
listboxRef?.scrollTo(0, 0)
// setStore("selected", 0)
// scroll.scrollTo(0)
})
return (
<KobalteSelect<T, { category: string; options: T[] }>
value={props.current}
options={grouped()}
optionValue={(x) => (props.value ? props.value(x) : (x as string))}
optionTextValue={(x) => (props.label ? props.label(x) : (x as string))}
optionGroupChildren="options"
placeholder={props.placeholder}
sectionComponent={(props) => (
<KobalteSelect.Section class="text-xs uppercase text-text-muted/60 font-light mt-3 first:mt-0 ml-2">
{props.section.rawValue.category}
</KobalteSelect.Section>
)}
itemComponent={(itemProps) => (
<KobalteSelect.Item
classList={{
"relative flex cursor-pointer select-none items-center": true,
"rounded-sm px-2 py-0.5 text-xs outline-none text-text": true,
"transition-colors data-[disabled]:pointer-events-none": true,
"data-[highlighted]:bg-background-element data-[disabled]:opacity-50": true,
[props.class ?? ""]: !!props.class,
}}
{...itemProps}
>
<KobalteSelect.ItemLabel>
{props.label ? props.label(itemProps.item.rawValue) : (itemProps.item.rawValue as string)}
</KobalteSelect.ItemLabel>
<KobalteSelect.ItemIndicator
classList={{
"ml-auto": true,
}}
>
<Icon name="checkmark" size={16} />
</KobalteSelect.ItemIndicator>
</KobalteSelect.Item>
)}
onChange={(v) => {
if (props.onSelect) props.onSelect(v ?? undefined)
if (v !== null) {
// close the select
}
}}
onOpenChange={(v) => v || setStore("filter", "")}
>
<KobalteSelect.Trigger
classList={{
...(props.classList ?? {}),
"flex w-full items-center justify-between rounded-md transition-colors": true,
"focus-visible:outline-none focus-visible:ring focus-visible:ring-border-active/30": true,
"disabled:cursor-not-allowed disabled:opacity-50": true,
"data-[placeholder-shown]:text-text-muted cursor-pointer": true,
"hover:bg-background-element focus-visible:ring-border-active": true,
"bg-background-element text-text": props.variant === "default" || !props.variant,
"border-2 border-border bg-transparent text-text": props.variant === "outline",
"h-6 pl-2 text-xs": props.size === "sm",
"h-8 pl-3 text-sm": props.size === "md" || !props.size,
"h-10 pl-4 text-base": props.size === "lg",
[props.class ?? ""]: !!props.class,
}}
>
<KobalteSelect.Value<T>>
{(state) => {
const selected = state.selectedOption() ?? props.current
if (!selected) return props.placeholder || ""
if (props.label) return props.label(selected)
return selected as string
}}
</KobalteSelect.Value>
<KobalteSelect.Icon
classList={{
"size-fit shrink-0 text-text-muted transition-transform duration-100 data-[expanded]:rotate-180": true,
}}
>
<Icon name="chevron-down" size={24} />
</KobalteSelect.Icon>
</KobalteSelect.Trigger>
<KobalteSelect.Portal>
<KobalteSelect.Content
onKeyDown={(e) => {
if (!props.filter) return
if (e.key === "ArrowUp" || e.key === "ArrowDown" || e.key === "Escape") {
return
}
inputRef?.focus()
}}
classList={{
"min-w-32 overflow-hidden rounded-md border border-border-subtle/40": true,
"bg-background-panel p-1 shadow-md z-50": true,
"data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:zoom-out-95": true,
"data-[expanded]:animate-in data-[expanded]:fade-in-0 data-[expanded]:zoom-in-95": true,
}}
>
<Show when={props.filter}>
<input
ref={(el) => (inputRef = el)}
id="select-filter"
type="text"
placeholder={props.filter ? props.filter.placeholder : "Filter items"}
value={store.filter}
onInput={(e) => setStore("filter", e.currentTarget.value)}
onKeyDown={(e) => {
if (e.key === "ArrowUp" || e.key === "ArrowDown" || e.key === "Escape") {
e.preventDefault()
e.stopPropagation()
listboxRef?.focus()
}
}}
classList={{
"w-full": true,
"px-2 pb-2 text-text font-light placeholder-text-muted/70 text-xs focus:outline-none": true,
}}
/>
</Show>
<KobalteSelect.Listbox
ref={(el) => (listboxRef = el)}
classList={{
"overflow-y-auto max-h-48 no-scrollbar": true,
}}
/>
</KobalteSelect.Content>
</KobalteSelect.Portal>
</KobalteSelect>
)
}
@@ -1,28 +0,0 @@
import { useSync, useLocal } from "@/context"
import { Button, Tooltip } from "@/ui"
import { VList } from "virtua/solid"
export default function SessionList() {
const sync = useSync()
const local = useLocal()
return (
<VList data={sync.data.session} class="p-2 no-scrollbar">
{(session) => (
<Tooltip placement="right" value={session.title} class="w-full min-w-0">
<Button
size="sm"
variant="ghost"
classList={{
"w-full min-w-0 py-1 text-left truncate justify-start text-text-muted text-xs": true,
"text-text!": local.session.active()?.id === session.id,
}}
onClick={() => local.session.setActive(session.id)}
>
<span class="truncate">{session.title}</span>
</Button>
</Tooltip>
)}
</VList>
)
}
@@ -1,369 +0,0 @@
import { useLocal, useSync } from "@/context"
import { Collapsible, Icon, type IconProps } from "@/ui"
import type { Part, ToolPart } from "@opencode-ai/sdk"
import { DateTime } from "luxon"
import {
createSignal,
onMount,
For,
Match,
splitProps,
Switch,
type ComponentProps,
type ParentProps,
createEffect,
createMemo,
} from "solid-js"
import { getFilename } from "@/utils"
import Markdown from "./markdown"
import { Code } from "./code"
import { createElementSize } from "@solid-primitives/resize-observer"
import { createScrollPosition } from "@solid-primitives/scroll"
function TimelineIcon(props: { name: IconProps["name"]; class?: string }) {
return (
<div
classList={{
"relative flex flex-none self-start items-center justify-center bg-background h-6 w-6": true,
[props.class ?? ""]: !!props.class,
}}
>
<Icon name={props.name} class="text-text/40" size={18} />
</div>
)
}
function CollapsibleTimelineIcon(props: { name: IconProps["name"]; class?: string }) {
return (
<>
<TimelineIcon
name={props.name}
class={`group-hover/li:hidden group-has-[[data-expanded]]/li:hidden ${props.class ?? ""}`}
/>
<TimelineIcon
name="chevron-right"
class={`hidden group-hover/li:flex group-has-[[data-expanded]]/li:hidden ${props.class ?? ""}`}
/>
<TimelineIcon name="chevron-down" class={`hidden group-has-[[data-expanded]]/li:flex ${props.class ?? ""}`} />
</>
)
}
function ToolIcon(props: { part: ToolPart }) {
return (
<Switch fallback={<TimelineIcon name="hammer" />}>
<Match when={props.part.tool === "read"}>
<TimelineIcon name="file" />
</Match>
<Match when={props.part.tool === "edit"}>
<CollapsibleTimelineIcon name="pencil" />
</Match>
<Match when={props.part.tool === "write"}>
<CollapsibleTimelineIcon name="file-plus" />
</Match>
</Switch>
)
}
function Part(props: ParentProps & ComponentProps<"div">) {
const [local, others] = splitProps(props, ["class", "classList", "children"])
return (
<div
classList={{
...(local.classList ?? {}),
"h-6 flex items-center": true,
[local.class ?? ""]: !!local.class,
}}
{...others}
>
<p class="text-xs leading-4 text-left text-text-muted/60 font-medium">{local.children}</p>
</div>
)
}
function CollapsiblePart(props: { title: ParentProps["children"] } & ParentProps & ComponentProps<typeof Collapsible>) {
return (
<Collapsible {...props}>
<Collapsible.Trigger class="peer/collapsible">
<Part>{props.title}</Part>
</Collapsible.Trigger>
<Collapsible.Content>
<p class="flex-auto py-1 text-xs min-w-0 text-pretty">
<span class="text-text-muted/60 break-words">{props.children}</span>
</p>
</Collapsible.Content>
</Collapsible>
)
}
function ReadToolPart(props: { part: ToolPart }) {
const local = useLocal()
return (
<Switch>
<Match when={props.part.state.status === "completed" && props.part.state}>
{(state) => {
const path = state().input["filePath"] as string
return (
<Part class="cursor-pointer" onClick={() => local.file.open(path)}>
<span class="text-text-muted">Read</span> {getFilename(path)}
</Part>
)
}}
</Match>
</Switch>
)
}
function EditToolPart(props: { part: ToolPart }) {
return (
<Switch>
<Match when={props.part.state.status === "completed" && props.part.state}>
{(state) => (
<CollapsiblePart
defaultOpen
title={
<>
<span class="text-text-muted">Edit</span> {getFilename(state().input["filePath"] as string)}
</>
}
>
<Code
path={state().input["filePath"] as string}
code={state().metadata["diff"] as string}
class="[&_code]:pb-0!"
/>
</CollapsiblePart>
)}
</Match>
</Switch>
)
}
function WriteToolPart(props: { part: ToolPart }) {
return (
<Switch>
<Match when={props.part.state.status === "completed" && props.part.state}>
{(state) => (
<CollapsiblePart
title={
<>
<span class="text-text-muted">Write</span> {getFilename(state().input["filePath"] as string)}
</>
}
>
<div class="p-2 bg-background-panel rounded-md border border-border-subtle"></div>
</CollapsiblePart>
)}
</Match>
</Switch>
)
}
function ToolPart(props: { part: ToolPart }) {
return (
<Switch
fallback={
<div class="flex-auto min-w-0 text-xs">
{props.part.type}:{props.part.tool}
</div>
}
>
<Match when={props.part.tool === "read"}>
<div class="min-w-0 flex-auto">
<ReadToolPart part={props.part} />
</div>
</Match>
<Match when={props.part.tool === "edit"}>
<div class="min-w-0 flex-auto">
<EditToolPart part={props.part} />
</div>
</Match>
<Match when={props.part.tool === "write"}>
<div class="min-w-0 flex-auto">
<WriteToolPart part={props.part} />
</div>
</Match>
</Switch>
)
}
export default function SessionTimeline(props: { session: string; class?: string }) {
const sync = useSync()
const [scrollElement, setScrollElement] = createSignal<HTMLElement | undefined>(undefined)
const [root, setRoot] = createSignal<HTMLDivElement | undefined>(undefined)
const [tail, setTail] = createSignal(true)
const size = createElementSize(root)
const scroll = createScrollPosition(scrollElement)
onMount(() => sync.session.sync(props.session))
const messages = createMemo(() => sync.data.message[props.session] ?? [])
const working = createMemo(() => {
const last = messages()[messages().length - 1]
if (!last) return false
if (last.role === "user") return true
return !last.time.completed
})
const getScrollParent = (el: HTMLElement | null): HTMLElement | undefined => {
let p = el?.parentElement
while (p && p !== document.body) {
const s = getComputedStyle(p)
if (s.overflowY === "auto" || s.overflowY === "scroll") return p
p = p.parentElement
}
return undefined
}
createEffect(() => {
if (!root()) return
setScrollElement(getScrollParent(root()!))
})
const scrollToBottom = () => {
const element = scrollElement()
if (!element) return
element.scrollTop = element.scrollHeight
}
createEffect(() => {
size.height
if (tail()) scrollToBottom()
})
createEffect(() => {
if (working()) {
setTail(true)
scrollToBottom()
}
})
let lastScrollY = 0
createEffect(() => {
if (scroll.y < lastScrollY) {
setTail(false)
}
lastScrollY = scroll.y
})
const valid = (part: Part) => {
if (!part) return false
switch (part.type) {
case "step-start":
case "step-finish":
case "file":
case "patch":
return false
case "text":
return !part.synthetic
case "reasoning":
return part.text.trim()
default:
return true
}
}
const duration = (part: Part) => {
switch (part.type) {
default:
if (
"time" in part &&
part.time &&
"start" in part.time &&
part.time.start &&
"end" in part.time &&
part.time.end
) {
const start = DateTime.fromMillis(part.time.start)
const end = DateTime.fromMillis(part.time.end)
return end.diff(start).toFormat("s")
}
return ""
}
}
return (
<div
ref={setRoot}
classList={{
"p-4 select-text flex flex-col gap-y-8": true,
[props.class ?? ""]: !!props.class,
}}
>
<For each={messages()}>
{(message) => (
<ul role="list" class="space-y-2">
<For each={sync.data.part[message.id]?.filter(valid)}>
{(part) => (
<li classList={{ "relative group/li flex gap-x-4 min-w-0 w-full": true }}>
<div
classList={{
"absolute top-0 left-0 flex w-6 justify-center": true,
"last:h-10 not-last:-bottom-10": true,
}}
>
<div class="w-px bg-border-subtle" />
</div>
<Switch
fallback={
<div class="m-0.5 relative flex size-5 flex-none items-center justify-center bg-background">
<div class="size-1 rounded-full bg-text/10 ring ring-text/20" />
</div>
}
>
<Match when={part.type === "text"}>
<Switch>
<Match when={message.role === "user"}>
<TimelineIcon name="avatar-square" />
</Match>
<Match when={message.role === "assistant"}>
<TimelineIcon name="sparkles" />
</Match>
</Switch>
</Match>
<Match when={part.type === "reasoning"}>
<CollapsibleTimelineIcon name="brain" />
</Match>
<Match when={part.type === "tool" && part}>{(part) => <ToolIcon part={part()} />}</Match>
</Switch>
<Switch fallback={<div class="flex-auto min-w-0 text-xs mt-1 text-left">{part.type}</div>}>
<Match when={part.type === "text" && part}>
{(part) => (
<Switch>
<Match when={message.role === "user"}>
<div class="w-full flex flex-col items-end justify-stretch gap-y-1.5 min-w-0">
<p class="w-full rounded-md p-3 ring-1 ring-text/15 ring-inset text-xs bg-background-panel">
<span class="font-medium text-text whitespace-pre-wrap break-words">{part().text}</span>
</p>
<p class="text-xs text-text-muted">12:07pm · adam</p>
</div>
</Match>
<Match when={message.role === "assistant"}>
<Markdown text={part().text} class="text-text" />
</Match>
</Switch>
)}
</Match>
<Match when={part.type === "reasoning" && part}>
{(part) => (
<CollapsiblePart
title={
<>
<span class="text-text-muted">Thought</span> for {duration(part())}s
</>
}
>
<Markdown text={part().text} />
</CollapsiblePart>
)}
</Match>
<Match when={part.type === "tool" && part}>{(part) => <ToolPart part={part()} />}</Match>
</Switch>
</li>
)}
</For>
</ul>
)}
</For>
</div>
)
}
@@ -1,48 +0,0 @@
import { For } from "solid-js"
import { Icon, Link, Logo, Tooltip } from "@/ui"
import { useLocation } from "@solidjs/router"
const navigation = [
{ name: "Sessions", href: "/sessions", icon: "dashboard" as const },
{ name: "Commands", href: "/commands", icon: "slash" as const },
{ name: "Agents", href: "/agents", icon: "bolt" as const },
{ name: "Providers", href: "/providers", icon: "cloud" as const },
{ name: "Tools (MCP)", href: "/tools", icon: "hammer" as const },
{ name: "LSP", href: "/lsp", icon: "code" as const },
{ name: "Settings", href: "/settings", icon: "settings" as const },
]
export default function SidebarNav() {
const location = useLocation()
return (
<div class="hidden md:fixed md:inset-y-0 md:left-0 md:z-50 md:block md:w-16 md:overflow-y-auto md:bg-background-panel md:pb-4">
<div class="flex h-16 shrink-0 items-center justify-center">
<Logo variant="mark" size={28} />
</div>
<nav class="mt-5">
<ul role="list" class="flex flex-col items-center space-y-1">
<For each={navigation}>
{(item) => (
<li>
<Tooltip placement="right" value={item.name}>
<Link
href={item.href}
classList={{
"bg-background-element text-text": location.pathname.startsWith(item.href),
"text-text-muted hover:bg-background-element hover:text-text": location.pathname !== item.href,
"flex gap-x-3 rounded-md p-3 text-sm font-semibold": true,
"focus-visible:outline-1 focus-visible:-outline-offset-1 focus-visible:outline-border-active": true,
}}
>
<Icon name={item.icon} size={20} />
<span class="sr-only">{item.name}</span>
</Link>
</Tooltip>
</li>
)}
</For>
</ul>
</nav>
</div>
)
}
-4
View File
@@ -1,4 +0,0 @@
export { LocalProvider, useLocal } from "./local"
export { SDKProvider, useSDK } from "./sdk"
export { SyncProvider, useSync } from "./sync"
export { ThemeProvider, useTheme } from "./theme"
-418
View File
@@ -1,418 +0,0 @@
import { createStore, produce, reconcile } from "solid-js/store"
import { batch, createContext, createEffect, createMemo, useContext, type ParentProps } from "solid-js"
import { useSync } from "./sync"
import { uniqueBy } from "remeda"
import type { FileContent, FileNode } from "@opencode-ai/sdk"
import { useSDK } from "./sdk"
export type LocalFile = FileNode &
Partial<{
loaded: boolean
pinned: boolean
expanded: boolean
content: FileContent
selection: { startLine: number; startChar: number; endLine: number; endChar: number }
scrollTop: number
view: "raw" | "diff-unified" | "diff-split"
folded: string[]
selectedChange: number
}>
export type TextSelection = LocalFile["selection"]
export type View = LocalFile["view"]
function init() {
const sdk = useSDK()
const sync = useSync()
const list = createMemo(() => sync.data.agent.filter((x) => x.mode !== "subagent"))
const agent = (() => {
const [store, setStore] = createStore<{
current: string
}>({
current: list()[0].name,
})
return {
list,
current() {
return list().find((x) => x.name === store.current)!
},
set(name: string | undefined) {
setStore("current", name ?? list()[0].name)
},
move(direction: 1 | -1) {
let next = list().findIndex((x) => x.name === store.current) + direction
if (next < 0) next = list().length - 1
if (next >= list().length) next = 0
const value = list()[next]
setStore("current", value.name)
if (value.model)
model.set({
providerID: value.model.providerID,
modelID: value.model.modelID,
})
},
}
})()
const model = (() => {
const [store, setStore] = createStore<{
model: Record<
string,
{
providerID: string
modelID: string
}
>
recent: {
providerID: string
modelID: string
}[]
}>({
model: {},
recent: [],
})
const value = localStorage.getItem("model")
setStore("recent", JSON.parse(value ?? "[]"))
createEffect(() => {
localStorage.setItem("model", JSON.stringify(store.recent))
})
const fallback = createMemo(() => {
if (store.recent.length) return store.recent[0]
const provider = sync.data.provider[0]
const model = Object.values(provider.models)[0]
return {
providerID: provider.id,
modelID: model.id,
}
})
const current = createMemo(() => {
const a = agent.current()
return store.model[agent.current().name] ?? (a.model ? a.model : fallback())
})
const list = createMemo(() =>
sync.data.provider.flatMap((x) => Object.values(x.models).map((m) => ({ providerID: x.id, modelID: m.id }))),
)
return {
list,
current,
recent() {
return store.recent
},
parsed: createMemo(() => {
const value = current()
const provider = sync.data.provider.find((x) => x.id === value.providerID)!
const model = provider.models[value.modelID]
return {
provider: provider.name ?? value.providerID,
model: model.name ?? value.modelID,
}
}),
set(model: { providerID: string; modelID: string } | undefined, options?: { recent?: boolean }) {
batch(() => {
setStore("model", agent.current().name, model ?? fallback())
if (options?.recent && model) {
const uniq = uniqueBy([model, ...store.recent], (x) => x.providerID + x.modelID)
if (uniq.length > 5) uniq.pop()
setStore("recent", uniq)
}
})
},
}
})()
const file = (() => {
const [store, setStore] = createStore<{
node: Record<string, LocalFile>
opened: string[]
active?: string
}>({
node: Object.fromEntries(sync.data.node.map((x) => [x.path, x])),
opened: [],
})
const active = createMemo(() => {
if (!store.active) return undefined
return store.node[store.active]
})
const opened = createMemo(() => store.opened.map((x) => store.node[x]))
const changes = createMemo(() => new Set(sync.data.changes.map((f) => f.path)))
const status = (path: string) => sync.data.changes.find((f) => f.path === path)
const changed = (path: string) => {
const set = changes()
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, {
loaded: undefined,
pinned: undefined,
content: undefined,
selection: undefined,
scrollTop: undefined,
folded: undefined,
view: undefined,
selectedChange: undefined,
})
}
const load = async (path: string) =>
sdk.file.read({ query: { path } }).then((x) => {
setStore(
"node",
path,
produce((draft) => {
draft.loaded = true
draft.content = x.data
}),
)
})
const open = async (path: string) => {
const relative = path.replace(sync.data.path.directory + "/", "")
if (!store.node[relative]) {
const parent = relative.split("/").slice(0, -1).join("/")
if (parent) {
await list(parent)
}
}
setStore("opened", (x) => {
if (x.includes(relative)) return x
return [
...opened()
.filter((x) => x.pinned)
.map((x) => x.path),
relative,
]
})
setStore("active", relative)
if (store.node[relative].loaded) return
return load(relative)
}
const list = async (path: string) => {
return sdk.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
})
}),
)
})
}
sdk.event.subscribe().then(async (events) => {
for await (const event of events.stream) {
switch (event.type) {
case "message.part.updated":
const part = event.properties.part
if (part.type === "tool" && part.state.status === "completed") {
switch (part.tool) {
case "read":
console.log("read", part.state.input)
break
case "edit":
const absolute = part.state.input["filePath"] as string
const path = absolute.replace(sync.data.path.directory + "/", "")
load(path)
break
default:
break
}
}
break
}
}
})
return {
active,
opened,
node: (path: string) => store.node[path],
update: (path: string, node: LocalFile) => setStore("node", path, reconcile(node)),
open,
load,
close(path: string) {
setStore("opened", (opened) => opened.filter((x) => x !== path))
if (store.active === path) {
const index = store.opened.findIndex((f) => f === path)
const previous = store.opened[Math.max(0, index - 1)]
setStore("active", previous)
}
resetNode(path)
},
expand(path: string) {
setStore("node", path, "expanded", true)
if (store.node[path].loaded) return
setStore("node", path, "loaded", true)
list(path)
},
collapse(path: string) {
setStore("node", path, "expanded", false)
},
select(path: string, selection: TextSelection | undefined) {
setStore("node", path, "selection", selection)
},
scroll(path: string, scrollTop: number) {
setStore("node", path, "scrollTop", scrollTop)
},
move(path: string, to: number) {
const index = store.opened.findIndex((f) => f === path)
if (index === -1) return
setStore(
"opened",
produce((opened) => {
opened.splice(to, 0, opened.splice(index, 1)[0])
}),
)
setStore("node", path, "pinned", true)
},
view(path: string): View {
const n = store.node[path]
return n && n.view ? n.view : "raw"
},
setView(path: string, view: View) {
setStore("node", path, "view", view)
},
unfold(path: string, key: string) {
setStore("node", path, "folded", (xs) => {
const a = xs ?? []
if (a.includes(key)) return a
return [...a, key]
})
},
fold(path: string, key: string) {
setStore("node", path, "folded", (xs) => (xs ?? []).filter((k) => k !== key))
},
folded(path: string) {
const n = store.node[path]
return n && n.folded ? n.folded : []
},
changeIndex(path: string) {
return store.node[path]?.selectedChange
},
setChangeIndex(path: string, index: number | undefined) {
setStore("node", path, "selectedChange", index)
},
changed,
status,
children(path: string) {
return Object.values(store.node).filter(
(x) =>
x.path.startsWith(path) &&
x.path !== path &&
!x.path.replace(new RegExp(`^${path + "/"}`), "").includes("/"),
)
},
}
})()
const layout = (() => {
const [store, setStore] = createStore<{
rightPane: boolean
leftWidth: number
rightWidth: number
}>({
rightPane: false,
leftWidth: 200, // Default 50 * 4px (w-50 = 12.5rem = 200px)
rightWidth: 320, // Default 80 * 4px (w-80 = 20rem = 320px)
})
const value = localStorage.getItem("layout")
if (value) {
const v = JSON.parse(value)
if (typeof v?.rightPane === "boolean") setStore("rightPane", v.rightPane)
if (typeof v?.leftWidth === "number") setStore("leftWidth", Math.max(150, Math.min(400, v.leftWidth)))
if (typeof v?.rightWidth === "number") setStore("rightWidth", Math.max(200, Math.min(500, v.rightWidth)))
}
createEffect(() => {
localStorage.setItem("layout", JSON.stringify(store))
})
return {
rightPane() {
return store.rightPane
},
leftWidth() {
return store.leftWidth
},
rightWidth() {
return store.rightWidth
},
toggleRightPane() {
setStore("rightPane", (x) => !x)
},
openRightPane() {
setStore("rightPane", true)
},
closeRightPane() {
setStore("rightPane", false)
},
setLeftWidth(width: number) {
setStore("leftWidth", Math.max(150, Math.min(400, width)))
},
setRightWidth(width: number) {
setStore("rightWidth", Math.max(200, Math.min(500, width)))
},
}
})()
const session = (() => {
const [store, setStore] = createStore<{
active?: string
}>({})
const active = createMemo(() => {
if (!store.active) return undefined
return sync.session.get(store.active)
})
return {
active,
setActive(sessionId: string | undefined) {
setStore("active", sessionId)
},
clearActive() {
setStore("active", undefined)
},
}
})()
const result = {
model,
agent,
file,
layout,
session,
}
return result
}
type LocalContext = ReturnType<typeof init>
const ctx = createContext<LocalContext>()
export function LocalProvider(props: ParentProps) {
const value = init()
return <ctx.Provider value={value}>{props.children}</ctx.Provider>
}
export function useLocal() {
const value = useContext(ctx)
if (!value) {
throw new Error("useLocal must be used within a LocalProvider")
}
return value
}
-29
View File
@@ -1,29 +0,0 @@
import { createContext, useContext, type ParentProps } from "solid-js"
import { createOpencodeClient } from "@opencode-ai/sdk/client"
const host = import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "127.0.0.1"
const port = import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"
function init() {
const client = createOpencodeClient({
baseUrl: `http://${host}:${port}`,
})
return client
}
type SDKContext = ReturnType<typeof init>
const ctx = createContext<SDKContext>()
export function SDKProvider(props: ParentProps) {
const value = init()
return <ctx.Provider value={value}>{props.children}</ctx.Provider>
}
export function useSDK() {
const value = useContext(ctx)
if (!value) {
throw new Error("useSDK must be used within a SDKProvider")
}
return value
}
-165
View File
@@ -1,165 +0,0 @@
import type { Message, Agent, Provider, Session, Part, Config, Path, File, FileNode } from "@opencode-ai/sdk"
import { createStore, produce, reconcile } from "solid-js/store"
import { useSDK } from "./sdk"
import { createContext, Show, useContext, type ParentProps } from "solid-js"
import { Binary } from "@/utils/binary"
function init() {
const [store, setStore] = createStore<{
ready: boolean
provider: Provider[]
agent: Agent[]
config: Config
path: Path
session: Session[]
message: {
[sessionID: string]: Message[]
}
part: {
[messageID: string]: Part[]
}
node: FileNode[]
changes: File[]
}>({
config: {},
path: { state: "", config: "", worktree: "", directory: "" },
ready: false,
agent: [],
provider: [],
session: [],
message: {},
part: {},
node: [],
changes: [],
})
const sdk = useSDK()
sdk.event.subscribe().then(async (events) => {
for await (const event of events.stream) {
switch (event.type) {
case "session.updated": {
const result = Binary.search(store.session, event.properties.info.id, (s) => s.id)
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 "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.part.updated": {
const parts = store.part[event.properties.part.messageID]
if (!parts) {
setStore("part", event.properties.part.messageID, [event.properties.part])
break
}
const result = Binary.search(parts, event.properties.part.id, (p) => p.id)
if (result.found) {
setStore("part", event.properties.part.messageID, result.index, reconcile(event.properties.part))
break
}
setStore(
"part",
event.properties.part.messageID,
produce((draft) => {
draft.splice(result.index, 0, event.properties.part)
}),
)
break
}
}
}
})
Promise.all([
sdk.config.providers().then((x) => setStore("provider", x.data!.providers)),
sdk.path.get().then((x) => setStore("path", x.data!)),
sdk.app.agents().then((x) => setStore("agent", x.data ?? [])),
sdk.session.list().then((x) =>
setStore(
"session",
(x.data ?? []).slice().sort((a, b) => a.id.localeCompare(b.id)),
),
),
sdk.config.get().then((x) => setStore("config", x.data!)),
sdk.file.status().then((x) => setStore("changes", x.data!)),
sdk.file.list({ query: { path: "/" } }).then((x) => setStore("node", x.data!)),
]).then(() => setStore("ready", true))
return {
data: store,
set: setStore,
session: {
get(sessionID: string) {
const match = Binary.search(store.session, sessionID, (s) => s.id)
if (match.found) return store.session[match.index]
return undefined
},
async sync(sessionID: string) {
const [session, messages] = await Promise.all([
sdk.session.get({ path: { id: sessionID } }),
sdk.session.messages({ path: { id: sessionID } }),
])
setStore(
produce((draft) => {
const match = Binary.search(draft.session, sessionID, (s) => s.id)
draft.session[match.index] = session.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))
}
}),
)
},
},
}
}
type SyncContext = ReturnType<typeof init>
const ctx = createContext<SyncContext>()
export function SyncProvider(props: ParentProps) {
const value = init()
return (
<Show when={value.data.ready}>
<ctx.Provider value={value}>{props.children}</ctx.Provider>
</Show>
)
}
export function useSync() {
const value = useContext(ctx)
if (!value) {
throw new Error("useSync must be used within a SyncProvider")
}
return value
}
-92
View File
@@ -1,92 +0,0 @@
import {
createContext,
useContext,
createSignal,
createEffect,
onMount,
type ParentComponent,
onCleanup,
} from "solid-js"
export interface ThemeContextValue {
theme: string | undefined
isDark: boolean
setTheme: (themeName: string) => void
setDarkMode: (isDark: boolean) => void
}
const ThemeContext = createContext<ThemeContextValue>()
export const useTheme = () => {
const context = useContext(ThemeContext)
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider")
}
return context
}
interface ThemeProviderProps {
defaultTheme?: string
defaultDarkMode?: boolean
}
const themes = ["opencode", "tokyonight", "ayu", "nord", "catppuccin"]
export const ThemeProvider: ParentComponent<ThemeProviderProps> = (props) => {
const [theme, setThemeSignal] = createSignal<string | undefined>()
const [isDark, setIsDark] = createSignal(props.defaultDarkMode ?? false)
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "t" && event.ctrlKey) {
event.preventDefault()
const current = theme()
if (!current) return
const index = themes.indexOf(current)
const next = themes[(index + 1) % themes.length]
setTheme(next)
}
}
onMount(() => {
window.addEventListener("keydown", handleKeyDown)
})
onCleanup(() => {
window.removeEventListener("keydown", handleKeyDown)
})
onMount(() => {
const savedTheme = localStorage.getItem("theme") ?? "opencode"
const savedDarkMode = localStorage.getItem("darkMode") ?? "true"
setIsDark(savedDarkMode === "true")
setTheme(savedTheme)
})
createEffect(() => {
const currentTheme = theme()
const darkMode = isDark()
if (currentTheme) {
document.documentElement.setAttribute("data-theme", currentTheme)
document.documentElement.setAttribute("data-dark", darkMode.toString())
}
})
const setTheme = async (theme: string) => {
setThemeSignal(theme)
localStorage.setItem("theme", theme)
}
const setDarkMode = (dark: boolean) => {
setIsDark(dark)
localStorage.setItem("darkMode", dark.toString())
}
const contextValue: ThemeContextValue = {
theme: theme(),
isDark: isDark(),
setTheme,
setDarkMode,
}
return <ThemeContext.Provider value={contextValue}>{props.children}</ThemeContext.Provider>
}
-155
View File
@@ -1,155 +0,0 @@
@import "tailwindcss";
:root {
interpolate-size: allow-keywords;
}
@layer components {
[data-popper-positioner] {
pointer-events: none;
}
body {
line-height: 1;
}
::selection {
background-color: color-mix(in srgb, var(--color-primary) 33%, transparent);
/* background-color: var(--color-primary); */
/* color: var(--color-background); */
}
.prose h1 {
color: var(--color-text);
font-size: var(--text-sm);
line-height: var(--text-sm--line-height);
margin-bottom: calc(var(--spacing) * 3);
}
.prose h2 {
color: var(--color-text);
font-size: var(--text-sm);
line-height: var(--text-sm--line-height);
margin-bottom: calc(var(--spacing) * 3);
}
.prose h3 {
color: var(--color-text);
font-size: var(--text-xs);
line-height: var(--text-xs--line-height);
margin-bottom: calc(var(--spacing) * 2);
}
.prose h4 {
color: var(--color-text);
font-size: var(--text-xs);
line-height: var(--text-xs--line-height);
margin-bottom: calc(var(--spacing) * 2);
}
.prose h5 {
color: var(--color-text);
font-size: var(--text-xs);
line-height: var(--text-xs--line-height);
margin-bottom: calc(var(--spacing) * 2);
}
.prose h6 {
color: var(--color-text);
font-size: var(--text-xs);
line-height: var(--text-xs--line-height);
margin-bottom: calc(var(--spacing) * 2);
}
.prose p {
font-size: var(--text-xs);
line-height: var(--text-xs--line-height);
margin-bottom: calc(var(--spacing) * 2);
}
.prose strong {
color: var(--color-text);
}
.prose ul,
ol {
list-style-type: disc;
list-style-position: inside;
margin-bottom: calc(var(--spacing) * 2);
}
.prose pre {
background-color: var(--color-background-panel);
padding: calc(var(--spacing) * 2);
border-radius: var(--radius-md);
border: 1px solid var(--color-border-subtle);
overflow-x: auto;
white-space: pre;
margin-bottom: calc(var(--spacing) * 2);
@apply no-scrollbar;
}
.prose code {
font-family: var(--font-mono);
font-size: var(--text-xs);
line-height: var(--text-xs--line-height);
}
.prose blockquote {
margin-bottom: calc(var(--spacing) * 2);
}
}
@utility no-scrollbar {
&::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
& {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
}
@theme {
--color-*: initial;
--color-primary: var(--theme-primary);
--color-secondary: var(--theme-secondary);
--color-accent: var(--theme-accent);
--color-error: var(--theme-error);
--color-warning: var(--theme-warning);
--color-success: var(--theme-success);
--color-info: var(--theme-info);
--color-text: var(--theme-text);
--color-text-muted: var(--theme-text-muted);
--color-background: var(--theme-background);
--color-background-panel: var(--theme-background-panel);
--color-background-element: var(--theme-background-element);
--color-border: var(--theme-border);
--color-border-active: var(--theme-border-active);
--color-border-subtle: var(--theme-border-subtle);
--color-diff-added: var(--theme-diff-added);
--color-diff-removed: var(--theme-diff-removed);
--color-diff-context: var(--theme-diff-context);
--color-diff-hunk-header: var(--theme-diff-hunk-header);
--color-diff-highlight-added: var(--theme-diff-highlight-added);
--color-diff-highlight-removed: var(--theme-diff-highlight-removed);
--color-diff-added-bg: var(--theme-diff-added-bg);
--color-diff-removed-bg: var(--theme-diff-removed-bg);
--color-diff-context-bg: var(--theme-diff-context-bg);
--color-diff-line-number: var(--theme-diff-line-number);
--color-diff-added-line-number-bg: var(--theme-diff-added-line-number-bg);
--color-diff-removed-line-number-bg: var(--theme-diff-removed-line-number-bg);
--color-markdown-text: var(--theme-markdown-text);
--color-markdown-heading: var(--theme-markdown-heading);
--color-markdown-link: var(--theme-markdown-link);
--color-markdown-link-text: var(--theme-markdown-link-text);
--color-markdown-code: var(--theme-markdown-code);
--color-markdown-block-quote: var(--theme-markdown-block-quote);
--color-markdown-emph: var(--theme-markdown-emph);
--color-markdown-strong: var(--theme-markdown-strong);
--color-markdown-horizontal-rule: var(--theme-markdown-horizontal-rule);
--color-markdown-list-item: var(--theme-markdown-list-item);
--color-markdown-list-enumeration: var(--theme-markdown-list-enumeration);
--color-markdown-image: var(--theme-markdown-image);
--color-markdown-image-text: var(--theme-markdown-image-text);
--color-markdown-code-block: var(--theme-markdown-code-block);
--color-syntax-comment: var(--theme-syntax-comment);
--color-syntax-keyword: var(--theme-syntax-keyword);
--color-syntax-function: var(--theme-syntax-function);
--color-syntax-variable: var(--theme-syntax-variable);
--color-syntax-string: var(--theme-syntax-string);
--color-syntax-number: var(--theme-syntax-number);
--color-syntax-type: var(--theme-syntax-type);
--color-syntax-operator: var(--theme-syntax-operator);
--color-syntax-punctuation: var(--theme-syntax-punctuation);
}
-34
View File
@@ -1,34 +0,0 @@
/* @refresh reload */
import { render } from "solid-js/web"
import { Router, Route } from "@solidjs/router"
import "@/index.css"
import Layout from "@/pages/layout"
import Home from "@/pages"
import { SDKProvider, SyncProvider, LocalProvider, ThemeProvider } from "@/context"
const root = document.getElementById("root")
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
throw new Error(
"Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?",
)
}
render(
() => (
<div class="h-full bg-background text-text-muted">
<ThemeProvider defaultTheme="opencode" defaultDarkMode={true}>
<SDKProvider>
<SyncProvider>
<LocalProvider>
<Router root={Layout}>
<Route path="/" component={Home} />
</Router>
</LocalProvider>
</SyncProvider>
</SDKProvider>
</ThemeProvider>
</div>
),
root!,
)
-617
View File
@@ -1,617 +0,0 @@
import { FileIcon, Icon, IconButton, Logo, Tooltip } from "@/ui"
import { Tabs } from "@/ui/tabs"
import { Select } from "@/components/select"
import FileTree from "@/components/file-tree"
import { For, Match, onCleanup, onMount, Show, Switch } from "solid-js"
import { useLocal, useSDK } from "@/context"
import { Code } from "@/components/code"
import {
DragDropProvider,
DragDropSensors,
DragOverlay,
SortableProvider,
createSortable,
closestCenter,
useDragDropContext,
} from "@thisbeyond/solid-dnd"
import type { DragEvent, Transformer } from "@thisbeyond/solid-dnd"
import type { LocalFile } from "@/context/local"
import SessionList from "@/components/session-list"
import SessionTimeline from "@/components/session-timeline"
import { createStore } from "solid-js/store"
export default function Page() {
const sdk = useSDK()
const local = useLocal()
const [store, setStore] = createStore({
clickTimer: undefined as number | undefined,
activeItem: undefined as string | undefined,
prompt: "",
dragging: undefined as "left" | "right" | undefined,
})
let inputRef: HTMLInputElement | undefined = undefined
const MOD = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform) ? "Meta" : "Control"
onMount(() => {
document.addEventListener("keydown", handleKeyDown)
})
onCleanup(() => {
document.removeEventListener("keydown", handleKeyDown)
})
const handleKeyDown = (e: KeyboardEvent) => {
const inputFocused = document.activeElement === inputRef
if (inputFocused) {
if (e.key === "Escape") {
inputRef?.blur()
}
return
}
if (document.activeElement?.id === "select-filter") {
return
}
if (local.file.active()) {
if (e.getModifierState(MOD)) {
if (e.key.toLowerCase() === "a") {
return
}
if (e.key.toLowerCase() === "c") {
return
}
}
}
if (e.key.length === 1 && e.key !== "Unidentified") {
inputRef?.focus()
}
}
const navigateChange = (dir: 1 | -1) => {
const active = local.file.active()
if (!active) return
const current = local.file.changeIndex(active.path)
const next = current == undefined ? (dir === 1 ? 0 : -1) : current + dir
local.file.setChangeIndex(active.path, next)
}
const resetClickTimer = () => {
if (!store.clickTimer) return
clearTimeout(store.clickTimer)
setStore("clickTimer", undefined)
}
const startClickTimer = () => {
const newClickTimer = setTimeout(() => {
setStore("clickTimer", undefined)
}, 300)
setStore("clickTimer", newClickTimer as unknown as number)
}
const handleFileClick = async (file: LocalFile) => {
if (store.clickTimer) {
resetClickTimer()
local.file.update(file.path, { ...file, pinned: true })
} else {
local.file.open(file.path)
startClickTimer()
}
}
const handleTabChange = (path: string) => {
local.file.open(path)
}
const handleTabClose = (file: LocalFile) => {
local.file.close(file.path)
}
const onDragStart = (event: any) => {
setStore("activeItem", event.draggable.id as string)
}
const onDragOver = (event: DragEvent) => {
const { draggable, droppable } = event
if (draggable && droppable) {
const currentFiles = local.file.opened().map((f) => f.path)
const fromIndex = currentFiles.indexOf(draggable.id.toString())
const toIndex = currentFiles.indexOf(droppable.id.toString())
if (fromIndex !== toIndex) {
local.file.move(draggable.id.toString(), toIndex)
}
}
}
const onDragEnd = () => {
setStore("activeItem", undefined)
}
const handleLeftDragStart = (e: MouseEvent) => {
e.preventDefault()
setStore("dragging", "left")
const startX = e.clientX
const startWidth = local.layout.leftWidth()
const handleMouseMove = (e: MouseEvent) => {
const deltaX = e.clientX - startX
const newWidth = startWidth + deltaX
local.layout.setLeftWidth(newWidth)
}
const handleMouseUp = () => {
setStore("dragging", undefined)
document.removeEventListener("mousemove", handleMouseMove)
document.removeEventListener("mouseup", handleMouseUp)
}
document.addEventListener("mousemove", handleMouseMove)
document.addEventListener("mouseup", handleMouseUp)
}
const handleRightDragStart = (e: MouseEvent) => {
e.preventDefault()
setStore("dragging", "right")
const startX = e.clientX
const startWidth = local.layout.rightWidth()
const handleMouseMove = (e: MouseEvent) => {
const deltaX = startX - e.clientX
const newWidth = startWidth + deltaX
local.layout.setRightWidth(newWidth)
}
const handleMouseUp = () => {
setStore("dragging", undefined)
document.removeEventListener("mousemove", handleMouseMove)
document.removeEventListener("mouseup", handleMouseUp)
}
document.addEventListener("mousemove", handleMouseMove)
document.addEventListener("mouseup", handleMouseUp)
}
const handleSubmit = async (e: SubmitEvent) => {
e.preventDefault()
const prompt = store.prompt
setStore("prompt", "")
inputRef?.blur()
const session =
(local.layout.rightPane() ? local.session.active() : undefined) ??
(await sdk.session.create().then((x) => x.data!))
local.session.setActive(session!.id)
local.layout.openRightPane()
const response = await sdk.session.prompt({
path: { id: session!.id },
body: {
agent: local.agent.current()!.name,
model: local.model.current(),
parts: [
{
type: "text",
text: prompt,
},
...local.file
.opened()
.filter((f) => f.selection || local.file.active()?.path === f.path)
.flatMap((f) => [
{
type: "file" as const,
mime: "text/plain",
url: `file://${f.absolute}${f.selection ? `?start=${f.selection.startLine}&end=${f.selection.endLine}` : ""}`,
filename: f.name,
source: {
type: "file" as const,
text: {
value: "@" + f.name,
start: 0, // f.start,
end: 0, // f.end,
},
path: f.absolute,
},
},
]),
],
},
})
console.log("response", response)
}
return (
<div class="relative">
<div
class="fixed top-0 left-0 h-full border-r border-border-subtle/30 flex flex-col overflow-hidden"
style={`width: ${local.layout.leftWidth()}px`}
>
<Tabs class="relative flex flex-col h-full" defaultValue="files">
<div class="sticky top-0 shrink-0 flex">
<Tabs.List class="grow w-full after:hidden">
<Tabs.Trigger value="files" class="flex-1 justify-center text-xs">
Files
</Tabs.Trigger>
<Tabs.Trigger value="changes" class="flex-1 justify-center text-xs">
Changes
</Tabs.Trigger>
</Tabs.List>
</div>
<Tabs.Content value="files" class="grow min-h-0 py-2 bg-background">
<FileTree path="" onFileClick={handleFileClick} />
</Tabs.Content>
<Tabs.Content value="changes" class="grow min-h-0 py-2 bg-background">
<div class="px-2 text-xs text-text-muted">No changes yet</div>
</Tabs.Content>
</Tabs>
</div>
<div
class="fixed top-0 h-full w-1.5 bg-transparent cursor-col-resize z-50 group"
style={`left: ${local.layout.leftWidth()}px`}
onMouseDown={(e) => handleLeftDragStart(e)}
>
<div
classList={{
"w-0.5 h-full bg-transparent group-hover:bg-border-active transition-colors": true,
"bg-border-active!": store.dragging === "left",
}}
/>
</div>
<Show when={local.layout.rightPane()}>
<div
class="fixed top-0 right-0 h-full border-l border-border-subtle/30 flex flex-col overflow-hidden"
style={`width: ${local.layout.rightWidth()}px`}
>
<div class="relative flex-1 min-h-0 overflow-y-auto no-scrollbar">
<Show when={local.session.active()} fallback={<SessionList />}>
{(activeSession) => (
<div class="relative">
<div class="sticky top-0 bg-background z-50 px-2 h-8 border-b border-border-subtle/30">
<div class="h-full flex items-center gap-2">
<IconButton
size="xs"
variant="ghost"
onClick={() => local.session.clearActive()}
class="text-text-muted hover:text-text"
>
<Icon name="arrow-left" size={14} />
</IconButton>
<h2 class="text-sm font-medium text-text truncate">
{activeSession().title || "Untitled Session"}
</h2>
</div>
</div>
<SessionTimeline session={activeSession().id} />
</div>
)}
</Show>
</div>
</div>
<div
class="fixed top-0 h-full w-1.5 bg-transparent cursor-col-resize z-50 group flex justify-end"
style={`right: ${local.layout.rightWidth()}px`}
onMouseDown={(e) => handleRightDragStart(e)}
>
<div
classList={{
"w-0.5 h-full bg-transparent group-hover:bg-border-active transition-colors": true,
"bg-border-active!": store.dragging === "right",
}}
/>
</div>
</Show>
<div
class="relative"
style={`margin-left: ${local.layout.leftWidth()}px; margin-right: ${local.layout.rightPane() ? local.layout.rightWidth() : 0}px`}
>
<Logo
size={64}
variant="ornate"
class="absolute top-2/5 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
/>
<DragDropProvider
onDragStart={onDragStart}
onDragEnd={onDragEnd}
onDragOver={onDragOver}
collisionDetector={closestCenter}
>
<DragDropSensors />
<ConstrainDragYAxis />
<Tabs
class="relative grow w-full flex flex-col h-screen"
value={local.file.active()?.path}
onChange={handleTabChange}
>
<div class="sticky top-0 shrink-0 flex">
<Tabs.List class="grow">
<SortableProvider ids={local.file.opened().map((f) => f.path)}>
<For each={local.file.opened()}>
{(file) => <SortableTab file={file} onTabClick={handleFileClick} onTabClose={handleTabClose} />}
</For>
</SortableProvider>
</Tabs.List>
<div class="shrink-0 h-full flex items-center gap-1 px-2 border-b border-border-subtle/40">
<Show when={local.file.active() && local.file.active()!.content?.diff}>
{(() => {
const f = local.file.active()!
const view = local.file.view(f.path)
return (
<div class="flex items-center gap-1">
<Show when={view !== "raw"}>
<div class="mr-1 flex items-center gap-1">
<Tooltip value="Previous change" placement="bottom">
<IconButton size="xs" variant="ghost" onClick={() => navigateChange(-1)}>
<Icon name="arrow-up" size={14} />
</IconButton>
</Tooltip>
<Tooltip value="Next change" placement="bottom">
<IconButton size="xs" variant="ghost" onClick={() => navigateChange(1)}>
<Icon name="arrow-down" size={14} />
</IconButton>
</Tooltip>
</div>
</Show>
<Tooltip value="Raw" placement="bottom">
<IconButton
size="xs"
variant="ghost"
classList={{
"text-text": view === "raw",
"text-text-muted/70": view !== "raw",
"bg-background-element": view === "raw",
}}
onClick={() => local.file.setView(f.path, "raw")}
>
<Icon name="file-text" size={14} />
</IconButton>
</Tooltip>
<Tooltip value="Unified diff" placement="bottom">
<IconButton
size="xs"
variant="ghost"
classList={{
"text-text": view === "diff-unified",
"text-text-muted/70": view !== "diff-unified",
"bg-background-element": view === "diff-unified",
}}
onClick={() => local.file.setView(f.path, "diff-unified")}
>
<Icon name="checklist" size={14} />
</IconButton>
</Tooltip>
<Tooltip value="Split diff" placement="bottom">
<IconButton
size="xs"
variant="ghost"
classList={{
"text-text": view === "diff-split",
"text-text-muted/70": view !== "diff-split",
"bg-background-element": view === "diff-split",
}}
onClick={() => local.file.setView(f.path, "diff-split")}
>
<Icon name="columns" size={14} />
</IconButton>
</Tooltip>
</div>
)
})()}
</Show>
<Tooltip value={local.layout.rightPane() ? "Close pane" : "Open pane"} placement="bottom">
<IconButton size="xs" variant="ghost" onClick={() => local.layout.toggleRightPane()}>
<Icon name={local.layout.rightPane() ? "close-pane" : "open-pane"} size={14} />
</IconButton>
</Tooltip>
</div>
</div>
<For each={local.file.opened()}>
{(file) => (
<Tabs.Content value={file.path} class="grow h-full pt-1 select-text">
{(() => {
const view = local.file.view(file.path)
const showRaw = view === "raw" || !file.content?.diff
const code = showRaw ? (file.content?.content ?? "") : (file.content?.diff ?? "")
return <Code path={file.path} code={code} />
})()}
</Tabs.Content>
)}
</For>
</Tabs>
<DragOverlay>
{store.activeItem &&
(() => {
const draggedFile = local.file.node(store.activeItem!)
return (
<div
class="relative px-3 h-8 flex items-center
text-sm font-medium text-text whitespace-nowrap
shrink-0 bg-background-panel
border-x border-border-subtle/40 border-b border-b-transparent"
>
<TabVisual file={draggedFile} />
</div>
)
})()}
</DragOverlay>
</DragDropProvider>
<form
onSubmit={handleSubmit}
class="peer/editor absolute inset-x-4 z-50 flex items-center justify-center"
classList={{
"bottom-8": !!local.file.active(),
"bottom-2/5": local.file.active() === undefined,
}}
>
<div
class="w-full max-w-xl min-w-0 p-2 mx-auto rounded-lg isolate backdrop-blur-xs
flex flex-col gap-1
bg-gradient-to-b from-background-panel/90 to-background/90
ring-1 ring-border-active/50 border border-transparent
shadow-[0_0_33px_rgba(0,0,0,0.8)]
focus-within:ring-2 focus-within:ring-primary/40 focus-within:border-primary"
>
<div class="flex flex-wrap gap-1">
<Show when={local.file.active()}>
<FileTag
default
file={local.file.active()!}
onClose={() => local.file.close(local.file.active()?.path ?? "")}
/>
</Show>
<For each={local.file.opened().filter((x) => x.selection)}>
{(file) => <FileTag file={file} onClose={() => local.file.select(file.path, undefined)} />}
</For>
</div>
<input
ref={(el) => (inputRef = el)}
type="text"
value={store.prompt}
onInput={(e) => setStore("prompt", e.currentTarget.value)}
placeholder="It all starts with a prompt..."
class="w-full p-1 pb-4 text-text font-light placeholder-text-muted/70 text-sm focus:outline-none"
/>
<div class="flex justify-between items-center text-xs text-text-muted">
<div class="flex gap-2 items-center">
<Select
options={local.agent.list().map((a) => a.name)}
current={local.agent.current().name}
onSelect={local.agent.set}
size="sm"
class="uppercase"
/>
<Select
options={local.model.list()}
current={local.model.current()}
onSelect={local.model.set}
label={(x) => x.modelID}
value={(x) => `${x.providerID}.${x.modelID}`}
filter={{
keys: ["providerID", "modelID"],
placeholder: "Filter models",
}}
groupBy={(x) => x.providerID}
size="sm"
class="uppercase"
/>
<span class="text-text-muted/70">{local.model.parsed().provider}</span>
</div>
<div class="flex gap-1 items-center">
<IconButton class="text-text-muted" size="xs" variant="ghost">
<Icon name="photo" size={16} />
</IconButton>
<IconButton class="text-background-panel! bg-primary rounded-full!" size="xs" variant="ghost">
<Icon name="arrow-up" size={14} />
</IconButton>
</div>
</div>
</div>
</form>
</div>
</div>
)
}
const TabVisual = (props: { file: LocalFile }) => {
const local = useLocal()
return (
<div class="flex items-center gap-x-1.5">
<FileIcon node={props.file} class="" />
<span
classList={{ "text-xs": true, "text-primary": local.file.changed(props.file.path), italic: !props.file.pinned }}
>
{props.file.name}
</span>
<span class="text-xs opacity-70">
<Switch>
<Match when={local.file.status(props.file.path)?.status === "modified"}>
<span class="text-primary">M</span>
</Match>
<Match when={local.file.status(props.file.path)?.status === "added"}>
<span class="text-success">A</span>
</Match>
<Match when={local.file.status(props.file.path)?.status === "deleted"}>
<span class="text-error">D</span>
</Match>
</Switch>
</span>
</div>
)
}
const SortableTab = (props: {
file: LocalFile
onTabClick: (file: LocalFile) => void
onTabClose: (file: LocalFile) => void
}) => {
const sortable = createSortable(props.file.path)
return (
// @ts-ignore
<div use:sortable classList={{ "opacity-0": sortable.isActiveDraggable }}>
<Tooltip value={props.file.path} placement="bottom">
<div class="relative">
<Tabs.Trigger value={props.file.path} class="peer/tab pr-7" onClick={() => props.onTabClick(props.file)}>
<TabVisual file={props.file} />
</Tabs.Trigger>
<IconButton
class="absolute right-1 top-1.5 opacity-0 text-text-muted/60
peer-data-[selected]/tab:opacity-100 peer-data-[selected]/tab:text-text
peer-data-[selected]/tab:hover:bg-border-subtle
hover:opacity-100 peer-hover/tab:opacity-100"
size="xs"
variant="ghost"
onClick={() => props.onTabClose(props.file)}
>
<Icon name="close" size={16} />
</IconButton>
</div>
</Tooltip>
</div>
)
}
const FileTag = (props: { file: LocalFile; default?: boolean; onClose: () => void }) => (
<div
class="flex items-center bg-background group/tag
border border-border-subtle/60 border-dashed
rounded-md text-xs text-text-muted"
>
<IconButton class="text-text-muted" size="xs" variant="ghost" onClick={props.onClose}>
<Switch fallback={<FileIcon node={props.file} class="group-hover/tag:hidden size-3!" />}>
<Match when={props.default}>
<Icon name="file" class="group-hover/tag:hidden" size={12} />
</Match>
</Switch>
<Icon name="close" class="hidden group-hover/tag:block" size={12} />
</IconButton>
<div class="pr-1 flex gap-1 items-center">
<span>{props.file.name}</span>
<Show when={!props.default && props.file.selection}>
<span class="">
({props.file.selection!.startLine}-{props.file.selection!.endLine})
</span>
</Show>
</div>
</div>
)
const ConstrainDragYAxis = () => {
const context = useDragDropContext()
if (!context) return <></>
const [, { onDragStart, onDragEnd, addTransformer, removeTransformer }] = context
const transformer: Transformer = {
id: "constrain-y-axis",
order: 100,
callback: (transform) => ({ ...transform, y: 0 }),
}
onDragStart((event: any) => {
addTransformer("draggables", event.draggable.id, transformer)
})
onDragEnd((event: any) => {
removeTransformer("draggables", event.draggable.id, transformer.id)
})
return <></>
}
-5
View File
@@ -1,5 +0,0 @@
import { type ParentProps } from "solid-js"
export default function Layout(props: ParentProps) {
return <main class="">{props.children}</main>
}
-49
View File
@@ -1,49 +0,0 @@
import { Button as KobalteButton } from "@kobalte/core/button"
import { splitProps } from "solid-js"
import type { ComponentProps } from "solid-js"
export interface ButtonProps extends ComponentProps<typeof KobalteButton> {
variant?: "primary" | "secondary" | "outline" | "ghost"
size?: "sm" | "md" | "lg"
}
export const buttonStyles = {
base: "inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 cursor-pointer",
variants: {
primary: "bg-primary text-background hover:bg-secondary focus-visible:ring-primary data-[disabled]:opacity-50",
secondary:
"bg-background-panel text-text hover:bg-background-element focus-visible:ring-secondary data-[disabled]:opacity-50",
outline:
"border border-border bg-transparent text-text hover:bg-background-panel focus-visible:ring-border-active data-[disabled]:border-border-subtle data-[disabled]:text-text-muted",
ghost: "text-text hover:bg-background-panel focus-visible:ring-border-active data-[disabled]:text-text-muted",
},
sizes: {
sm: "h-8 px-3 text-sm",
md: "h-10 px-4 text-sm",
lg: "h-12 px-6 text-base",
},
}
export function getButtonClasses(
variant: keyof typeof buttonStyles.variants = "primary",
size: keyof typeof buttonStyles.sizes = "md",
className?: string,
) {
return `${buttonStyles.base} ${buttonStyles.variants[variant]} ${buttonStyles.sizes[size]}${className ? ` ${className}` : ""}`
}
export function Button(props: ButtonProps) {
const [local, others] = splitProps(props, ["variant", "size", "class", "classList"])
return (
<KobalteButton
classList={{
...(local.classList ?? {}),
[buttonStyles.base]: true,
[buttonStyles.variants[local.variant || "primary"]]: true,
[buttonStyles.sizes[local.size || "md"]]: true,
[local.class ?? ""]: !!local.class,
}}
{...others}
/>
)
}
-38
View File
@@ -1,38 +0,0 @@
import { Button as KobalteButton } from "@kobalte/core/button"
import { splitProps } from "solid-js"
import type { ComponentProps, JSX } from "solid-js"
export interface IconButtonProps extends ComponentProps<typeof KobalteButton> {
variant?: "primary" | "secondary" | "outline" | "ghost"
size?: "xs" | "sm" | "md" | "lg"
children: JSX.Element
}
export function IconButton(props: IconButtonProps) {
const [local, others] = splitProps(props, ["variant", "size", "class", "classList"])
return (
<KobalteButton
classList={{
...(local.classList || {}),
"inline-flex items-center justify-center rounded-md font-medium cursor-pointer": true,
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2": true,
"disabled:pointer-events-none disabled:opacity-50": true,
"bg-primary text-background hover:bg-secondary focus-visible:ring-primary data-[disabled]:opacity-50":
(local.variant || "primary") === "primary",
"bg-background-panel text-text hover:bg-background-element focus-visible:ring-secondary data-[disabled]:opacity-50":
local.variant === "secondary",
"border border-border bg-transparent text-text hover:bg-background-panel": local.variant === "outline",
"focus-visible:ring-border-active data-[disabled]:border-border-subtle data-[disabled]:text-text-muted":
local.variant === "outline",
"text-text hover:bg-background-panel focus-visible:ring-border-active data-[disabled]:text-text-muted":
local.variant === "ghost",
"h-5 w-5 text-xs": local.size === "xs",
"h-8 w-8 text-sm": local.size === "sm",
"h-10 w-10 text-sm": (local.size || "md") === "md",
"h-12 w-12 text-base": local.size === "lg",
[local.class ?? ""]: !!local.class,
}}
{...others}
/>
)
}
-13
View File
@@ -1,13 +0,0 @@
export { Button, type ButtonProps } from "./button"
export {
Collapsible,
type CollapsibleProps,
type CollapsibleTriggerProps,
type CollapsibleContentProps,
} from "./collapsible"
export { FileIcon, type FileIconProps } from "./file-icon"
export { Icon, type IconProps } from "./icon"
export { IconButton, type IconButtonProps } from "./icon-button"
export { Link, type LinkProps } from "./link"
export { Logo, type LogoProps } from "./logo"
export { Tooltip, type TooltipProps } from "./tooltip"
-15
View File
@@ -1,15 +0,0 @@
import { A } from "@solidjs/router"
import { splitProps } from "solid-js"
import type { ComponentProps } from "solid-js"
import { getButtonClasses } from "./button"
export interface LinkProps extends ComponentProps<typeof A> {
variant?: "primary" | "secondary" | "outline" | "ghost"
size?: "sm" | "md" | "lg"
}
export function Link(props: LinkProps) {
const [local, others] = splitProps(props, ["variant", "size", "class"])
const classes = local.variant ? getButtonClasses(local.variant, local.size, local.class) : local.class
return <A class={classes} {...others} />
}
-125
View File
@@ -1,125 +0,0 @@
import type { ComponentProps } from "solid-js"
export interface LogoProps extends ComponentProps<"svg"> {
variant?: "mark" | "full" | "ornate"
size?: number
}
export function Logo(props: LogoProps) {
const { variant = "mark", size = 64, ...others } = props
if (variant === "mark") {
return (
<svg
width={size}
height={size * (42 / 64)}
viewBox="0 0 64 42"
fill="none"
xmlns="http://www.w3.org/2000/svg"
class={`text-text ${props.class ?? ""}`}
{...others}
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M0 0H32V41.5955H0V0ZM24 8.5H8V33H24V8.5Z"
fill="currentColor"
/>
<path d="M40 0H64V8.5H48V33H64V41.5H40V0Z" fill="currentColor" />
</svg>
)
}
if (variant === "full") {
return (
<svg
width={size * (289 / 42)}
height={size}
viewBox="0 0 289 42"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...others}
>
<path d="M264.5 0H288.5V8.5H272.5V16.5H288.5V25H272.5V33H288.5V41.5H264.5V0Z" fill="currentColor" />
<path d="M248.5 0H224.5V41.5H248.5V33H232.5V8.5H248.5V0Z" fill="currentColor" />
<path d="M256.5 8.5H248.5V33H256.5V8.5Z" fill="currentColor" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M184.5 0H216.5V41.5H184.5V0ZM208.5 8.5H192.5V33H208.5V8.5Z"
fill="currentColor"
/>
<path d="M144.5 8.5H136.5V41.5H144.5V8.5Z" fill="currentColor" />
<path d="M136.5 0H112.5V41.5H120.5V8.5H136.5V0Z" fill="currentColor" />
<path d="M80.5 0H104.5V8.5H88.5V16.5H104.5V25H88.5V33H104.5V41.5H80.5V0Z" fill="currentColor" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M40.5 0H72.5V41.5H48.5V49.5H40.5V0ZM64.5 8.5H48.5V33H64.5V8.5Z"
fill="currentColor"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M0.5 0H32.5V41.5955H0.5V0ZM24.5 8.5H8.5V33H24.5V8.5Z"
fill="currentColor"
/>
<path d="M152.5 0H176.5V8.5H160.5V33H176.5V41.5H152.5V0Z" fill="currentColor" />
</svg>
)
}
return (
<svg
width={size * (289 / 42)}
height={size}
viewBox="0 0 289 50"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...others}
>
<path d="M8.5 16.5H24.5V33H8.5V16.5Z" fill="currentColor" fill-opacity="0.2" />
<path d="M48.5 16.5H64.5V33H48.5V16.5Z" fill="currentColor" fill-opacity="0.2" />
<path d="M120.5 16.5H136.5V33H120.5V16.5Z" fill="currentColor" fill-opacity="0.2" />
<path d="M160.5 16.5H176.5V33H160.5V16.5Z" fill="currentColor" fill-opacity="0.2" />
<path d="M192.5 16.5H208.5V33H192.5V16.5Z" fill="currentColor" fill-opacity="0.2" />
<path d="M232.5 16.5H248.5V33H232.5V16.5Z" fill="currentColor" fill-opacity="0.2" />
<path
d="M264.5 0H288.5V8.5H272.5V16.5H288.5V25H272.5V33H288.5V41.5H264.5V0Z"
fill="currentColor"
fill-opacity="0.95"
/>
<path d="M248.5 0H224.5V41.5H248.5V33H232.5V8.5H248.5V0Z" fill="currentColor" fill-opacity="0.95" />
<path d="M256.5 8.5H248.5V33H256.5V8.5Z" fill="currentColor" fill-opacity="0.95" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M184.5 0H216.5V41.5H184.5V0ZM208.5 8.5H192.5V33H208.5V8.5Z"
fill="currentColor"
fill-opacity="0.95"
/>
<path d="M144.5 8.5H136.5V41.5H144.5V8.5Z" fill="currentColor" fill-opacity="0.5" />
<path d="M136.5 0H112.5V41.5H120.5V8.5H136.5V0Z" fill="currentColor" fill-opacity="0.5" />
<path
d="M80.5 0H104.5V8.5H88.5V16.5H104.5V25H88.5V33H104.5V41.5H80.5V0Z"
fill="currentColor"
fill-opacity="0.5"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M40.5 0H72.5V41.5H48.5V49.5H40.5V0ZM64.5 8.5H48.5V33H64.5V8.5Z"
fill="currentColor"
fill-opacity="0.5"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M0.5 0H32.5V41.5955H0.5V0ZM24.5 8.5H8.5V33H24.5V8.5Z"
fill="currentColor"
fill-opacity="0.5"
/>
<path d="M152.5 0H176.5V8.5H160.5V33H176.5V41.5H152.5V0Z" fill="currentColor" fill-opacity="0.95" />
</svg>
)
}
-71
View File
@@ -1,71 +0,0 @@
import { Tabs as KobalteTabs } from "@kobalte/core/tabs"
import { splitProps } from "solid-js"
import type { ComponentProps, ParentProps } from "solid-js"
export interface TabsProps extends ComponentProps<typeof KobalteTabs> {}
export interface TabsListProps extends ComponentProps<typeof KobalteTabs.List> {}
export interface TabsTriggerProps extends ComponentProps<typeof KobalteTabs.Trigger> {}
export interface TabsContentProps extends ComponentProps<typeof KobalteTabs.Content> {}
function TabsRoot(props: TabsProps) {
return <KobalteTabs {...props} />
}
function TabsList(props: TabsListProps) {
const [local, others] = splitProps(props, ["class"])
return (
<KobalteTabs.List
classList={{
"relative flex items-center bg-background overflow-x-auto no-scrollbar": true,
"divide-x divide-border-subtle/40": true,
"after:content-[''] after:block after:grow after:h-8": true,
"after:border-l empty:after:border-l-0! after:border-b after:border-border-subtle/40": true,
[local.class ?? ""]: !!local.class,
}}
{...others}
/>
)
}
function TabsTrigger(props: ParentProps<TabsTriggerProps>) {
const [local, others] = splitProps(props, ["class", "children"])
return (
<KobalteTabs.Trigger
classList={{
"relative px-3 h-8 flex items-center": true,
"text-sm font-medium text-text-muted/60 cursor-pointer": true,
"whitespace-nowrap shrink-0 border-b border-border-subtle/40": true,
"disabled:pointer-events-none disabled:opacity-50": true,
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring": true,
"data-[selected]:text-text data-[selected]:bg-background-panel": true,
"data-[selected]:!border-b-transparent": true,
[local.class ?? ""]: !!local.class,
}}
{...others}
>
{local.children}
</KobalteTabs.Trigger>
)
}
function TabsContent(props: ParentProps<TabsContentProps>) {
const [local, others] = splitProps(props, ["class", "children"])
return (
<KobalteTabs.Content
classList={{
"bg-background-panel overflow-y-auto h-full no-scrollbar": true,
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring": true,
[local.class ?? ""]: !!local.class,
}}
{...others}
>
{local.children}
</KobalteTabs.Content>
)
}
export const Tabs = Object.assign(TabsRoot, {
List: TabsList,
Trigger: TabsTrigger,
Content: TabsContent,
})
-9
View File
@@ -1,9 +0,0 @@
export function getFilename(path: string) {
const parts = path.split("/")
return parts[parts.length - 1]
}
export function getFileExtension(path: string) {
const parts = path.split(".")
return parts[parts.length - 1]
}
-14
View File
@@ -1,14 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@tsconfig/bun/tsconfig.json",
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "solid-js",
"types": ["vite/client"],
"lib": ["DOM", "DOM.Iterable"],
"customConditions": ["development"],
"paths": {
"@/*": ["./src/*"]
}
}
}
+13 -5
View File
@@ -1,18 +1,22 @@
{
"name": "@opencode/console-app",
"name": "@opencode-ai/console-app",
"type": "module",
"scripts": {
"typecheck": "tsc --noEmit",
"typecheck": "tsgo --noEmit",
"dev": "vinxi dev --host 0.0.0.0",
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
"build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
"start": "vinxi start",
"version": "0.10.3"
"version": "1.0.23"
},
"dependencies": {
"@ibm/plex": "6.4.1",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@opencode/console-core": "workspace:*",
"@opencode-ai/console-core": "workspace:*",
"@opencode-ai/console-mail": "workspace:*",
"@openauthjs/openauth": "catalog:",
"@kobalte/core": "catalog:",
"@jsx-email/render": "1.1.1",
"@opencode-ai/console-resource": "workspace:*",
"@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.15.0",
"@solidjs/start": "^1.1.0",
@@ -20,6 +24,10 @@
"vinxi": "^0.5.7",
"zod": "catalog:"
},
"devDependencies": {
"typescript": "catalog:",
"@typescript/native-preview": "catalog:"
},
"engines": {
"node": ">=22"
}
+1
View File
@@ -0,0 +1 @@
../../mail/emails/templates/static
@@ -0,0 +1,23 @@
<svg width="400" height="400" viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="400" height="400" fill="#FDFCFC"/>
<path d="M96 122.001V70.001H148V122.001H96Z" fill="#17181C"/>
<path d="M148.004 122.001V70.001H200.004V122.001H148.004Z" fill="#17181C"/>
<path d="M200.008 122.001V70.001H252.008V122.001H200.008Z" fill="#17181C"/>
<path d="M251.996 122.001V70.001H303.996V122.001H251.996Z" fill="#17181C"/>
<path d="M251.996 173.988V121.988H303.996V173.988H251.996Z" fill="#17181C"/>
<path d="M96 225.998V173.998H148V225.998H96Z" fill="#CFCECD"/>
<rect width="52" height="52" transform="translate(148.004 173.998)" fill="#17181C"/>
<path d="M148.004 225.998V173.998H200.004V225.998H148.004Z" fill="#17181C" fill-opacity="0.1"/>
<path d="M200.008 225.998V173.998H252.008V225.998H200.008Z" fill="#17181C"/>
<path d="M252.016 225.998V173.998H304.016V225.998H252.016Z" fill="#CFCECD"/>
<rect width="52" height="52" transform="translate(96 226.002)" fill="#17181C"/>
<path d="M96 278.002V226.002H148V278.002H96Z" fill="#17181C" fill-opacity="0.1"/>
<rect width="52" height="52" transform="translate(148.004 226.002)" fill="white"/>
<path d="M148.004 278.002V226.002H200.004V278.002H148.004Z" fill="#CFCECD"/>
<path d="M200.008 278.002V226.002H252.008V278.002H200.008Z" fill="#CFCECD"/>
<path d="M252.016 278.002V226.002H304.016V278.002H252.016Z" fill="#CFCECD"/>
<path d="M96 330.012V278.012H148V330.012H96Z" fill="#17181C"/>
<path d="M148.004 330.012V278.012H200.004V330.012H148.004Z" fill="#17181C"/>
<path d="M200.008 329.99V277.99H252.008V329.99H200.008Z" fill="#17181C"/>
<path d="M251.996 330.012V278.012H303.996V330.012H251.996Z" fill="#17181C"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+3 -4
View File
@@ -1,5 +1,4 @@
<svg width="600" height="600" viewBox="0 0 600 600" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="600" height="600" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M115 180H300V420H115V180ZM253.75 229.044H161.25V370.405H253.75V229.044Z" fill="white"/>
<path d="M346.25 180H485V229.044H392.5V370.405H485V419.449H346.25V180Z" fill="white"/>
<svg width="400" height="400" viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="400" height="400" fill="#0E0E0E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M312 340H88V60H312V340ZM256 116H144V284H256V116Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -0,0 +1,16 @@
<svg width="240" height="300" viewBox="0 0 240 300" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1401_86283)">
<mask id="mask0_1401_86283" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="240" height="300">
<path d="M240 0H0V300H240V0Z" fill="white"/>
</mask>
<g mask="url(#mask0_1401_86283)">
<path d="M180 240H60V120H180V240Z" fill="#4B4646"/>
<path d="M180 60H60V240H180V60ZM240 300H0V0H240V300Z" fill="#F1ECEC"/>
</g>
</g>
<defs>
<clipPath id="clip0_1401_86283">
<rect width="240" height="300" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -0,0 +1,16 @@
<svg width="240" height="300" viewBox="0 0 240 300" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1401_86274)">
<mask id="mask0_1401_86274" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="240" height="300">
<path d="M240 0H0V300H240V0Z" fill="white"/>
</mask>
<g mask="url(#mask0_1401_86274)">
<path d="M180 240H60V120H180V240Z" fill="#CFCECD"/>
<path d="M180 60H60V240H180V60ZM240 300H0V0H240V300Z" fill="#211E1E"/>
</g>
</g>
<defs>
<clipPath id="clip0_1401_86274">
<rect width="240" height="300" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

@@ -0,0 +1,30 @@
<svg width="641" height="115" viewBox="0 0 641 115" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1401_86292)">
<mask id="mask0_1401_86292" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="641" height="115">
<path d="M640.714 0H0V115H640.714V0Z" fill="white"/>
</mask>
<g mask="url(#mask0_1401_86292)">
<path d="M49.2868 82.1433H16.4297V49.2861H49.2868V82.1433Z" fill="#4B4646"/>
<path d="M49.2857 32.8573H16.4286V82.143H49.2857V32.8573ZM65.7143 98.5716H0V16.4287H65.7143V98.5716Z" fill="#B7B1B1"/>
<path d="M131.427 82.1433H98.5703V49.2861H131.427V82.1433Z" fill="#4B4646"/>
<path d="M98.5692 82.143H131.426V32.8573H98.5692V82.143ZM147.855 98.5716H98.5692V115H82.1406V16.4287H147.855V98.5716Z" fill="#B7B1B1"/>
<path d="M229.997 65.7139V82.1424H180.711V65.7139H229.997Z" fill="#4B4646"/>
<path d="M230.003 65.7144H180.718V82.143H230.003V98.5716H164.289V16.4287H230.003V65.7144ZM180.718 49.2859H213.575V32.8573H180.718V49.2859Z" fill="#B7B1B1"/>
<path d="M295.717 98.5718H262.859V49.2861H295.717V98.5718Z" fill="#4B4646"/>
<path d="M295.715 32.8573H262.858V98.5716H246.43V16.4287H295.715V32.8573ZM312.144 98.5716H295.715V32.8573H312.144V98.5716Z" fill="#B7B1B1"/>
<path d="M394.286 82.1433H345V49.2861H394.286V82.1433Z" fill="#4B4646"/>
<path d="M394.285 32.8573H344.999V82.143H394.285V98.5716H328.57V16.4287H394.285V32.8573Z" fill="#F1ECEC"/>
<path d="M459.998 82.1433H427.141V49.2861H459.998V82.1433Z" fill="#4B4646"/>
<path d="M459.997 32.8573H427.14V82.143H459.997V32.8573ZM476.425 98.5716H410.711V16.4287H476.425V98.5716Z" fill="#F1ECEC"/>
<path d="M542.146 82.1433H509.289V49.2861H542.146V82.1433Z" fill="#4B4646"/>
<path d="M542.145 32.8571H509.288V82.1429H542.145V32.8571ZM558.574 98.5714H492.859V16.4286H542.145V0H558.574V98.5714Z" fill="#F1ECEC"/>
<path d="M640.715 65.7139V82.1424H591.43V65.7139H640.715Z" fill="#4B4646"/>
<path d="M591.429 32.8573V49.2859H624.286V32.8573H591.429ZM640.714 65.7144H591.429V82.143H640.714V98.5716H575V16.4287H640.714V65.7144Z" fill="#F1ECEC"/>
</g>
</g>
<defs>
<clipPath id="clip0_1401_86292">
<rect width="640.714" height="115" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -0,0 +1,30 @@
<svg width="640" height="115" viewBox="0 0 640 115" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1401_86330)">
<mask id="mask0_1401_86330" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="640" height="115">
<path d="M640 0H0V115H640V0Z" fill="white"/>
</mask>
<g mask="url(#mask0_1401_86330)">
<path d="M49.2346 82.1433H16.4141V49.2861H49.2346V82.1433Z" fill="#CFCECD"/>
<path d="M49.2308 32.8573H16.4103V82.143H49.2308V32.8573ZM65.641 98.5716H0V16.4287H65.641V98.5716Z" fill="#656363"/>
<path d="M131.281 82.1433H98.4609V49.2861H131.281V82.1433Z" fill="#CFCECD"/>
<path d="M98.4649 82.143H131.285V32.8573H98.4649V82.143ZM147.696 98.5716H98.4649V115H82.0547V16.4287H147.696V98.5716Z" fill="#656363"/>
<path d="M229.746 65.7139V82.1424H180.516V65.7139H229.746Z" fill="#CFCECD"/>
<path d="M229.743 65.7144H180.512V82.143H229.743V98.5716H164.102V16.4287H229.743V65.7144ZM180.512 49.2859H213.332V32.8573H180.512V49.2859Z" fill="#656363"/>
<path d="M295.383 98.5718H262.562V49.2861H295.383V98.5718Z" fill="#CFCECD"/>
<path d="M295.387 32.8573H262.567V98.5716H246.156V16.4287H295.387V32.8573ZM311.797 98.5716H295.387V32.8573H311.797V98.5716Z" fill="#656363"/>
<path d="M393.848 82.1433H344.617V49.2861H393.848V82.1433Z" fill="#CFCECD"/>
<path d="M393.844 32.8573H344.613V82.143H393.844V98.5716H328.203V16.4287H393.844V32.8573Z" fill="#211E1E"/>
<path d="M459.485 82.1433H426.664V49.2861H459.485V82.1433Z" fill="#CFCECD"/>
<path d="M459.489 32.8573H426.668V82.143H459.489V32.8573ZM475.899 98.5716H410.258V16.4287H475.899V98.5716Z" fill="#211E1E"/>
<path d="M541.539 82.1433H508.719V49.2861H541.539V82.1433Z" fill="#CFCECD"/>
<path d="M541.535 32.8571H508.715V82.1428H541.535V32.8571ZM557.946 98.5714H492.305V16.4286H541.535V0H557.946V98.5714Z" fill="#211E1E"/>
<path d="M639.996 65.7139V82.1424H590.766V65.7139H639.996Z" fill="#CFCECD"/>
<path d="M590.77 32.8573V49.2859H623.59V32.8573H590.77ZM640 65.7144H590.77V82.143H640V98.5716H574.359V16.4287H640V65.7144Z" fill="#211E1E"/>
</g>
</g>
<defs>
<clipPath id="clip0_1401_86330">
<rect width="640" height="115" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -0,0 +1,22 @@
<svg width="641" height="115" viewBox="0 0 641 115" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1401_86315)">
<mask id="mask0_1401_86315" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="641" height="115">
<path d="M640.714 0H0V115H640.714V0Z" fill="white"/>
</mask>
<g mask="url(#mask0_1401_86315)">
<path d="M49.2857 32.8573H16.4286V82.143H49.2857V32.8573ZM65.7143 98.5716H0V16.4287H65.7143V98.5716Z" fill="white"/>
<path d="M98.5692 82.143H131.426V32.8573H98.5692V82.143ZM147.855 98.5716H98.5692V115H82.1406V16.4287H147.855V98.5716Z" fill="white"/>
<path d="M230.003 65.7144H180.718V82.143H230.003V98.5716H164.289V16.4287H230.003V65.7144ZM180.718 49.2859H213.575V32.8573H180.718V49.2859Z" fill="white"/>
<path d="M295.715 32.8573H262.858V98.5716H246.43V16.4287H295.715V32.8573ZM312.144 98.5716H295.715V32.8573H312.144V98.5716Z" fill="white"/>
<path d="M394.285 32.8573H344.999V82.143H394.285V98.5716H328.57V16.4287H394.285V32.8573Z" fill="white"/>
<path d="M459.997 32.8573H427.14V82.143H459.997V32.8573ZM476.425 98.5716H410.711V16.4287H476.425V98.5716Z" fill="white"/>
<path d="M542.145 32.8571H509.288V82.1429H542.145V32.8571ZM558.574 98.5714H492.859V16.4286H542.145V0H558.574V98.5714Z" fill="white"/>
<path d="M591.429 32.8573V49.2859H624.286V32.8573H591.429ZM640.714 65.7144H591.429V82.143H640.714V98.5716H575V16.4287H640.714V65.7144Z" fill="white"/>
</g>
</g>
<defs>
<clipPath id="clip0_1401_86315">
<rect width="640.714" height="115" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -0,0 +1,22 @@
<svg width="640" height="115" viewBox="0 0 640 115" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1401_86353)">
<mask id="mask0_1401_86353" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="640" height="115">
<path d="M640 0H0V115H640V0Z" fill="white"/>
</mask>
<g mask="url(#mask0_1401_86353)">
<path d="M49.2308 32.8573H16.4103V82.143H49.2308V32.8573ZM65.641 98.5716H0V16.4287H65.641V98.5716Z" fill="black"/>
<path d="M98.4649 82.143H131.285V32.8573H98.4649V82.143ZM147.696 98.5716H98.4649V115H82.0547V16.4287H147.696V98.5716Z" fill="black"/>
<path d="M229.743 65.7144H180.512V82.143H229.743V98.5716H164.102V16.4287H229.743V65.7144ZM180.512 49.2859H213.332V32.8573H180.512V49.2859Z" fill="black"/>
<path d="M295.387 32.8573H262.567V98.5716H246.156V16.4287H295.387V32.8573ZM311.797 98.5716H295.387V32.8573H311.797V98.5716Z" fill="black"/>
<path d="M393.844 32.8573H344.613V82.143H393.844V98.5716H328.203V16.4287H393.844V32.8573Z" fill="black"/>
<path d="M459.489 32.8573H426.668V82.143H459.489V32.8573ZM475.899 98.5716H410.258V16.4287H475.899V98.5716Z" fill="black"/>
<path d="M541.535 32.8571H508.715V82.1428H541.535V32.8571ZM557.946 98.5714H492.305V16.4286H541.535V0H557.946V98.5714Z" fill="black"/>
<path d="M590.77 32.8573V49.2859H623.59V32.8573H590.77ZM640 65.7144H590.77V82.143H640V98.5716H574.359V16.4287H640V65.7144Z" fill="black"/>
</g>
</g>
<defs>
<clipPath id="clip0_1401_86353">
<rect width="640" height="115" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

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