opencode-miniterm 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -1,8 +1,9 @@
1
- # OpenCode MiniTerm - Agent Guidelines
1
+ # OpenCode Miniterm - Agent Guidelines
2
2
 
3
3
  ## Build & Development Commands
4
4
 
5
5
  ### Run the application
6
+
6
7
  ```bash
7
8
  bun run src/index.ts
8
9
  # Or just:
@@ -10,46 +11,55 @@ bun src/index.ts
10
11
  ```
11
12
 
12
13
  ### Build (when bundler is added)
14
+
13
15
  ```bash
14
16
  bun build src/index.ts --outdir dist
15
17
  ```
16
18
 
17
19
  ### Testing
20
+
18
21
  No test framework is currently configured. Add one of these to package.json:
22
+
19
23
  - **Bun Test**: `bun test` (recommended - built-in, fast)
20
24
  - **Jest**: `npm test` or `bun run test`
21
25
  - **Vitest**: `vitest`
22
26
 
23
27
  To run a single test (once configured):
28
+
24
29
  - Bun Test: `bun test --test-name-pattern "testName"`
25
30
  - Jest: `npm test -- testName`
26
31
  - Vitest: `vitest run testName`
27
32
 
28
33
  ### Linting & Formatting (recommended additions)
34
+
29
35
  Install and configure these tools:
36
+
30
37
  ```bash
31
38
  bun add -d eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser prettier
32
39
  ```
33
40
 
34
41
  Commands to add to package.json:
42
+
35
43
  ```json
36
44
  {
37
- "lint": "eslint src --ext .ts",
38
- "lint:fix": "eslint src --ext .ts --fix",
39
- "format": "prettier --write \"src/**/*.ts\"",
40
- "format:check": "prettier --check \"src/**/*.ts\"",
41
- "typecheck": "tsc --noEmit"
45
+ "lint": "eslint src --ext .ts",
46
+ "lint:fix": "eslint src --ext .ts --fix",
47
+ "format": "prettier --write \"src/**/*.ts\"",
48
+ "format:check": "prettier --check \"src/**/*.ts\"",
49
+ "typecheck": "tsc --noEmit"
42
50
  }
43
51
  ```
44
52
 
45
53
  ## Code Style Guidelines
46
54
 
47
55
  ### TypeScript Configuration
56
+
48
57
  - Use strict mode: `"strict": true` in tsconfig.json
49
58
  - Target ES2022+ for modern Node/Bun features
50
59
  - Use `moduleResolution: "bundler"` for Bun compatibility
51
60
 
52
61
  ### Imports
62
+
53
63
  - Use ES6 imports (ESM): `import { something } from 'module'`
54
64
  - Group imports in this order:
55
65
  1. Node/Bun built-ins
@@ -59,6 +69,7 @@ Commands to add to package.json:
59
69
  - Avoid default exports; prefer named exports for better tree-shaking
60
70
 
61
71
  ### Formatting
72
+
62
73
  - Use 2 spaces for indentation
63
74
  - Use single quotes for strings
64
75
  - Use semicolons at end of statements
@@ -67,6 +78,7 @@ Commands to add to package.json:
67
78
  - Spaces around operators: `a = b + c` not `a=b+c`
68
79
 
69
80
  ### Types & Type Safety
81
+
70
82
  - Always provide explicit return types for functions
71
83
  - Use `interface` for object shapes, `type` for unions/primitives
72
84
  - Avoid `any`; use `unknown` when type is truly unknown
@@ -74,6 +86,7 @@ Commands to add to package.json:
74
86
  - Leverage Bun's built-in type definitions (from `bun-types`)
75
87
 
76
88
  ### Naming Conventions
89
+
77
90
  - **Files**: kebab-case: `my-service.ts`
78
91
  - **Variables/Functions**: camelCase: `myFunction`
79
92
  - **Classes**: PascalCase: `MyService`
@@ -82,14 +95,18 @@ Commands to add to package.json:
82
95
  - **Types/Interfaces**: PascalCase, often with suffixes: `UserService`, `ConfigOptions`
83
96
 
84
97
  ### Error Handling
98
+
85
99
  - Use try/catch for async operations
86
100
  - Create custom error classes for domain-specific errors:
87
101
  ```ts
88
102
  class TerminalError extends Error {
89
- constructor(message: string, public code: string) {
90
- super(message);
91
- this.name = 'TerminalError';
92
- }
103
+ constructor(
104
+ message: string,
105
+ public code: string,
106
+ ) {
107
+ super(message);
108
+ this.name = "TerminalError";
109
+ }
93
110
  }
94
111
  ```
95
112
  - Always include error context in error messages
@@ -97,24 +114,28 @@ Commands to add to package.json:
97
114
  - Never swallow errors silently
98
115
 
99
116
  ### Async/Promise Handling
117
+
100
118
  - Use async/await over .then()/.catch()
101
119
  - Handle promise rejections: `process.on('unhandledRejection')`
102
120
  - Use Bun's optimized APIs where available (e.g., `Bun.file()`)
103
121
  - Implement timeouts for network requests
104
122
 
105
123
  ### Code Organization
124
+
106
125
  - Structure by feature/domain, not by file type
107
126
  - Keep files focused: one responsibility per file
108
127
  - Export at file end; avoid export分散
109
128
  - Use barrel files (`index.ts`) for cleaner imports
110
129
 
111
130
  ### Comments
131
+
112
132
  - Use JSDoc for public APIs: `/** @description ... */`
113
133
  - Comment WHY, not WHAT
114
134
  - Keep comments current with code changes
115
135
  - Avoid inline comments for obvious logic
116
136
 
117
137
  ### Performance (Bun-Specific)
138
+
118
139
  - Leverage Bun's fast I/O: `Bun.write()`, `Bun.file()`
119
140
  - Use `TextEncoder`/`TextDecoder` for encoding
120
141
  - Prefer native over polyfills
@@ -123,6 +144,7 @@ Commands to add to package.json:
123
144
  ## Project Context
124
145
 
125
146
  This is an alternative terminal UI for OpenCode. Focus on:
147
+
126
148
  - Fast, responsive terminal rendering
127
149
  - Clean CLI UX with good error messages
128
150
  - Efficient resource usage (memory/CPU)
@@ -138,6 +160,7 @@ This is an alternative terminal UI for OpenCode. Focus on:
138
160
  ## OpenCode Server Integration
139
161
 
140
162
  ### Starting the Server
163
+
141
164
  - Use `opencode serve` to start a headless HTTP server (not `opencode server`)
142
165
  - Default URL: `http://127.0.0.1:4096` (port may vary, can be 0/random)
143
166
  - Server requires 2-3 seconds to initialize before accepting requests
@@ -145,6 +168,7 @@ This is an alternative terminal UI for OpenCode. Focus on:
145
168
  - Always handle SIGINT to properly shut down the server process
146
169
 
147
170
  ### Authentication
171
+
148
172
  - Server may require HTTP Basic Auth if `OPENCODE_SERVER_PASSWORD` is set
149
173
  - Username: `OPENCODE_SERVER_USERNAME` env var (default: 'opencode')
150
174
  - Password: `OPENCODE_SERVER_PASSWORD` env var (required if server has password set)
@@ -153,6 +177,7 @@ This is an alternative terminal UI for OpenCode. Focus on:
153
177
  - Check env vars at startup: `echo $OPENCODE_SERVER_PASSWORD` to verify it's set
154
178
 
155
179
  ### Creating Sessions
180
+
156
181
  ```ts
157
182
  POST /session
158
183
  Headers: { "Content-Type": "application/json", "Authorization": "Basic <creds>" }
@@ -161,14 +186,17 @@ Response: { id: string, title?: string, ... }
161
186
  ```
162
187
 
163
188
  ### Getting Available Models
189
+
164
190
  ```ts
165
191
  GET /config/providers
166
192
  Headers: { "Authorization": "Basic <creds>" }
167
193
  Response: { providers: Provider[], default: { [key: string]: string } }
168
194
  ```
195
+
169
196
  Note: `/models` endpoint returns HTML documentation, not JSON. Use `/config/providers` for programmatic access.
170
197
 
171
198
  ### Sending Messages
199
+
172
200
  ```ts
173
201
  POST /session/:id/message
174
202
  Headers: { "Content-Type": "application/json", "Authorization": "Basic <creds>" }
@@ -183,6 +211,7 @@ Response: { info: Message, parts: Part[] }
183
211
  ```
184
212
 
185
213
  ### Getting Session Messages
214
+
186
215
  ```ts
187
216
  GET /session/:id/message
188
217
  Headers: { "Authorization": "Basic <creds>" }
@@ -190,20 +219,24 @@ Response: { info: Message, parts: Part[] }[]
190
219
  ```
191
220
 
192
221
  ### Undoing Messages (Revert)
222
+
193
223
  ```ts
194
224
  POST /session/:id/revert
195
225
  Headers: { "Content-Type": "application/json", "Authorization": "Basic <creds>" }
196
226
  Body: { messageID: string, partID?: string }
197
227
  Response: { id: string, revert: { messageID, snapshot, diff } }
198
228
  ```
229
+
199
230
  Typically used to undo the last assistant message by fetching messages first, then reverting the last one.
200
231
 
201
232
  **IMPORTANT**: The `model` field is required when sending messages. Without it, the request will hang indefinitely. Get available models from `GET /config/providers` or `GET /models`. Common models:
233
+
202
234
  - `big-pickle` (opencode provider) - default, high quality
203
235
  - `glm-5-free` (opencode provider) - free GLM model
204
236
  - `gpt-5-nano` (opencode provider) - fast GPT model
205
237
 
206
238
  ### Response Format
239
+
207
240
  - Response has `{ info, parts }` structure
208
241
  - Parts can be: `step-start`, `reasoning`, `text`, `step-finish`, `tool_use`, `tool_result`
209
242
  - `step-start` - Indicates beginning of a thinking/processing step
@@ -215,6 +248,7 @@ Typically used to undo the last assistant message by fetching messages first, th
215
248
  - Display reasoning and text parts to the user for transparency
216
249
 
217
250
  ### Server-Sent Events (SSE)
251
+
218
252
  - Connect to event stream at `/event` for real-time updates
219
253
  - Events include: `message.part.updated`, `session.status`, `session.updated`, `message.updated`, `session.diff`, `session.idle`
220
254
  - Event structure: `{ type: string, properties: {...} }`
@@ -224,6 +258,7 @@ Typically used to undo the last assistant message by fetching messages first, th
224
258
  - Delta updates allow streaming reasoning and text for better UX
225
259
 
226
260
  ### Error Handling
261
+
227
262
  - Server returns 401 Unauthorized when authentication is missing/invalid
228
263
  - Handle connection errors (server may not be ready yet)
229
264
  - Always parse error text from response for debugging
@@ -240,4 +275,4 @@ Typically used to undo the last assistant message by fetching messages first, th
240
275
  - Never commit API keys, tokens, or secrets
241
276
  - Validate all user inputs
242
277
  - Sanitize terminal output to prevent injection
243
- - Use environment variables for configuration
278
+ - Use environment variables for configuration
package/README.md CHANGED
@@ -2,4 +2,167 @@
2
2
 
