Skip to content

Conversation

@shuv1337
Copy link
Collaborator

@shuv1337 shuv1337 commented Nov 21, 2025

Note

Introduce favorite models with persistence, star indicators, favorite cycling keybinds, and in-dialog toggle; update SDK types and docs accordingly.

  • TUI:
    • Add commands/keybinds for cycling favorite models: model_cycle_favorite, model_cycle_favorite_reverse in packages/opencode/src/cli/cmd/tui/app.tsx.
    • Enhance DialogModel (packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx): show separate “Favorites” and “Recent” sections, star-mark favorites, prioritize current recent, and add ctrl+f to toggle favorite.
  • State/Persistence:
    • Extend model store in packages/opencode/src/cli/cmd/tui/context/local.tsx with favorite[]; load/save to state/model.json; add favorite(), toggleFavorite(), and cycleFavorite(); persist via shared save().
  • Config/Keybinds:
    • Add model_cycle_favorite and model_cycle_favorite_reverse to schema with default "none" (packages/opencode/src/config/config.ts).
  • SDKs/Types:
    • Go/JS/Python SDKs updated to include new keybind fields (model_cycle_favorite*) in generated types.
  • UI:
    • packages/ui/src/components/select-dialog.tsx: add onKeyEvent to pass key events and selected item to consumers.
  • Docs:
    • Update keybinds docs to include favorite model cycling (packages/web/src/content/docs/keybinds.mdx).

Written by Cursor Bugbot for commit fd4d38b. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features
    • Added favorites feature to mark and organize preferred models
    • Ability to cycle through favorite models using configurable keyboard shortcuts
    • Favorite models now display in a dedicated section within the model selection dialog with visual indicator
    • Favorites persist across sessions for quick access to frequently used models

✏️ Tip: You can customize this high-level summary in your review settings.

@shuv1337
Copy link
Collaborator Author

@copilot fix type errors

Copy link

Copilot AI commented Nov 21, 2025

@kcrommett I've opened a new pull request, #28, to work on those changes. Once the pull request is ready, I'll request review from you.

@coderabbitai
Copy link

coderabbitai bot commented Nov 21, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The pull request introduces a favorites feature that allows users to cycle through their preferred models using new keybinds. Changes include new UI commands, expanded configuration options across TypeScript and SDK packages, enhanced state management for persistence, refactored dialog model with ordering logic, and component updates to support keyboard events for favorites.

Changes

Cohort / File(s) Summary
Keybind Configuration
packages/opencode/src/config/config.ts, packages/sdk/go/config.go, packages/sdk/python/src/opencode_ai/models/keybinds_config.py, packages/web/src/content/docs/keybinds.mdx
Added two new keybind configuration fields: model_cycle_favorite and model_cycle_favorite_reverse (defaults "none", describes "Next/Previous favorite model") across TypeScript config, Go structs, Python models, and documentation.
Dialog Model & UI Components
packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx, packages/ui/src/components/select-dialog.tsx, packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx
Refactored dialog model with comprehensive favorite handling: stateful ordering for favorites and recents, enhanced option construction with favorite indicators, added onKeyEvent callback to SelectDialogProps for keyboard integration, and adjusted dialog spacing.
App Commands
packages/opencode/src/cli/cmd/tui/app.tsx
Added two UI command items: "Favorite cycle" (calls cycleFavorite(1)) and "Favorite cycle reverse" (calls cycleFavorite(-1)) with keybinds and Agent category.
State Management
packages/opencode/src/cli/cmd/tui/context/local.tsx
Extended model store with favorite array field, added public methods cycleFavorite(direction), toggleFavorite(model), and favorite() accessor; implemented persistence via new save() helper that writes both recent and favorite data to disk.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant App as App
    participant Context as local.tsx<br/>(State)
    participant Dialog as dialog-model.tsx<br/>(UI)
    participant Disk as model.json

    User->>App: Press Ctrl+F (cycle favorite keybind)
    App->>Context: cycleFavorite(1)
    Context->>Context: Select next favorite model
    Context->>Context: Update current & recent
    Context->>Disk: save() → persist {recent, favorite}
    Disk-->>Context: Persisted
    Context-->>Dialog: Notify state change
    Dialog->>Dialog: Recompute orderedFavorites<br/>& option ordering
    Dialog-->>User: Render updated model list
    Note over Dialog: Favorites appear first<br/>with ★ indicator
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • dialog-model.tsx: Significant refactoring with new stateful ordering system, context usage, and filtering logic—requires careful verification of favorites/recents/provider ordering and option construction.
  • local.tsx: New public API surface and persistence logic—verify save() helper properly encapsulates writes and that cycleFavorite correctly handles empty favorites and wraps appropriately.
  • SelectDialogProps: New onKeyEvent callback—confirm it's invoked on all key events and selected item is properly computed outside Enter branch.
  • Cross-package config changes: Repetitive but low-risk; verify consistency of field names across TypeScript, Go, Python, and documentation.

Poem

🐰 A cycle through favorites so neat,
With stars that make models complete,
Ctrl+F to dance through the list,
Forward and backward—you can't miss!
Hop, hop, hop—your perfect choice is kissed! ⭐

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Add model favorites' directly and accurately summarizes the main change: introducing a new favorites feature for models with persistence, UI indicators, and cycling keybinds.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (1)

23-178: Solid refactoring with clear separation of concerns.

The refactored option construction properly separates favorites, recents, and provider models with appropriate deduplication. The star indicator (★) correctly shows favorite status across all option types.

