diff --git a/.opencode/agent/test-max-thinking.md b/.opencode/agent/test-max-thinking.md new file mode 100644 index 00000000000..0dc5e9ab6b2 --- /dev/null +++ b/.opencode/agent/test-max-thinking.md @@ -0,0 +1,9 @@ +--- +description: Test agent with max thinking. +model: zai-coding-plan/glm-4.7 +variant: max +--- + +You are a test agent for GLM-4.7 with max thinking enabled. + +Your purpose is to test model variants in this codebase. Keep responses brief and direct. diff --git a/.opencode/agent/test-no-thinking.md b/.opencode/agent/test-no-thinking.md new file mode 100644 index 00000000000..7e7166cc9bc --- /dev/null +++ b/.opencode/agent/test-no-thinking.md @@ -0,0 +1,9 @@ +--- +description: Test agent with no thinking. +model: zai-coding-plan/glm-4.7 +variant: none +--- + +You are a test agent for GLM-4.7 with thinking disabled. + +Your purpose is to test model variants in this codebase. Keep responses brief and direct. diff --git a/.opencode/opencode.jsonc b/.opencode/opencode.jsonc index 5d2dec625c6..c3f0b7070d1 100644 --- a/.opencode/opencode.jsonc +++ b/.opencode/opencode.jsonc @@ -4,7 +4,6 @@ // "enterprise": { // "url": "https://enterprise.dev.opencode.ai", // }, - "instructions": ["STYLE_GUIDE.md"], "provider": { "opencode": { "options": {}, diff --git a/.opencode/remember.jsonc b/.opencode/remember.jsonc new file mode 100644 index 00000000000..42d86610f85 --- /dev/null +++ b/.opencode/remember.jsonc @@ -0,0 +1,16 @@ +{ + // Enable or disable the plugin + "enabled": true, + // Where to store memories: "global", "project", or "both" + // - "global": ~/.config/opencode/memory/memories.sqlite (shared across projects) + // - "project": .opencode/memory/memories.sqlite (project-specific) + // - "both": search both, save to project + "scope": "project", + // Memory injection settings + "inject": { + // Number of memories to inject after user messages (default: 5) + "count": 5, + // Score threshold for [important] vs [related] tag (default: 0.6) + "highThreshold": 0.6 + } +} diff --git a/AGENTS.md b/AGENTS.md index 3138f6c5ece..6619861c2aa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,75 @@ -- To test opencode in `packages/opencode`, run `bun dev`. - To regenerate the JavaScript SDK, run `./packages/sdk/js/script/build.ts`. - ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE. - The default branch in this repo is `dev`. + +## Style Guide + +- Keep things in one function unless composable or reusable +- Avoid unnecessary destructuring. Instead of `const { a, b } = obj`, use `obj.a` and `obj.b` to preserve context +- Avoid `try`/`catch` where possible +- Avoid using the `any` type +- Prefer single word variable names where possible +- Use Bun APIs when possible, like `Bun.file()` + +# Avoid let statements + +We don't like `let` statements, especially combined with if/else statements. +Prefer `const`. + +Good: + +```ts +const foo = condition ? 1 : 2 +``` + +Bad: + +```ts +let foo + +if (condition) foo = 1 +else foo = 2 +``` + +# Avoid else statements + +Prefer early returns or using an `iife` to avoid else statements. + +Good: + +```ts +function foo() { + if (condition) return 1 + return 2 +} +``` + +Bad: + +```ts +function foo() { + if (condition) return 1 + else return 2 +} +``` + +# Prefer single word naming + +Try your best to find a single word name for your variables, functions, etc. +Only use multiple words if you cannot. + +Good: + +```ts +const foo = 1 +const bar = 2 +const baz = 3 +``` + +Bad: + +```ts +const fooBar = 1 +const barBaz = 2 +const bazFoo = 3 +``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a32504b22f8..60b76a95e9f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -148,7 +148,7 @@ This runs `bun run --cwd packages/desktop build` automatically via Tauri’s `be > [!NOTE] > If you make changes to the API or SDK (e.g. `packages/opencode/src/server/server.ts`), run `./script/generate.ts` to regenerate the SDK and related files. -Please try to follow the [style guide](./STYLE_GUIDE.md) +Please try to follow the [style guide](./AGENTS.md) ### Setting up a Debugger diff --git a/STATS.md b/STATS.md index 6ecb1606b26..a8be3156512 100644 --- a/STATS.md +++ b/STATS.md @@ -210,3 +210,4 @@ | 2026-01-22 | 5,766,340 (+321,498) | 2,029,487 (+66,956) | 7,795,827 (+388,454) | | 2026-01-23 | 6,096,236 (+329,896) | 2,096,235 (+66,748) | 8,192,471 (+396,644) | | 2026-01-24 | 6,371,019 (+274,783) | 2,156,870 (+60,635) | 8,527,889 (+335,418) | +| 2026-01-25 | 6,639,082 (+268,063) | 2,187,853 (+30,983) | 8,826,935 (+299,046) | diff --git a/STYLE_GUIDE.md b/STYLE_GUIDE.md deleted file mode 100644 index 52d012fcb97..00000000000 --- a/STYLE_GUIDE.md +++ /dev/null @@ -1,71 +0,0 @@ -## Style Guide - -- Keep things in one function unless composable or reusable -- Avoid unnecessary destructuring. Instead of `const { a, b } = obj`, use `obj.a` and `obj.b` to preserve context -- Avoid `try`/`catch` where possible -- Avoid using the `any` type -- Prefer single word variable names where possible -- Use Bun APIs when possible, like `Bun.file()` - -# Avoid let statements - -We don't like `let` statements, especially combined with if/else statements. -Prefer `const`. - -Good: - -```ts -const foo = condition ? 1 : 2 -``` - -Bad: - -```ts -let foo - -if (condition) foo = 1 -else foo = 2 -``` - -# Avoid else statements - -Prefer early returns or using an `iife` to avoid else statements. - -Good: - -```ts -function foo() { - if (condition) return 1 - return 2 -} -``` - -Bad: - -```ts -function foo() { - if (condition) return 1 - else return 2 -} -``` - -# Prefer single word naming - -Try your best to find a single word name for your variables, functions, etc. -Only use multiple words if you cannot. - -Good: - -```ts -const foo = 1 -const bar = 2 -const baz = 3 -``` - -Bad: - -```ts -const fooBar = 1 -const barBaz = 2 -const bazFoo = 3 -``` diff --git a/bun.lock b/bun.lock index 58cbbf12b6c..d9eeddd615a 100644 --- a/bun.lock +++ b/bun.lock @@ -23,7 +23,7 @@ }, "packages/app": { "name": "@opencode-ai/app", - "version": "1.1.35", + "version": "1.1.36", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -73,7 +73,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.1.35", + "version": "1.1.36", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -107,7 +107,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.1.35", + "version": "1.1.36", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -134,7 +134,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.1.35", + "version": "1.1.36", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -158,7 +158,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.1.35", + "version": "1.1.36", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -182,7 +182,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.1.35", + "version": "1.1.36", "dependencies": { "@opencode-ai/app": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -211,7 +211,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.1.35", + "version": "1.1.36", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -240,7 +240,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.1.35", + "version": "1.1.36", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -256,7 +256,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.1.35", + "version": "1.1.36", "bin": { "opencode": "./bin/opencode", }, @@ -360,7 +360,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.1.35", + "version": "1.1.36", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -380,7 +380,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.1.35", + "version": "1.1.36", "devDependencies": { "@hey-api/openapi-ts": "0.90.10", "@tsconfig/node22": "catalog:", @@ -391,7 +391,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.1.35", + "version": "1.1.36", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -404,7 +404,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.1.35", + "version": "1.1.36", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -446,7 +446,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.1.35", + "version": "1.1.36", "dependencies": { "zod": "catalog:", }, @@ -457,7 +457,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.1.35", + "version": "1.1.36", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/app/package.json b/packages/app/package.json index e10c23eeba1..8284234c5cb 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/app", - "version": "1.1.35", + "version": "1.1.36", "description": "", "type": "module", "exports": { diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 074a035829e..5ec0eb1ea04 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -137,6 +137,8 @@ export const PromptInput: Component = (props) => { let scrollRef!: HTMLDivElement let slashPopoverRef!: HTMLDivElement + const mirror = { input: false } + const scrollCursorIntoView = () => { const container = scrollRef const selection = window.getSelection() @@ -183,23 +185,26 @@ export const PromptInput: Component = (props) => { const openComment = (item: { path: string; commentID?: string; commentOrigin?: "review" | "file" }) => { if (!item.commentID) return - comments.setFocus({ file: item.path, id: item.commentID }) - comments.setActive({ file: item.path, id: item.commentID }) + const focus = { file: item.path, id: item.commentID } + comments.setActive(focus) view().reviewPanel.open() if (item.commentOrigin === "review") { tabs().open("review") + requestAnimationFrame(() => comments.setFocus(focus)) return } if (item.commentOrigin !== "file" && commentInReview(item.path)) { tabs().open("review") + requestAnimationFrame(() => comments.setFocus(focus)) return } const tab = files.tab(item.path) tabs().open(tab) files.load(item.path) + requestAnimationFrame(() => comments.setFocus(focus)) } const recent = createMemo(() => { @@ -651,6 +656,25 @@ export const PromptInput: Component = (props) => { () => prompt.current(), (currentParts) => { const inputParts = currentParts.filter((part) => part.type !== "image") as Prompt + + if (mirror.input) { + mirror.input = false + if (isNormalizedEditor()) return + + const selection = window.getSelection() + let cursorPosition: number | null = null + if (selection && selection.rangeCount > 0 && editorRef.contains(selection.anchorNode)) { + cursorPosition = getCursorPosition(editorRef) + } + + renderEditor(inputParts) + + if (cursorPosition !== null) { + setCursorPosition(editorRef, cursorPosition) + } + return + } + const domParts = parseFromDOM() if (isNormalizedEditor() && isPromptEqual(inputParts, domParts)) return @@ -765,6 +789,7 @@ export const PromptInput: Component = (props) => { setStore("savedPrompt", null) } if (prompt.dirty()) { + mirror.input = true prompt.set(DEFAULT_PROMPT, 0) } queueScroll() @@ -795,6 +820,7 @@ export const PromptInput: Component = (props) => { setStore("savedPrompt", null) } + mirror.input = true prompt.set([...rawParts, ...images], cursorPosition) queueScroll() } diff --git a/packages/app/src/components/session-context-usage.tsx b/packages/app/src/components/session-context-usage.tsx index cd43c33c1c3..4dbb9e04898 100644 --- a/packages/app/src/components/session-context-usage.tsx +++ b/packages/app/src/components/session-context-usage.tsx @@ -26,13 +26,17 @@ export function SessionContextUsage(props: SessionContextUsageProps) { const view = createMemo(() => layout.view(sessionKey)) const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : [])) + const usd = createMemo( + () => + new Intl.NumberFormat(language.locale(), { + style: "currency", + currency: "USD", + }), + ) + const cost = createMemo(() => { - const locale = language.locale() const total = messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0) - return new Intl.NumberFormat(locale, { - style: "currency", - currency: "USD", - }).format(total) + return usd().format(total) }) const context = createMemo(() => { diff --git a/packages/app/src/components/session/session-context-tab.tsx b/packages/app/src/components/session/session-context-tab.tsx index 4c672af3e30..37733caff63 100644 --- a/packages/app/src/components/session/session-context-tab.tsx +++ b/packages/app/src/components/session/session-context-tab.tsx @@ -26,6 +26,14 @@ export function SessionContextTab(props: SessionContextTabProps) { const sync = useSync() const language = useLanguage() + const usd = createMemo( + () => + new Intl.NumberFormat(language.locale(), { + style: "currency", + currency: "USD", + }), + ) + const ctx = createMemo(() => { const last = findLast(props.messages(), (x) => { if (x.role !== "assistant") return false @@ -62,12 +70,8 @@ export function SessionContextTab(props: SessionContextTabProps) { }) const cost = createMemo(() => { - const locale = language.locale() const total = props.messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0) - return new Intl.NumberFormat(locale, { - style: "currency", - currency: "USD", - }).format(total) + return usd().format(total) }) const counts = createMemo(() => { diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 808fbb11242..e4a64b3390a 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -5,8 +5,6 @@ import { useParams } from "@solidjs/router" import { useLayout } from "@/context/layout" import { useCommand } from "@/context/command" import { useLanguage } from "@/context/language" -// import { useServer } from "@/context/server" -// import { useDialog } from "@opencode-ai/ui/context/dialog" import { usePlatform } from "@/context/platform" import { useSync } from "@/context/sync" import { useGlobalSDK } from "@/context/global-sdk" @@ -256,7 +254,7 @@ export function SessionHeader() { >