diff --git a/.github/workflows/opencode-agents.yml b/.github/workflows/opencode-agents.yml
new file mode 100644
index 00000000..8280719c
--- /dev/null
+++ b/.github/workflows/opencode-agents.yml
@@ -0,0 +1,79 @@
+name: Opencode Agents
+
+on:
+ issues:
+ types: [opened]
+ pull_request_target:
+ types: [opened]
+
+jobs:
+ triage-issue:
+ if: github.event_name == 'issues'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ issues: write
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 1
+
+ - name: Install opencode
+ run: curl -fsSL https://opencode.ai/install | bash
+
+ - name: Triage issue
+ env:
+ OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ ISSUE_TITLE: ${{ github.event.issue.title }}
+ ISSUE_BODY: ${{ github.event.issue.body }}
+ run: |
+ opencode run --agent triage "The following issue was just opened, triage it:
+
+ Title: $ISSUE_TITLE
+
+ $ISSUE_BODY"
+
+ duplicate-prs:
+ if: github.event_name == 'pull_request_target' && github.event.pull_request.user.login != 'opencode-agent[bot]'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: write
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 1
+
+ - name: Install opencode
+ run: curl -fsSL https://opencode.ai/install | bash
+
+ - name: Build prompt
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ run: |
+ {
+ echo "Check for duplicate PRs related to this new PR:"
+ echo ""
+ echo "CURRENT_PR_NUMBER: $PR_NUMBER"
+ echo ""
+ echo "Title: $(gh pr view \"$PR_NUMBER\" --json title --jq .title)"
+ echo ""
+ echo "Description:"
+ gh pr view "$PR_NUMBER" --json body --jq .body
+ } > pr_info.txt
+
+ - name: Check for duplicate PRs
+ env:
+ OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ run: |
+ COMMENT=$(opencode run --agent duplicate-pr "$(cat pr_info.txt)")
+
+ gh pr comment "$PR_NUMBER" --body "_The following comment was made by an LLM, it may be inaccurate:_
+
+ $COMMENT"
diff --git a/.opencode/agent/css.md b/.opencode/agent/css.md
new file mode 100644
index 00000000..d5e68c7b
--- /dev/null
+++ b/.opencode/agent/css.md
@@ -0,0 +1,149 @@
+---
+description: use whenever you are styling a ui with css
+---
+
+you are very good at writing clean maintainable css using modern techniques
+
+css is structured like this
+
+```css
+[data-page="home"] {
+ [data-component="header"] {
+ [data-slot="logo"] {
+ }
+ }
+}
+```
+
+top level pages are scoped using `data-page`
+
+pages can break down into components using `data-component`
+
+components can break down into slots using `data-slot`
+
+structure things so that this hierarchy is followed IN YOUR CSS - you should rarely need to
+nest components inside other components. you should NEVER nest components inside
+slots. you should NEVER nest slots inside other slots.
+
+**IMPORTANT: This hierarchy rule applies to CSS structure, NOT JSX/DOM structure.**
+
+The hierarchy in css file does NOT have to match the hierarchy in the dom - you
+can put components or slots at the same level in CSS even if one goes inside another in the DOM.
+
+Your JSX can nest however makes semantic sense - components can be inside slots,
+slots can contain components, etc. The DOM structure should be whatever makes the most
+semantic and functional sense.
+
+It is more important to follow the pages -> components -> slots structure IN YOUR CSS,
+while keeping your JSX/DOM structure logical and semantic.
+
+use data attributes to represent different states of the component
+
+```css
+[data-component="modal"] {
+ opacity: 0;
+
+ &[data-state="open"] {
+ opacity: 1;
+ }
+}
+```
+
+this will allow jsx to control the styling
+
+avoid selectors that just target an element type like `> span` you should assign
+it a slot name. it's ok to do this sometimes where it makes sense semantically
+like targeting `li` elements in a list
+
+in terms of file structure `./src/style/` contains all universal styling rules.
+these should not contain anything specific to a page
+
+`./src/style/token` contains all the tokens used in the project
+
+`./src/style/component` is for reusable components like buttons or inputs
+
+page specific styles should go next to the page they are styling so
+`./src/routes/about.tsx` should have its styles in `./src/routes/about.css`
+
+`about.css` should be scoped using `data-page="about"`
+
+## Example of correct implementation
+
+JSX can nest however makes sense semantically:
+
+```jsx
+
+
Section Title
+
Content here
+
+```
+
+CSS maintains clean hierarchy regardless of DOM nesting:
+
+```css
+[data-page="home"] {
+ [data-component="screenshots"] {
+ [data-slot="left"] {
+ /* styles */
+ }
+ [data-slot="content"] {
+ /* styles */
+ }
+ }
+
+ [data-component="title"] {
+ /* can be at same level even though nested in DOM */
+ }
+}
+```
+
+## Reusable Components
+
+If a component is reused across multiple sections of the same page, define it at the page level:
+
+```jsx
+
+
+
+
+```
+
+```css
+[data-page="home"] {
+ /* Reusable title component defined at page level since it's used in multiple components */
+ [data-component="title"] {
+ text-transform: uppercase;
+ font-weight: 400;
+ }
+
+ [data-component="install"] {
+ /* install-specific styles */
+ }
+
+ [data-component="screenshots"] {
+ /* screenshots-specific styles */
+ }
+}
+```
+
+This is correct because the `title` component has consistent styling and behavior across the page.
+
+## Key Clarifications
+
+1. **JSX Nesting is Flexible**: Components can be nested inside slots, slots can contain components - whatever makes semantic sense
+2. **CSS Hierarchy is Strict**: Follow pages → components → slots structure in CSS
+3. **Reusable Components**: Define at the appropriate level where they're shared (page level if used across the page, component level if only used within that component)
+4. **DOM vs CSS Structure**: These don't need to match - optimize each for its purpose
+
+See ./src/routes/index.css and ./src/routes/index.tsx for a complete example.
diff --git a/.opencode/agent/docs.md b/.opencode/agent/docs.md
new file mode 100644
index 00000000..21cfc6a1
--- /dev/null
+++ b/.opencode/agent/docs.md
@@ -0,0 +1,34 @@
+---
+description: ALWAYS use this when writing docs
+color: "#38A3EE"
+---
+
+You are an expert technical documentation writer
+
+You are not verbose
+
+Use a relaxed and friendly tone
+
+The title of the page should be a word or a 2-3 word phrase
+
+The description should be one short line, should not start with "The", should
+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 separated by a divider of 3 dashes
+
+The section titles are short with only the first letter of the word capitalized
+
+The section titles are in the imperative mood
+
+The section titles should not repeat the term used in the page title, for
+example, if the page title is "Models", avoid using a section title like "Add
+new models". This might be unavoidable in some cases, but try to avoid it.
+
+Check out the /packages/web/src/content/docs/docs/index.mdx as an example.
+
+For JS or TS code snippets remove trailing semicolons and any trailing commas
+that might not be needed.
+
+If you are making a commit prefix the commit message with `docs:`
diff --git a/.opencode/agent/duplicate-pr.md b/.opencode/agent/duplicate-pr.md
new file mode 100644
index 00000000..c9c932ef
--- /dev/null
+++ b/.opencode/agent/duplicate-pr.md
@@ -0,0 +1,26 @@
+---
+mode: primary
+hidden: true
+model: opencode/claude-haiku-4-5
+color: "#E67E22"
+tools:
+ "*": false
+ "github-pr-search": true
+---
+
+You are a duplicate PR detection agent. When a PR is opened, your job is to search for potentially duplicate or related open PRs.
+
+Use the github-pr-search tool to search for PRs that might be addressing the same issue or feature.
+
+IMPORTANT: The input will contain a line `CURRENT_PR_NUMBER: NNNN`. This is the current PR number, you should not mark that the current PR as a duplicate of itself.
+
+Search using keywords from the PR title and description. Try multiple searches with different relevant terms.
+
+If you find potential duplicates:
+
+- List them with their titles and URLs
+- Briefly explain why they might be related
+
+If no duplicates are found, say so clearly. BUT ONLY SAY "No duplicate PRs found" (don't say anything else if no dups)
+
+Keep your response concise and actionable.
diff --git a/.opencode/agent/triage.md b/.opencode/agent/triage.md
new file mode 100644
index 00000000..5d1147a8
--- /dev/null
+++ b/.opencode/agent/triage.md
@@ -0,0 +1,78 @@
+---
+mode: primary
+hidden: true
+model: opencode/claude-haiku-4-5
+color: "#44BA81"
+tools:
+ "*": false
+ "github-triage": true
+---
+
+You are a triage agent responsible for triaging github issues.
+
+Use your github-triage tool to triage issues.
+
+## Labels
+
+### windows
+
+Use for any issue that mentions Windows (the OS). Be sure they are saying that they are on Windows.
+
+- Use if they mention WSL too
+
+#### perf
+
+Performance-related issues:
+
+- Slow performance
+- High RAM usage
+- High CPU usage
+
+**Only** add if it's likely a RAM or CPU issue. **Do not** add for LLM slowness.
+
+#### desktop
+
+Desktop app issues:
+
+- `opencode web` command
+- The desktop app itself
+
+**Only** add if it's specifically about the Desktop application or `opencode web` view. **Do not** add for terminal, TUI, or general opencode issues.
+
+#### nix
+
+**Only** add if the issue explicitly mentions nix.
+
+#### zen
+
+**Only** add if the issue mentions "zen" or "opencode zen" or "opencode black".
+
+If the issue doesn't have "zen" or "opencode black" in it then don't add zen label
+
+#### docs
+
+Add if the issue requests better documentation or docs updates.
+
+#### opentui
+
+TUI issues potentially caused by our underlying TUI library:
+
+- Keybindings not working
+- Scroll speed issues (too fast/slow/laggy)
+- Screen flickering
+- Crashes with opentui in the log
+
+**Do not** add for general TUI bugs.
+
+When assigning to people here are the following rules:
+
+adamdotdev:
+ONLY assign adam if the issue will have the "desktop" label.
+
+fwang:
+ONLY assign fwang if the issue will have the "zen" label.
+
+jayair:
+ONLY assign jayair if the issue will have the "docs" label.
+
+In all other cases use best judgment. Avoid assigning to kommander needlessly, when in doubt assign to rekram1-node.
diff --git a/packages/desktop/src-tauri/Cargo.lock b/packages/desktop/src-tauri/Cargo.lock
index b944a762..f70fa7fd 100644
--- a/packages/desktop/src-tauri/Cargo.lock
+++ b/packages/desktop/src-tauri/Cargo.lock
@@ -921,14 +921,13 @@ dependencies = [
[[package]]
name = "filetime"
-version = "0.2.26"
+version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed"
+checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db"
dependencies = [
"cfg-if",
"libc",
"libredox",
- "windows-sys 0.60.2",
]
[[package]]
@@ -2371,7 +2370,7 @@ dependencies = [
[[package]]
name = "openwork"
-version = "0.2.9"
+version = "0.3.0"
dependencies = [
"serde",
"serde_json",
@@ -5415,9 +5414,9 @@ dependencies = [
[[package]]
name = "zbus"
-version = "5.13.1"
+version = "5.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17f79257df967b6779afa536788657777a0001f5b42524fcaf5038d4344df40b"
+checksum = "1bfeff997a0aaa3eb20c4652baf788d2dfa6d2839a0ead0b3ff69ce2f9c4bdd1"
dependencies = [
"async-broadcast",
"async-executor",
@@ -5450,9 +5449,9 @@ dependencies = [
[[package]]
name = "zbus_macros"
-version = "5.13.1"
+version = "5.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aad23e2d2f91cae771c7af7a630a49e755f1eb74f8a46e9f6d5f7a146edf5a37"
+checksum = "0bbd5a90dbe8feee5b13def448427ae314ccd26a49cac47905cafefb9ff846f1"
dependencies = [
"proc-macro-crate 3.4.0",
"proc-macro2",
@@ -5568,15 +5567,15 @@ dependencies = [
[[package]]
name = "zmij"
-version = "1.0.15"
+version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94f63c051f4fe3c1509da62131a678643c5b6fbdc9273b2b79d4378ebda003d2"
+checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65"
[[package]]
name = "zvariant"
-version = "5.9.1"
+version = "5.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "326aaed414f04fe839777b4c443d4e94c74e7b3621093bd9c5e649ac8aa96543"
+checksum = "68b64ef4f40c7951337ddc7023dd03528a57a3ce3408ee9da5e948bd29b232c4"
dependencies = [
"endi",
"enumflags2",
@@ -5588,9 +5587,9 @@ dependencies = [
[[package]]
name = "zvariant_derive"
-version = "5.9.1"
+version = "5.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba44e1f8f4da9e6e2d25d2a60b116ef8b9d0be174a7685e55bb12a99866279a7"
+checksum = "484d5d975eb7afb52cc6b929c13d3719a20ad650fea4120e6310de3fc55e415c"
dependencies = [
"proc-macro-crate 3.4.0",
"proc-macro2",