The favorite checking logic is repeated in multiple places (lines 81, 127-129). Consider extracting to a helper function:

+  const isFavorite = (item: { providerID: string; modelID: string }) =>
+    favorites.some((fav) => fav.providerID === item.providerID && fav.modelID === item.modelID)
+
   const recentOptions = !query
     ? orderedRecents.flatMap((item) => {
         const provider = sync.data.provider.find((x) => x.id === item.providerID)
         if (!provider) return []
         const model = provider.models[item.modelID]
         if (!model) return []
-        const favorite = favorites.some((fav) => fav.providerID === item.providerID && fav.modelID === item.modelID)
+        const favorite = isFavorite(item)
         return [
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a67b616 and b67a0bb.

⛔ Files ignored due to path filters (1)
  • packages/sdk/js/src/gen/types.gen.ts is excluded by !**/gen/**
📒 Files selected for processing (8)
  • packages/opencode/src/cli/cmd/tui/app.tsx (1 hunks)
  • packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (4 hunks)
  • packages/opencode/src/cli/cmd/tui/context/local.tsx (4 hunks)
  • packages/opencode/src/config/config.ts (1 hunks)
  • packages/sdk/go/config.go (2 hunks)
  • packages/sdk/python/src/opencode_ai/models/keybinds_config.py (6 hunks)
  • packages/ui/src/components/select-dialog.tsx (2 hunks)
  • packages/web/src/content/docs/keybinds.mdx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (1)
packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx (1)
  • filter (149-151)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: update
🔇 Additional comments (12)
packages/opencode/src/config/config.ts (1)

420-421: LGTM!

The new keybind entries follow the established pattern and are appropriately defaulted to "none" for opt-in functionality.

packages/sdk/python/src/opencode_ai/models/keybinds_config.py (1)

46-47: LGTM!

The Python SDK changes correctly implement serialization and deserialization for the new keybind fields, following the established pattern.

Also applies to: 100-101, 183-186, 288-291, 396-399, 469-470

packages/sdk/go/config.go (1)

1938-1941: LGTM!

The Go SDK changes correctly add the new keybind fields with proper naming conventions and JSON metadata.

Also applies to: 2015-2016

packages/web/src/content/docs/keybinds.mdx (1)

41-42: LGTM!

Documentation correctly reflects the new keybind configuration options with appropriate defaults.

packages/opencode/src/cli/cmd/tui/app.tsx (1)

251-268: LGTM!

The favorite cycling commands follow the established pattern for recent model cycling and correctly integrate with the local context API.

packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (1)

191-197: LGTM!

The ctrl+f keybind correctly integrates with the toggleFavorite API and passes the selected model option.

packages/opencode/src/cli/cmd/tui/context/local.tsx (4)

129-137: Good refactoring with the shared save() helper.

Centralizing persistence logic improves maintainability and ensures favorites and recents are always saved together.


202-204: LGTM!

Simple getter following the established pattern for accessing favorites.


227-253: Solid implementation with good UX considerations.

The cycleFavorite method correctly handles edge cases (no favorites, wrapping), validates models, maintains recent history, and persists state.


273-292: LGTM!

The toggleFavorite method correctly validates, toggles, and persists favorite status.

packages/ui/src/components/select-dialog.tsx (2)

14-14: LGTM: Clean API extension for key event forwarding.

The addition of the optional onKeyEvent prop enables consumers to observe keyboard interactions and the currently selected item, supporting features like favorite toggling via ctrl+f as described in the PR objectives.


62-76: ****

The review comment conflates two different components and makes an incorrect verification request. The original comment assumes onKeyEvent callback (from SelectDialog in the ui package) is used for favorites cycling in dialog-model.tsx, but it is not.

Actual implementation: dialog-model.tsx uses DialogSelect (a different component in the TUI package), not SelectDialog. Favorites cycling with ctrl+f is handled through DialogSelect's keybind system (line 192), not through SelectDialog's onKeyEvent callback. The onKeyEvent callback is defined in SelectDialog but is completely unused in the codebase.

The refactor at select-dialog.tsx (lines 62-76) is valid — it correctly moves the onKeyEvent call before internal handling. However, this change has no impact on favorites cycling since that feature never used this callback. The requested verification for favorites cycling is therefore not applicable to this code change.

Likely an incorrect or invalid review comment.

const next = favorites[index]
if (!next) return
setModelStore("model", agent.current().name, { ...next })
const uniq = uniqueBy([next, ...modelStore.recent], (x) => x.providerID + x.modelID)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Consider using a delimiter in uniqueBy key generation.

The uniqueBy predicate concatenates providerID and modelID without a delimiter. While unlikely, this could cause collisions (e.g., {providerID: "ab", modelID: "cdef"} and {providerID: "abc", modelID: "def"} both produce "abcdef").

Apply this diff to use a delimiter:

-          const uniq = uniqueBy([next, ...modelStore.recent], (x) => x.providerID + x.modelID)
+          const uniq = uniqueBy([next, ...modelStore.recent], (x) => `${x.providerID}/${x.modelID}`)

Also update line 266:

-              const uniq = uniqueBy([model, ...modelStore.recent], (x) => x.providerID + x.modelID)
+              const uniq = uniqueBy([model, ...modelStore.recent], (x) => `${x.providerID}/${x.modelID}`)

Also applies to: 266-266

🤖 Prompt for AI Agents
In packages/opencode/src/cli/cmd/tui/context/local.tsx around lines 249 and 266,
the uniqueBy key concatenates providerID and modelID without a separator which
can cause collisions; update both occurrences to include a clear delimiter (for
example '|' or '::') when building the key (e.g., use
`${x.providerID}|${x.modelID}`) so the combined key is unambiguous and preserves
uniqueness.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/console/app/package.json (1)

1-11: Move version field to root level of package.json.

The version field is incorrectly nested inside the scripts object. In package.json, version must be a top-level field alongside name, type, scripts, etc. This malformation will cause npm/bun tooling to not recognize the package version, breaking versioning and publishing workflows.

Apply this diff to correct the structure:

{
  "name": "@opencode-ai/console-app",
  "type": "module",
+ "version": "1.0.86",
  "scripts": {
    "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": "./script/generate-sitemap.ts && vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
    "start": "vinxi start",
-   "version": "1.0.86"
  },
🧹 Nitpick comments (1)
packages/opencode/src/tool/bash.ts (1)

185-193: Constrain $SHELL usage to non-Windows to avoid cross-platform shell incompatibility.

The change from shell: true to shell: process.env.SHELL ?? true introduces valid cross-platform and shell compatibility risks:

  • On Windows: process.env.SHELL may be set to a Unix-style path (e.g., by Git Bash or WSL tooling), but Node.js spawn will attempt to execute that path directly, which fails because Windows does not recognize Unix paths and those executables don't exist in the Windows filesystem.
  • On non-POSIX shells: If the agent generates bash-specific syntax and the user's $SHELL is fish, csh, or another non-POSIX shell, the command will fail when executed by that shell instead of /bin/sh.

Recommended fix: Constrain to non-Windows and fall back to Node's default on Windows:

-    const proc = spawn(params.command, {
-      shell: process.env.SHELL ?? true, // $SHELL if set, otherwise default shell
+    const proc = spawn(params.command, {
+      shell: process.platform === "win32" ? true : process.env.SHELL ?? true,
       cwd: Instance.directory,

If non-POSIX shell support is not intended, also consider restricting $SHELL to known POSIX shells (bash, sh, ksh, zsh) to avoid incompatibility with the agent's generated commands.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1dab0fa and 087f075.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • package.json (1 hunks)
  • packages/console/app/package.json (1 hunks)
  • packages/console/core/package.json (1 hunks)
  • packages/console/function/package.json (1 hunks)
  • packages/console/mail/package.json (1 hunks)
  • packages/desktop/package.json (1 hunks)
  • packages/extensions/zed/extension.toml (2 hunks)
  • packages/function/package.json (1 hunks)
  • packages/opencode/package.json (1 hunks)
  • packages/opencode/src/tool/bash.ts (1 hunks)
  • packages/plugin/package.json (1 hunks)
  • packages/sdk/js/package.json (1 hunks)
  • packages/slack/package.json (1 hunks)
  • packages/ui/package.json (1 hunks)
  • packages/util/package.json (1 hunks)
  • packages/web/package.json (1 hunks)
  • packages/web/src/content/docs/zen.mdx (1 hunks)
  • sdks/vscode/package.json (1 hunks)
✅ Files skipped from review due to trivial changes (9)
  • packages/web/package.json
  • packages/console/mail/package.json
  • sdks/vscode/package.json
  • packages/desktop/package.json
  • packages/function/package.json
  • packages/console/core/package.json
  • packages/ui/package.json
  • packages/console/function/package.json
  • packages/util/package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: update
  • GitHub Check: update
🔇 Additional comments (7)
packages/extensions/zed/extension.toml (1)

4-4: Version bump applied consistently across all platform targets.

The version has been updated from 1.0.85 to 1.0.86, and all archive URLs for the five platform targets (darwin-aarch64, darwin-x86_64, linux-aarch64, linux-x86_64, windows-x86_64) have been synchronized correctly. The GitHub release URL pattern is correct and consistent.

Also applies to: 14-14, 19-19, 24-24, 29-29, 34-34

package.json (1)

7-7: Verify Bun 1.3.3 compatibility with the project.

The packageManager has been bumped from bun@1.3.2 to bun@1.3.3. Confirm this patch update doesn't introduce any breaking changes or incompatibilities with the new favorites feature or other toolchain configurations.

packages/slack/package.json (1)

3-3: Version bump aligns with repository-wide versioning.

Coordinated version bump across packages to 1.0.86.

packages/plugin/package.json (1)

4-4: Version bump aligns with repository-wide versioning.

Coordinated version bump across packages to 1.0.86.

packages/sdk/js/package.json (1)

4-4: Version bump aligns with repository-wide versioning.

Coordinated version bump across packages to 1.0.86.

packages/opencode/package.json (1)

3-3: Version bump aligns with repository-wide versioning.

Coordinated version bump to 1.0.86 with correct root-level placement.

packages/web/src/content/docs/zen.mdx (1)

136-139: Verify GPT 5.x pricing reflects actual backend pricing.

The pricing table for GPT 5.x models has been updated with reduced rates across input (~14% reduction), output (~15%), and cached reads. Confirm these prices match the actual backend pricing configuration and that no other documentation or config files need corresponding updates.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
packages/ui/src/components/message-nav.tsx (1)

67-73: Tooltip placement + nested MessageNav override make sense; minor prop cleanup possible

Using placement="left-start" for the compact tooltip is fine and keeps the API usage intact. Overriding class on the nested <MessageNav> to "" is a good way to prevent outer layout/visibility classes (e.g., breakpoint-based hidden/margins) from affecting the tooltip content.

If you want to tighten this up, you could instead avoid passing class at all to the nested MessageNav (e.g., by cloning props with class removed before spreading), which is a bit clearer than using an empty string but functionally equivalent here.

packages/enterprise/src/routes/share/[sessionID].tsx (2)

187-252: wide memo and MessageNav layout logic are coherent and match intent

Deriving wide from diffs().length === 0 and then using it to drive padding (px-21 vs px-6), MessageNav positioning (right-[90%] vs right-full), and size (normal vs compact) makes the layout logic much easier to follow and keeps the “no diffs” vs “has diffs” states clearly separated.

Behavior-wise, this preserves:

  • A wider content area when there are no diffs.
  • A more compact nav when the diff panel is present.
  • Separate compact (xl:hidden) and larger (xl:block) nav instances with breakpoint-specific positioning.

I don’t see any functional regressions; this is a nice cleanup.


267-297: Explicit diffs gating looks good; consider making the mobile label dynamic

Gating both the desktop diff panel and the mobile Tabs on diffs().length > 0 is clearer and avoids truthiness edge cases. The overall control flow for small screens (Tabs when there are diffs, plain turns() otherwise) is straightforward and correct.

One small improvement you might consider while you’re here: the “5 Files Changed” label in the mobile Tabs.Trigger is still hard-coded. Using diffs().length (or a precomputed fileCount memo) would keep the label accurate when the number of changed files varies.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1984ba5 and 912d7ec.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • nix/hashes.json (1 hunks)
  • packages/desktop/src/pages/session.tsx (1 hunks)
  • packages/enterprise/src/routes/share/[sessionID].tsx (3 hunks)
  • packages/opencode/package.json (1 hunks)
  • packages/sdk/js/src/client.ts (1 hunks)
  • packages/ui/src/components/message-nav.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • nix/hashes.json
🧰 Additional context used
📓 Path-based instructions (7)
packages/opencode/**/package.json

📄 CodeRabbit inference engine (packages/opencode/AGENTS.md)

Install dependencies using bun install

Files:

  • packages/opencode/package.json
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: DO NOT do unnecessary destructuring of variables
DO NOT use else statements unless necessary
DO NOT use try/catch if it can be avoided
AVOID try/catch where possible
AVOID let statements
PREFER single word variable names where possible
Use as many Bun APIs as possible like Bun.file()

Files:

  • packages/sdk/js/src/client.ts
  • packages/ui/src/components/message-nav.tsx
  • packages/desktop/src/pages/session.tsx
  • packages/enterprise/src/routes/share/[sessionID].tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

AVOID using any type

Files:

  • packages/sdk/js/src/client.ts
  • packages/ui/src/components/message-nav.tsx
  • packages/desktop/src/pages/session.tsx
  • packages/enterprise/src/routes/share/[sessionID].tsx
packages/desktop/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/desktop/AGENTS.md)

packages/desktop/**/*.{ts,tsx,js,jsx}: Use @/ alias for imports from src/ directory (e.g., import Button from "@/ui/button")
Use function declarations and splitProps for SolidJS component props
Use camelCase for variable and function names

Files:

  • packages/desktop/src/pages/session.tsx
packages/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/desktop/AGENTS.md)

Define interfaces for component props and avoid using any type

Files:

  • packages/desktop/src/pages/session.tsx
packages/desktop/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (packages/desktop/AGENTS.md)

Use TailwindCSS with custom CSS variables theme system for styling

Files:

  • packages/desktop/src/pages/session.tsx
packages/desktop/**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (packages/desktop/AGENTS.md)

Use snake_case for file names

Files:

  • packages/desktop/src/pages/session.tsx
🧠 Learnings (6)
📚 Learning: 2025-11-24T22:47:41.129Z
Learnt from: CR
Repo: kcrommett/opencode PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2025-11-24T22:47:41.129Z
Learning: Applies to packages/opencode/**/*.{ts,tsx} : Use relative imports for local modules with named imports preferred

Applied to files:

  • packages/opencode/package.json
📚 Learning: 2025-11-24T22:47:41.129Z
Learnt from: CR
Repo: kcrommett/opencode PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2025-11-24T22:47:41.129Z
Learning: Applies to packages/opencode/**/*.{ts,tsx} : Organize code using namespace-based structure (e.g., `Tool.define()`, `Session.create()`)

Applied to files:

  • packages/opencode/package.json
  • packages/enterprise/src/routes/share/[sessionID].tsx
📚 Learning: 2025-11-24T22:47:41.129Z
Learnt from: CR
Repo: kcrommett/opencode PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2025-11-24T22:47:41.129Z
Learning: Applies to packages/opencode/**/*.{ts,tsx} : Pass `sessionID` in tool context and use `App.provide()` for dependency injection

Applied to files:

  • packages/opencode/package.json
  • packages/enterprise/src/routes/share/[sessionID].tsx
📚 Learning: 2025-11-24T22:47:41.129Z
Learnt from: CR
Repo: kcrommett/opencode PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2025-11-24T22:47:41.129Z
Learning: Go TUI communicates with TypeScript server via stainless SDK; when modifying server endpoints in `packages/opencode/src/server/server.ts`, request user to generate a new client SDK before proceeding with client-side changes

Applied to files:

  • packages/sdk/js/src/client.ts
📚 Learning: 2025-11-24T22:47:28.227Z
Learnt from: CR
Repo: kcrommett/opencode PR: 0
File: packages/desktop/AGENTS.md:0-0
Timestamp: 2025-11-24T22:47:28.227Z
Learning: Applies to packages/desktop/**/*.{ts,tsx,js,jsx} : Use function declarations and splitProps for SolidJS component props

Applied to files:

  • packages/desktop/src/pages/session.tsx
  • packages/enterprise/src/routes/share/[sessionID].tsx
📚 Learning: 2025-11-24T22:47:28.227Z
Learnt from: CR
Repo: kcrommett/opencode PR: 0
File: packages/desktop/AGENTS.md:0-0
Timestamp: 2025-11-24T22:47:28.227Z
Learning: Applies to packages/desktop/**/({ui,components,pages})/**/*.{ts,tsx,js,jsx} : Use PascalCase for component names

Applied to files:

  • packages/desktop/src/pages/session.tsx
🧬 Code graph analysis (3)
packages/sdk/js/src/client.ts (2)
packages/console/app/src/config.ts (1)
  • config (4-29)
packages/opencode/src/share/share.ts (1)
  • URL (68-70)
packages/ui/src/components/message-nav.tsx (1)
packages/ui/src/components/tooltip.tsx (1)
  • Tooltip (11-50)
packages/enterprise/src/routes/share/[sessionID].tsx (1)
packages/ui/src/components/message-nav.tsx (1)
  • MessageNav (7-81)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: update
  • GitHub Check: update
🔇 Additional comments (2)
packages/desktop/src/pages/session.tsx (1)

351-355: 6xl breakpoint display change for MessageNav looks consistent

Switching the large-screen class from @6xl:block to @6xl:flex aligns the nav with the surrounding flex-based layout and should improve alignment without affecting behavior at smaller breakpoints. I don’t see any functional or layout regressions based on this file alone.

packages/opencode/package.json (1)

65-66: Dependency versions verified and properly locked.

Both @opentui/core@0.1.50 and @opentui/solid@0.1.50 are valid versions released on npm (2025-11-24) and correctly reflected in bun.lock with all platform-specific binaries. The patch version bump is compatible with existing codebase usage (useKeyboard, useRenderer, useTerminalDimensions, etc.) and satisfies opentui-spinner's peer dependency constraint (^0.1.49).

Note: SelectDialog with onKeyEvent prop was not found in the current codebase, so this may reference planned functionality or a different change.

Comment on lines 28 to 29
const baseUrl = new URL(config.baseUrl)
if (baseUrl.username || baseUrl.password) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Verify URL validation upstream or document throwing behavior.

The new URL() constructor will throw a TypeError if config.baseUrl is malformed. Ensure that callers validate the URL format before passing it to this function, or document that this function may throw on invalid input.

Run the following script to check if there's upstream validation:


🏁 Script executed:

#!/bin/bash
# Description: Find all calls to createOpencodeClient and check for URL validation

# Find all calls to createOpencodeClient
rg -nP -A5 -B5 'createOpencodeClient\s*\(' --type=ts --type=js

Length of output: 8241


Looking at the output, I found a critical issue: on line 250 of packages/opencode/src/cli/cmd/run.ts, the baseUrl is set to args.attach which comes from CLI arguments—this appears to be user input that may not be validated. Let me verify if this is validated upstream.<!-- [scratchpad_start]
[task review_file_1/1 review_segment_2/3 btoa_verification]
Verify btoa() Unicode limitation is a real issue.
[observations]
Web search confirms btoa() limitation with Unicode:

  • btoa() only accepts Latin-1 characters (0-255 code point range)
  • Throws "InvalidCharacterError: The string to be encoded contains characters outside of the Latin1 range"
  • Affects emoji, accented characters, non-Latin scripts
  • MDN, Stack Overflow, and multiple sources confirm this
    [analysis]
  • btoa(username:password) with Unicode characters WILL throw
  • Example: username with emoji or accented chars will cause failure
  • This is a real security/usability issue that should be flagged
    [done]
    btoa() limitation confirmed as major issue
    [scratchpad_start] -->

Now I need to check the actual code around line 28-29 to see the full context of the btoa() usage:


🏁 Script executed:

#!/bin/bash
# Get the full context around lines 28-38 in packages/sdk/js/src/client.ts
head -40 packages/sdk/js/src/client.ts | tail -20

Length of output: 513


Now I need to check if there's validation of the attach CLI argument:

Based on my verification, I have found two issues that need addressing:

Fix btoa() to support Unicode credentials.

The code uses btoa() which will throw a "Character Out Of Range" exception if a character's code point exceeds 0xff. For use cases where you need to encode arbitrary Unicode text, it is necessary to first convert the string to its constituent bytes in UTF-8, and then encode the bytes.

Example failure case: If a URL contains credentials with Unicode characters (emoji, accented characters, non-Latin scripts), the btoa() call at line 32 will throw an uncaught error.

Recommended fix: Use the TextEncoder API to first encode the Unicode string into a sequence of UTF-8 bytes, then convert those bytes into a Base64 string.

// Replace line 32 with:
const utf8Bytes = new TextEncoder().encode(`${baseUrl.username}:${baseUrl.password}`)
const binaryString = String.fromCharCode.apply(null, Array.from(utf8Bytes))
Authorization: `Basic ${btoa(binaryString)}`
🤖 Prompt for AI Agents
packages/sdk/js/src/client.ts around lines 28-32: the code uses btoa on
`${baseUrl.username}:${baseUrl.password}` which throws for Unicode characters;
replace that call by encoding the credential string to UTF-8 bytes with
TextEncoder, convert the resulting Uint8Array to a binary string (e.g. by
mapping bytes to String.fromCharCode), then call btoa on that binary string and
set Authorization to `Basic ${...}`; ensure this path only runs when
username/password are present and preserve existing header behavior.

if (baseUrl.username || baseUrl.password) {
config.headers = {
...config.headers,
Authorization: `Basic ${btoa(`${baseUrl.username}:${baseUrl.password}`)}`,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace btoa() with Unicode-safe encoding.

btoa() only supports Latin1 characters and will throw InvalidCharacterError if the username or password contains Unicode characters (e.g., emoji, accented letters, non-Latin scripts). Users with valid Unicode passwords will encounter runtime errors.

Apply this diff to use Unicode-safe encoding:

-        Authorization: `Basic ${btoa(`${baseUrl.username}:${baseUrl.password}`)}`,
+        Authorization: `Basic ${Buffer.from(`${baseUrl.username}:${baseUrl.password}`, 'utf-8').toString('base64')}`,

Note: If Buffer is not available in the target environment, use:

const credentials = `${baseUrl.username}:${baseUrl.password}`
const encoded = typeof Buffer !== 'undefined' 
  ? Buffer.from(credentials, 'utf-8').toString('base64')
  : btoa(unescape(encodeURIComponent(credentials)))
🤖 Prompt for AI Agents
In packages/sdk/js/src/client.ts around line 32, the Authorization header
currently uses btoa which fails for Unicode credentials; replace it by first
building credentials = `${baseUrl.username}:${baseUrl.password}` and then
base64-encoding using Buffer when available (typeof Buffer !== 'undefined' ?
Buffer.from(credentials, 'utf-8').toString('base64') :
btoa(unescape(encodeURIComponent(credentials)))) and use that encoded string in
the Authorization header; ensure the typeof Buffer check is used so environments
without Buffer fall back to the Unicode-safe btoa wrapper.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (2)

23-54: LGTM! Well-structured ordering and deduplication logic.

The state management helpers correctly prioritize the current model and prevent duplicates between favorites and recents lists. The logic is clear and correct.

Optional: Consider shorter variable names per coding guidelines.

The coding guidelines suggest preferring single-word variable names where possible. Variables like orderedRecents, currentIsFavorite, and orderedRecentList could be shortened (e.g., ordered, isFav, recentList), though the current names are more descriptive.


89-120: LGTM! Recent options correctly constructed.

The recent options logic mirrors the favorites implementation and correctly excludes items already in favorites.

Optional: Extract shared option-building logic.

The construction logic for favoriteOptions (lines 55-87) and recentOptions (lines 89-120) is nearly identical. Consider extracting a helper function to reduce duplication:

const buildOptionsFromList = (
  list: typeof favorites,
  category: string,
  showStar = false
) => {
  return list.flatMap((item) => {
    const provider = sync.data.provider.find((x) => x.id === item.providerID)
    if (!provider) return []
    const model = provider.models[item.modelID]
    if (!model) return []
    return [{
      value: { providerID: provider.id, modelID: model.id },
      title: model.name ?? item.modelID,
      description: `${provider.name}${showStar ? " ★" : ""}`,
      category,
      disabled: provider.id === "opencode" && model.id.includes("-nano"),
      footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
      onSelect: () => {
        dialog.clear()
        local.model.set({ providerID: provider.id, modelID: model.id }, { recent: true })
      }
    }]
  })
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12f6be4 and e747767.

📒 Files selected for processing (2)
  • packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (6 hunks)
  • packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: DO NOT do unnecessary destructuring of variables
DO NOT use else statements unless necessary
DO NOT use try/catch if it can be avoided
AVOID try/catch where possible
AVOID let statements
PREFER single word variable names where possible
Use as many Bun APIs as possible like Bun.file()

Files:

  • packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx
  • packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

AVOID using any type

Files:

  • packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx
  • packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
packages/opencode/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/opencode/AGENTS.md)

Use Bun with TypeScript ESM modules as the runtime environment

Files:

  • packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx
  • packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
packages/opencode/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/opencode/AGENTS.md)

packages/opencode/**/*.{ts,tsx}: Use relative imports for local modules with named imports preferred
Use Zod schemas for validation
Use TypeScript interfaces for defining data structures
Use camelCase for variable and function names
Use PascalCase for class and namespace names
Use Result patterns for error handling; avoid throwing exceptions in tools
Organize code using namespace-based structure (e.g., Tool.define(), Session.create())
Pass sessionID in tool context and use App.provide() for dependency injection
Validate all inputs with Zod schemas
Use Log.create({ service: "name" }) pattern for logging
Use Storage namespace for data persistence

Files:

  • packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx
  • packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
🧬 Code graph analysis (1)
packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (1)
packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx (1)
  • filter (149-151)
🔇 Additional comments (4)
packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx (1)

240-240: LGTM! Improved spacing in action bar.

The increased gap improves visual separation between action items in the bottom bar.

packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (3)

4-8: LGTM! Import changes align with new favorites feature.

The additional remeda utilities and Keybind import support the new favorites and recents logic.


55-87: LGTM! Favorites section correctly constructed.

The favorite options are built with proper filtering, star indicators, and category labeling. The onSelect handler correctly updates state and marks as recent.


122-191: LGTM! Provider options correctly filter duplicates and show favorites.

The concatenation of favoriteOptions, recentOptions, and provider options with filtering ensures no duplicates appear in the list. The star indicators and category assignments are consistent throughout.

Comment on lines +204 to +210
{
keybind: Keybind.parse("ctrl+f")[0],
title: "Favorite",
onTrigger: (option) => {
local.model.toggleFavorite(option.value as { providerID: string; modelID: string })
},
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Verify type safety of option.value.

The type assertion option.value as { providerID: string; modelID: string } could be unsafe if the option's value doesn't match the expected shape.

The DialogSelect component is generic (DialogSelect<T>), and while all options in this dialog appear to have the correct shape, the type assertion bypasses TypeScript's type checking. Consider one of these approaches:

Option 1: Type the DialogSelect with a specific type

-  const [ref, setRef] = createSignal<DialogSelectRef<unknown>>()
+  type ModelOption = { providerID: string; modelID: string }
+  const [ref, setRef] = createSignal<DialogSelectRef<ModelOption>>()

Then update the DialogSelect usage:

-  return (
-    <DialogSelect
+  return (
+    <DialogSelect<ModelOption>
       keybind={[
         ...
         {
           keybind: Keybind.parse("ctrl+f")[0],
           title: "Favorite",
-          onTrigger: (option) => {
-            local.model.toggleFavorite(option.value as { providerID: string; modelID: string })
+          onTrigger: (option) => {
+            local.model.toggleFavorite(option.value)
           },
         },

Option 2: Add runtime validation

         {
           keybind: Keybind.parse("ctrl+f")[0],
           title: "Favorite",
           onTrigger: (option) => {
+            const value = option.value as any
+            if (typeof value?.providerID === 'string' && typeof value?.modelID === 'string') {
-              local.model.toggleFavorite(option.value as { providerID: string; modelID: string })
+              local.model.toggleFavorite(value)
+            }
           },
         },
🤖 Prompt for AI Agents
In packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx around lines 204
to 210, the code unsafely asserts option.value as { providerID: string; modelID:
string }; instead, make the DialogSelect usage type-safe by specifying the
generic type parameter (e.g., DialogSelect<{ providerID: string; modelID: string
}>) so option.value is correctly typed and remove the assertion, or if you
prefer runtime safety, validate option.value before use (check that providerID
and modelID are strings) and handle the unexpected shape (log/error/no-op)
rather than casting.

…rite recents, instead of counting the favorites against the recents if a model is both
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/opencode/src/cli/cmd/tui/context/local.tsx (1)

250-250: Add delimiter in uniqueBy key generation.

The uniqueBy predicate concatenates providerID and modelID without a delimiter, which can cause collisions (e.g., {providerID: "ab", modelID: "cdef"} and {providerID: "abc", modelID: "def"} both produce "abcdef").

Apply this diff to use a delimiter at line 250:

-          const uniq = uniqueBy([next, ...modelStore.recent], (x) => x.providerID + x.modelID)
+          const uniq = uniqueBy([next, ...modelStore.recent], (x) => `${x.providerID}/${x.modelID}`)

And at line 267:

-              const uniq = uniqueBy([model, ...modelStore.recent], (x) => x.providerID + x.modelID)
+              const uniq = uniqueBy([model, ...modelStore.recent], (x) => `${x.providerID}/${x.modelID}`)

Also applies to: 267-267

🧹 Nitpick comments (2)
packages/opencode/src/cli/cmd/tui/context/local.tsx (2)

228-254: Consider extracting shared recent-update logic.

The logic at lines 249-253 (updating model, adding to recent, and saving) duplicates the same logic in the set() method (lines 265-270). This violates DRY.

You could refactor by:

  1. Extracting the recent-update logic into a shared helper function
  2. Or having cycleFavorite call set(next, { recent: true }) after line 248

Example refactor (option 2):

           const next = favorites[index]
           if (!next) return
-          setModelStore("model", agent.current().name, { ...next })
-          const uniq = uniqueBy([next, ...modelStore.recent], (x) => x.providerID + x.modelID)
-          if (uniq.length > 10) uniq.pop()
-          setModelStore("recent", uniq)
-          save()
+          this.set(next, { recent: true })

Note: This would add a redundant isModelValid check, since cycleFavorite already filters for valid models at line 229. If performance is a concern, option 1 (extracting a helper) would be cleaner.


130-138: Consider awaiting Bun.write or documenting fire-and-forget behavior.

The save() function calls Bun.write() without awaiting the returned Promise. This means:

  • Write errors will be silently ignored
  • The function returns before persistence completes
  • Rapid successive calls could theoretically interleave (though the event loop will serialize them)

If fire-and-forget is intentional (given the guidelines to avoid try/catch), this is acceptable. Otherwise, consider:

-      function save() {
-        Bun.write(
+      async function save() {
+        await Bun.write(
           file,
           JSON.stringify({
             recent: modelStore.recent,
             favorite: modelStore.favorite,
           }),
         )
       }

Then update call sites (lines 253, 270, 291) to await or handle the Promise.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e747767 and 13b2839.

📒 Files selected for processing (1)
  • packages/opencode/src/cli/cmd/tui/context/local.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: DO NOT do unnecessary destructuring of variables
DO NOT use else statements unless necessary
DO NOT use try/catch if it can be avoided
AVOID try/catch where possible
AVOID let statements
PREFER single word variable names where possible
Use as many Bun APIs as possible like Bun.file()

Files:

  • packages/opencode/src/cli/cmd/tui/context/local.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

AVOID using any type

Files:

  • packages/opencode/src/cli/cmd/tui/context/local.tsx
packages/opencode/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/opencode/AGENTS.md)

Use Bun with TypeScript ESM modules as the runtime environment

Files:

  • packages/opencode/src/cli/cmd/tui/context/local.tsx
packages/opencode/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/opencode/AGENTS.md)

packages/opencode/**/*.{ts,tsx}: Use relative imports for local modules with named imports preferred
Use Zod schemas for validation
Use TypeScript interfaces for defining data structures
Use camelCase for variable and function names
Use PascalCase for class and namespace names
Use Result patterns for error handling; avoid throwing exceptions in tools
Organize code using namespace-based structure (e.g., Tool.define(), Session.create())
Pass sessionID in tool context and use App.provide() for dependency injection
Validate all inputs with Zod schemas
Use Log.create({ service: "name" }) pattern for logging
Use Storage namespace for data persistence

Files:

  • packages/opencode/src/cli/cmd/tui/context/local.tsx
🧠 Learnings (1)
📚 Learning: 2025-11-24T22:47:41.129Z
Learnt from: CR
Repo: kcrommett/opencode PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2025-11-24T22:47:41.129Z
Learning: Applies to packages/opencode/**/*.{ts,tsx} : Use `Storage` namespace for data persistence

Applied to files:

  • packages/opencode/src/cli/cmd/tui/context/local.tsx

@shuv1337 shuv1337 force-pushed the add-model-favorites branch from c6e3cbc to 13b2839 Compare November 25, 2025 10:05
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (1)

204-210: Avoid unsafe cast for option.value; type DialogSelect instead.

onTrigger currently does:

onTrigger: (option) => {
  local.model.toggleFavorite(option.value as { providerID: string; modelID: string })
}

This bypasses type safety. Since DialogSelect is generic, prefer giving it a concrete value type (e.g., ModelOption = { providerID: string; modelID: string }) and using DialogSelect<ModelOption> plus DialogSelectRef<ModelOption>, so option.value is correctly typed and you can drop the assertion. This keeps the handler safer and aligns with the existing option construction.

Based on learnings, prefer strong typing over casts in TSX.

🧹 Nitpick comments (1)
packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (1)

22-121: Consider de‑duping only against displayed recents to avoid “hidden” models.

In the provider‑model pipeline, the final filter checks membership against orderedRecents:

const inRecents = orderedRecents.some(
  (item) => item.providerID === value.providerID && item.modelID === value.modelID,
)
if (inRecents) return false

But the “Recent” section actually renders only orderedRecentList (top 5, favorites excluded). As a result, models that are in recents but outside that top 5 disappear from the default dialog view (neither in “Recent” nor in the provider list) and are only reachable once the user types a query.

If you just want to de‑duplicate against visibly rendered recents, consider checking against orderedRecentList (or recentList) instead of orderedRecents, so older recents still appear under their provider when no query is active.

Also applies to: 135-174

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13b2839 and 878b598.

⛔ Files ignored due to path filters (1)
  • packages/sdk/js/src/gen/types.gen.ts is excluded by !**/gen/**
📒 Files selected for processing (2)
  • packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (6 hunks)
  • packages/opencode/src/config/config.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: DO NOT do unnecessary destructuring of variables
DO NOT use else statements unless necessary
DO NOT use try/catch if it can be avoided
AVOID try/catch where possible
AVOID let statements
PREFER single word variable names where possible
Use as many Bun APIs as possible like Bun.file()

Files:

  • packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
  • packages/opencode/src/config/config.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

AVOID using any type

Files:

  • packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
  • packages/opencode/src/config/config.ts
packages/opencode/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/opencode/AGENTS.md)

Use Bun with TypeScript ESM modules as the runtime environment

Files:

  • packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
  • packages/opencode/src/config/config.ts
packages/opencode/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/opencode/AGENTS.md)

packages/opencode/**/*.{ts,tsx}: Use relative imports for local modules with named imports preferred
Use Zod schemas for validation
Use TypeScript interfaces for defining data structures
Use camelCase for variable and function names
Use PascalCase for class and namespace names
Use Result patterns for error handling; avoid throwing exceptions in tools
Organize code using namespace-based structure (e.g., Tool.define(), Session.create())
Pass sessionID in tool context and use App.provide() for dependency injection
Validate all inputs with Zod schemas
Use Log.create({ service: "name" }) pattern for logging
Use Storage namespace for data persistence

Files:

  • packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
  • packages/opencode/src/config/config.ts
🧠 Learnings (1)
📚 Learning: 2025-11-24T22:47:17.581Z
Learnt from: CR
Repo: kcrommett/opencode PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T22:47:17.581Z
Learning: Applies to **/*.{ts,tsx} : AVOID using `any` type

Applied to files:

  • packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx
🧬 Code graph analysis (1)
packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx (1)
packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx (1)
  • filter (149-151)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: update
🔇 Additional comments (1)
packages/opencode/src/config/config.ts (1)

431-432: New favorite‑cycle keybinds align with existing schema pattern.

The added model_cycle_favorite and model_cycle_favorite_reverse entries follow the existing keybind conventions (optional string, "none" default, clear descriptions) and should integrate cleanly with the new TUI commands and SDK updates.

@shuv1337 shuv1337 closed this Nov 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants