Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/opencode/test/fixture/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export async function tmpdir<T>(options?: TmpDirOptions<T>) {
const result = {
[Symbol.asyncDispose]: async () => {
await options?.dispose?.(dirpath)
// await fs.rm(dirpath, { recursive: true, force: true })
await fs.rm(dirpath, { recursive: true, force: true })
},
path: realpath,
extra: extra as T,
Expand Down
127 changes: 70 additions & 57 deletions packages/opencode/test/lsp/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,72 +24,85 @@ describe("LSPClient interop", () => {
test("handles workspace/workspaceFolders request", async () => {
const handle = spawnFakeServer() as any

const client = await Instance.provide({
directory: process.cwd(),
fn: () =>
LSPClient.create({
serverID: "fake",
server: handle as unknown as LSPServer.Handle,
root: process.cwd(),
}),
})

await client.connection.sendNotification("test/trigger", {
method: "workspace/workspaceFolders",
})

await new Promise((r) => setTimeout(r, 100))

expect(client.connection).toBeDefined()

await client.shutdown()
try {
const client = await Instance.provide({
directory: process.cwd(),
fn: () =>
LSPClient.create({
serverID: "fake",
server: handle as unknown as LSPServer.Handle,
root: process.cwd(),
}),
})

await client.connection.sendNotification("test/trigger", {
method: "workspace/workspaceFolders",
})

await new Promise((r) => setTimeout(r, 100))

expect(client.connection).toBeDefined()

await client.shutdown()
} finally {
// Ensure fake LSP server process is always killed, even if test fails
handle.process.kill()
}
})

test("handles client/registerCapability request", async () => {
const handle = spawnFakeServer() as any

const client = await Instance.provide({
directory: process.cwd(),
fn: () =>
LSPClient.create({
serverID: "fake",
server: handle as unknown as LSPServer.Handle,
root: process.cwd(),
}),
})

await client.connection.sendNotification("test/trigger", {
method: "client/registerCapability",
})

await new Promise((r) => setTimeout(r, 100))

expect(client.connection).toBeDefined()

await client.shutdown()
try {
const client = await Instance.provide({
directory: process.cwd(),
fn: () =>
LSPClient.create({
serverID: "fake",
server: handle as unknown as LSPServer.Handle,
root: process.cwd(),
}),
})

await client.connection.sendNotification("test/trigger", {
method: "client/registerCapability",
})

await new Promise((r) => setTimeout(r, 100))

expect(client.connection).toBeDefined()

await client.shutdown()
} finally {
handle.process.kill()
}
})

test("handles client/unregisterCapability request", async () => {
const handle = spawnFakeServer() as any

const client = await Instance.provide({
directory: process.cwd(),
fn: () =>
LSPClient.create({
serverID: "fake",
server: handle as unknown as LSPServer.Handle,
root: process.cwd(),
}),
})

await client.connection.sendNotification("test/trigger", {
method: "client/unregisterCapability",
})

await new Promise((r) => setTimeout(r, 100))

expect(client.connection).toBeDefined()

await client.shutdown()
try {
const client = await Instance.provide({
directory: process.cwd(),
fn: () =>
LSPClient.create({
serverID: "fake",
server: handle as unknown as LSPServer.Handle,
root: process.cwd(),
}),
})

await client.connection.sendNotification("test/trigger", {
method: "client/unregisterCapability",
})

await new Promise((r) => setTimeout(r, 100))

expect(client.connection).toBeDefined()

await client.shutdown()
} finally {
handle.process.kill()
}
})
})
1 change: 1 addition & 0 deletions packages/web/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default defineConfig({
},
sidebar: [
"",
"get-started",
"config",
"providers",
"network",
Expand Down
146 changes: 146 additions & 0 deletions packages/web/src/content/docs/docs/get-started.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
---
title: Get Started
description: Set up your development environment
---

OpenCode is an open source AI coding agent. It helps you write code, understand codebases, and build features faster.

---

## Prerequisites

You need **Bun 1.3+** to develop OpenCode. Bun is a fast JavaScript runtime and package manager that the project uses.

---

## Install dependencies

Install all project dependencies from the root directory.

```bash
bun install
```

This will install packages across all workspaces using the monorepo structure.

---

## Start development

Run the development server to start working on OpenCode.

```bash
bun dev
```

This starts OpenCode in the `packages/opencode` directory by default.

---

## Run against a different directory

You can run OpenCode against any directory or repository.

```bash
bun dev <directory>
```

To run it in the root of the opencode repo itself.

```bash
bun dev .
```

---

## Build a standalone executable

Compile OpenCode into a standalone binary.

```bash
./packages/opencode/script/build.ts --single
```

Then run it from the dist folder.

```bash
./packages/opencode/dist/opencode-<platform>/bin/opencode
```

Replace `<platform>` with your system (e.g., `darwin-arm64`, `linux-x64`).

---

## Test the web app

Test UI changes using the web development server.

```bash
bun run --cwd packages/app dev
```

This starts a local dev server at `http://localhost:5173`.

---

## Run the desktop app

The desktop app is a native Tauri application wrapping the web UI.

```bash
bun run --cwd packages/desktop tauri dev
```

This opens the native window with a dev server at `http://localhost:1420`.

---

## Project structure

The codebase is organized into several key packages:

- `packages/opencode` - Core business logic and server
- `packages/opencode/src/cli/cmd/tui/` - Terminal UI code in SolidJS
- `packages/app` - Shared web UI components in SolidJS
- `packages/desktop` - Native desktop app with Tauri
- `packages/plugin` - Plugin source for `@opencode-ai/plugin`

---

## Regenerate the SDK

If you make changes to the API or server code, regenerate the SDK.

```bash
./script/generate.ts
```

This updates the SDK and related files automatically.

---

## Type checking

Run TypeScript type checking across the entire project.

```bash
bun turbo typecheck
```

This checks all packages in the monorepo for type errors.

---

## Contributing

We welcome contributions! Read the [contributing docs](../../CONTRIBUTING.md) before submitting a pull request.

Look for issues labeled `help wanted`, `good first issue`, `bug`, or `perf` to get started.

---

## Style guide

Follow our [style guide](../../STYLE_GUIDE.md) when contributing to the codebase.

Key points: prefer single-word variables, avoid `let` and `else` statements, use Bun APIs when possible.