3
3
  A small front-end terminal UI for [OpenCode](https://github.com/anomalyco/opencode).
4
4
 
5
- This project is not affiliated with OpenCode.
5
+ > **Note:** This project is not affiliated with OpenCode.
6
+
7
+ ## Features
8
+
9
+ - **Slash Commands** - Quick access to common operations
10
+ - **File Auto-Completion** - Type `@` followed by file path for intelligent completions
11
+ - **Real-Time Streaming** - See AI responses as they're being generated
12
+ - **Logging Support** - Optional conversation logging for debugging
13
+ - **Keyboard Navigation** - Readline support with history and editing
14
+
15
+ ## Installation
16
+
17
+ ### Prerequisites
18
+
19
+ - [OpenCode](https://github.com/anomalyco/opencode) - OpenCode server
20
+ - [Bun](https://bun.sh/) - Required runtime
21
+
22
+ ### Install from npm
23
+
24
+ ```bash
25
+ npm install -g opencode-miniterm
26
+ # or
27
+ pnpm add -g opencode-miniterm
28
+ ```
29
+
30
+ ### Install from Source
31
+
32
+ ```bash
33
+ git clone https://github.com/your-repo/opencode-miniterm.git
34
+ cd opencode-miniterm
35
+ bun install
36
+ bun link
37
+ ```
38
+
39
+ ### Quick Start
40
+
41
+ ```bash
42
+ ocmt
43
+ ```
44
+
45
+ This will:
46
+
47
+ 1. Start the OpenCode server (if not already running)
48
+ 2. Create or resume a session for the current directory
49
+ 3. Present the interactive prompt
50
+
51
+ ## Configuration
52
+
53
+ Configuration is stored in `~/.config/opencode-miniterm/opencode-miniterm.json`:
54
+
55
+ ```json
56
+ {
57
+ "providerID": "opencode",
58
+ "modelID": "big-pickle",
59
+ "agentID": "build",
60
+ "sessionIDs": {
61
+ "/path/to/project1": "session-id-1",
62
+ "/path/to/project2": "session-id-2"
63
+ },
64
+ "loggingEnabled": false
65
+ }
66
+ ```
67
+
68
+ ### Environment Variables
69
+
70
+ - `OPENCODE_SERVER_USERNAME` - Server username (default: "opencode")
71
+ - `OPENCODE_SERVER_PASSWORD` - Server password (required if server has auth)
72
+ - `OPENCODE_MT_CONFIG_CONTENT` - Override config as JSON string
73
+
74
+ ## Usage
75
+
76
+ ### Basic Interaction
77
+
78
+ Simply type your question or request at the prompt and press Enter:
79
+
80
+ ```
81
+ > Help me fix the bug in auth.ts
82
+ ```
83
+
84
+ ### Slash Commands
85
+
86
+ | Command | Description |
87
+ | ------------------ | ------------------------------------------- |
88
+ | `/help` | Show available commands |
89
+ | `/init` | Analyze project and create/update AGENTS.md |
90
+ | `/new` | Create a new session |
91
+ | `/sessions` | List and switch sessions |
92
+ | `/diff` | Show file additions and deletions |
93
+ | `/undo` | Undo last assistant request |
94
+ | `/details` | Show detailed info for the previous request |
95
+ | `/page` | Page through the detailed info |
96
+ | `/agents` | Show available agents |
97
+ | `/models` | Show available models |
98
+ | `/log` | Enable/disable logging |
99
+ | `/run <cmd>` | Run a shell command from within miniterm |
100
+ | `/exit` or `/quit` | Exit the application |
101
+
102
+ ### File References
103
+
104
+ Reference files in your conversation using `@` followed by the path:
105
+
106
+ ```
107
+ > Review @src/index.ts and suggest improvements
108
+ ```
109
+
110
+ Tab completion is supported for file paths:
111
+
112
+ ```
113
+ > @sr<tab> → @src/
114
+ > @src/in<tab> → @src/index.ts
115
+ ```
116
+
117
+ ### Keyboard Shortcuts
118
+
119
+ | Key | Action |
120
+ | ---------------------- | ---------------------------- |
121
+ | `↑` / `↓` | Navigate command history |
122
+ | `←` / `→` | Move cursor |
123
+ | `Opt+←` / `Opt+→` | Move by word boundaries |
124
+ | `Tab` | Auto-complete commands/files |
125
+ | `Backspace` / `Delete` | Delete characters |
126
+ | `Esc` | Cancel current request |
127
+ | `Ctrl+C` | Force quit application |
128
+
129
+ ## Session Management
130
+
131
+ OpenCode Miniterm automatically manages sessions per directory:
132
+
133
+ - **First Launch**: Creates a new session for the current directory
134
+ - **Subsequent Launches**: Resumes the last session for that directory
135
+ - **New Session**: Use `/new` to create a fresh session
136
+ - **Switch Sessions**: Use `/sessions` to browse and switch between all your sessions
137
+
138
+ ## Development
139
+
140
+ ### Running Locally
141
+
142
+ ```bash
143
+ bun run dev
144
+ # or
145
+ bun src/index.ts
146
+ ```
147
+
148
+ ### Build
149
+
150
+ ```bash
151
+ bun build src/index.ts --outdir dist
152
+ ```
153
+
154
+ ### Type Check
155
+
156
+ ```bash
157
+ bun run check
158
+ ```
159
+
160
+ ### Formatting
161
+
162
+ ```bash
163
+ bunx prettier --write "**/*.{ts,json,md}"
164
+ ```
165
+
166
+ ## License
167
+
168
+ ISC
package/bun.lock CHANGED
@@ -5,7 +5,7 @@
5
5
  "": {
6
6
  "name": "opencode-miniterm",
7
7
  "dependencies": {
8
- "@opencode-ai/sdk": "^1.2.10",
8
+ "@opencode-ai/sdk": "^1.2.14",
9
9
  "allmark": "^1.0.0",
10
10
  },
11
11
  "devDependencies": {
@@ -47,7 +47,7 @@
47
47
 
48
48
  "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
49
49
 
50
- "@opencode-ai/sdk": ["@opencode-ai/sdk@1.2.14", "", {}, "sha512-nPkWAmzgPJYyfCJAV4NG7HTfN/iuO3B6fv8sT26NhPiR+EqD9i8sh4X1LwI7wEbbMOwWOX1PhrssW6gXQOOQZQ=="],
50
+ "@opencode-ai/sdk": ["@opencode-ai/sdk@1.2.15", "", {}, "sha512-NUJNlyBCdZ4R0EBLjJziEQOp2XbRPJosaMcTcWSWO5XJPKGUpz0u8ql+5cR8K+v2RJ+hp2NobtNwpjEYfe6BRQ=="],
51
51
 
52
52
  "@trivago/prettier-plugin-sort-imports": ["@trivago/prettier-plugin-sort-imports@6.0.2", "", { "dependencies": { "@babel/generator": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "javascript-natural-sort": "^0.7.1", "lodash-es": "^4.17.21", "minimatch": "^9.0.0", "parse-imports-exports": "^0.2.4" }, "peerDependencies": { "@vue/compiler-sfc": "3.x", "prettier": "2.x - 3.x", "prettier-plugin-ember-template-tag": ">= 2.0.0", "prettier-plugin-svelte": "3.x", "svelte": "4.x || 5.x" }, "optionalPeers": ["@vue/compiler-sfc", "prettier-plugin-ember-template-tag", "prettier-plugin-svelte", "svelte"] }, "sha512-3DgfkukFyC/sE/VuYjaUUWoFfuVjPK55vOFDsxD56XXynFMCZDYFogH2l/hDfOsQAm1myoU/1xByJ3tWqtulXA=="],
53
53
 
@@ -71,7 +71,7 @@
71
71
 
72
72
  "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260122.2", "", { "os": "win32", "cpu": "x64" }, "sha512-TJH+sSn7vxcGvaSm+fhcpW//dfSGBZGHrxoy0edAbE7UpFf8ub/3cILL5V1pCsF7w3aLNNgVKRpUkowdUgYcPQ=="],
73
73
 
74
- "allmark": ["allmark@1.0.0", "", { "bin": { "allmark": "dist/bin/index.mjs" } }, "sha512-Stsjcu2cLPBwPlbfba/iMaDGVhJcm6pYU8hNtyTIlYHhckBvCOtiHhyBujl6PltcFnpvG0VQpwyBXWjwY1e6ZA=="],
74
+ "allmark": ["allmark@1.0.1", "", { "bin": { "allmark": "dist/bin/index.mjs" } }, "sha512-r4eeGshglWb3G99D4anWSPpieZ9kw9zXHeGd6NXjBbsbq+aCq/7W9hkrqYq0ywlf43ync2KKfmjeTHJum0AHEQ=="],
75
75
 
76
76
  "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
77
77
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-miniterm",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "A small front-end terminal UI for OpenCode",
5
5
  "main": "src/index.ts",
6
6
  "bin": {
@@ -28,7 +28,7 @@
28
28
  "typescript": "^5"
29
29
  },
30
30
  "dependencies": {
31
- "@opencode-ai/sdk": "^1.2.14",
32
- "allmark": "^1.0.0"
31
+ "@opencode-ai/sdk": "^1.2.15",
32
+ "allmark": "^1.0.1"
33
33
  }
34
34
  }
package/src/ansi.ts CHANGED
@@ -9,12 +9,16 @@ export const CURSOR_UP = (lines: number) => `\x1b[${lines}A`;
9
9
  export const RESET = "\x1b[0m";
10
10
  export const BRIGHT_WHITE = "\x1b[97m";
11
11
  export const BRIGHT_BLACK = "\x1b[90m";
12
+ export const BOLD_BRIGHT_BLACK = "\x1b[1;90m";
12
13
  export const RED = "\x1b[31m";
13
14
  export const GREEN = "\x1b[32m";
14
15
  export const BLUE = "\x1b[34m";
15
16
  export const CYAN = "\x1b[36m";
16
17
  export const BOLD_MAGENTA = "\x1b[1;35m";
17
18
  export const STRIKETHROUGH = "\x1b[9m";
19
+ export const WHITE_BACKGROUND = "\x1b[47m";
20
+ export const BRIGHT_BLACK_BACKGROUND = "\x1b[0;100m";
21
+ export const BOLD_BLACK = "\x1b[1;30m";
18
22
  export const ANSI_CODE_PATTERN = /^\x1b\[[0-9;]*m/;
19
23
 
20
24
  export function stripAnsiCodes(str: string): string {
@@ -20,7 +20,8 @@ interface DiffLine {
20
20
  }
21
21
 
22
22
  async function run(client: OpencodeClient, state: State): Promise<void> {
23
- if (!config.sessionID) {
23
+ const cwd = process.cwd();
24
+ if (!config.sessionIDs[cwd]) {
24
25
  console.log("No active session.\n");
25
26
  return;
26
27
  }
@@ -28,7 +29,7 @@ async function run(client: OpencodeClient, state: State): Promise<void> {
28
29
  console.log("Fetching file changes...");
29
30
 
30
31
  const result = await client.session.diff({
31
- path: { id: config.sessionID },
32
+ path: { id: config.sessionIDs[cwd] },
32
33
  });
33
34
 
34
35
  if (result.error) {
@@ -14,11 +14,12 @@ let command: Command = {
14
14
  export default command;
15
15
 
16
16
  async function run(_client: OpencodeClient, _state: State): Promise<void> {
17
- if (!config.sessionID) return;
17
+ const cwd = process.cwd();
18
+ if (!config.sessionIDs[cwd]) return;
18
19
 
19
20
  console.log("Running /init command (analyzing project and creating AGENTS.md)...");
20
21
  const result = await _client.session.init({
21
- path: { id: config.sessionID },
22
+ path: { id: config.sessionIDs[cwd] },
22
23
  });
23
24
 
24
25
  if (result.error) {
@@ -16,7 +16,7 @@ export default command;
16
16
 
17
17
  async function run(client: OpencodeClient, state: State): Promise<void> {
18
18
  state.sessionID = await createSession(client);
19
- config.sessionID = state.sessionID;
19
+ config.sessionIDs[process.cwd()] = state.sessionID;
20
20
  saveConfig();
21
21
 
22
22
  await updateSessionTitle();
@@ -1,5 +1,6 @@
1
1
  import type { OpencodeClient } from "@opencode-ai/sdk";
2
2
  import type { Key } from "node:readline";
3
+ import * as ansi from "../ansi";
3
4
  import type { State } from "../index";
4
5
  import { wrapText } from "../render";
5
6
  import type { Command } from "../types";
@@ -24,18 +25,20 @@ function run(client: OpencodeClient, state: State): void {
24
25
  if (!part || !part.text.trim()) continue;
25
26
 
26
27
  if (part.title === "thinking") {
27
- pages.push(`💭 \x1b[90m${part.text.trimStart()}\x1b[0m`);
28
+ pages.push(
29
+ `${ansi.BOLD_BRIGHT_BLACK}~${ansi.RESET} ${ansi.BRIGHT_BLACK}${part.text.trimStart()}${ansi.RESET}`,
30
+ );
28
31
  } else if (part.title === "response") {
29
- pages.push(`💬 ${part.text.trimStart()}`);
30
- } else if (part.title === "tool") {
31
- pages.push(part.text);
32
- } else if (part.title === "files") {
32
+ pages.push(
33
+ `${ansi.WHITE_BACKGROUND}${ansi.BOLD_BLACK}*${ansi.RESET} ${part.text.trimStart()}`,
34
+ );
35
+ } else {
33
36
  pages.push(part.text);
34
37
  }
35
38
  }
36
39
 
37
40
  if (pages.length === 0) {
38
- console.log("\n\x1b[90mNo parts to display yet.\x1b[0m\n");
41
+ console.log(`${ansi.BRIGHT_BLACK}No parts to display yet.${ansi.RESET}\n`);
39
42
  return;
40
43
  }
41
44
 
@@ -44,7 +44,7 @@ async function run(client: OpencodeClient, state: State): Promise<void> {
44
44
  if (sessions.length === 0) {
45
45
  console.log("No sessions found. Creating a new session...");
46
46
  state.sessionID = await createSession(client);
47
- config.sessionID = state.sessionID;
47
+ config.sessionIDs[process.cwd()] = state.sessionID;
48
48
  saveConfig();
49
49
  console.log(`Created new session: ${state.sessionID}...\n`);
50
50
  await updateSessionTitle();
@@ -135,7 +135,7 @@ async function handleKey(_client: OpencodeClient, key: Key, str?: string) {
135
135
  readline.cursorTo(process.stdout, 0);
136
136
  readline.clearScreenDown(process.stdout);
137
137
  if (selected) {
138
- config.sessionID = selected.id;
138
+ config.sessionIDs[process.cwd()] = selected.id;
139
139
  saveConfig();
140
140
  console.log(`Switched to session: ${selected.id.substring(0, 8)}...`);
141
141
  if (selected.title) {
@@ -222,7 +222,7 @@ function renderSessionList(): void {
222
222
  const globalIndex = sessionList.indexOf(session);
223
223
  const filteredIndex = sessionFilteredIndices.indexOf(globalIndex);
224
224
  const isSelected = filteredIndex === selectedSessionIndex;
225
- const isActive = session.id === config.sessionID;
225
+ const isActive = session.id === config.sessionIDs[process.cwd()];
226
226
  const prefix = isSelected ? " >" : " -";
227
227
  const title = session.title || "(no title)";
228
228
  const name = isSelected ? `\x1b[33;1m${title}\x1b[0m` : title;
@@ -250,7 +250,7 @@ function updateSessionFilter(): void {
250
250
  }
251
251
  if (sessionFilteredIndices.length > 0) {
252
252
  selectedSessionIndex = sessionFilteredIndices.indexOf(
253
- sessionList.findIndex((s) => s.id === config.sessionID),
253
+ sessionList.findIndex((s) => s.id === config.sessionIDs[process.cwd()]),
254
254
  );
255
255
  if (selectedSessionIndex === -1) selectedSessionIndex = 0;
256
256
  }
@@ -13,12 +13,13 @@ let command: Command = {
13
13
  export default command;
14
14
 
15
15
  async function run(client: OpencodeClient, _state: State): Promise<void> {
16
- if (!config.sessionID) return;
16
+ const cwd = process.cwd();
17
+ if (!config.sessionIDs[cwd]) return;
17
18
 
18
19
  console.log("Fetching session messages...");
19
20
 
20
21
  const messagesRes = await client.session.messages({
21
- path: { id: config.sessionID },
22
+ path: { id: config.sessionIDs[cwd] },
22
23
  });
23
24
 
24
25
  if (messagesRes.error) {
@@ -49,7 +50,7 @@ async function run(client: OpencodeClient, _state: State): Promise<void> {
49
50
  console.log(`Reverting last assistant message (${lastMessage.info.id})...`);
50
51
 
51
52
  const revertRes = await client.session.revert({
52
- path: { id: config.sessionID },
53
+ path: { id: config.sessionIDs[process.cwd()] },
53
54
  body: {
54
55
  messageID: lastMessage.info.id,
55
56
  },
package/src/config.ts CHANGED
@@ -5,7 +5,7 @@ export interface Config {
5
5
  providerID: string;
6
6
  modelID: string;
7
7
  agentID: string;
8
- sessionID?: string;
8
+ sessionIDs: Record<string, string>;
9
9
  loggingEnabled: boolean;
10
10
  }
11
11
 
@@ -15,6 +15,7 @@ export const config: Config = {
15
15
  providerID: "opencode",
16
16
  modelID: "big-pickle",
17
17
  agentID: "build",
18
+ sessionIDs: {},
18
19
  loggingEnabled: false,
19
20
  };
20
21