opencode-miniterm 1.0.1 → 1.0.2

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/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": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-miniterm",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "A small front-end terminal UI for OpenCode",
5
5
  "main": "src/index.ts",
6
6
  "bin": {
@@ -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();
@@ -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
 
package/src/index.ts CHANGED
@@ -14,7 +14,6 @@ import detailsCommand from "./commands/details";
14
14
  import diffCommand from "./commands/diff";
15
15
  import exitCommand from "./commands/exit";
16
16
  import initCommand from "./commands/init";
17
- import killCommand from "./commands/kill";
18
17
  import logCommand, { isLoggingEnabled } from "./commands/log";
19
18
  import modelsCommand from "./commands/models";
20
19
  import newCommand from "./commands/new";
@@ -42,7 +41,6 @@ const SLASH_COMMANDS = [
42
41
  debugCommand,
43
42
  logCommand,
44
43
  pageCommand,
45
- killCommand,
46
44
  exitCommand,
47
45
  quitCommand,
48
46
  runCommand,
@@ -122,11 +120,11 @@ async function main() {
122
120
  try {
123
121
  let isNewSession = false;
124
122
 
125
- const initialSessionID = config.sessionID;
123
+ const initialSessionID = config.sessionIDs[cwd];
126
124
  if (!initialSessionID || !(await validateSession(initialSessionID))) {
127
125
  state.sessionID = await createSession();
128
126
  isNewSession = true;
129
- config.sessionID = state.sessionID;
127
+ config.sessionIDs[cwd] = state.sessionID;
130
128
  saveConfig();
131
129
  } else {
132
130
  state.sessionID = initialSessionID;
@@ -1,33 +0,0 @@
1
- import type { OpencodeClient } from "@opencode-ai/sdk";
2
- import type { State } from "../index";
3
- import type { Command } from "../types";
4
-
5
- let command: Command = {
6
- name: "/kill",
7
- description: "Abort a session (e.g. `/kill ses_123`)",
8
- run,
9
- running: false,
10
- };
11
-
12
- export default command;
13
-
14
- async function run(client: OpencodeClient, _state: State, input?: string): Promise<void> {
15
- if (!input) {
16
- console.log("Usage: /kill <session_id>");
17
- console.log();
18
- return;
19
- }
20
-
21
- const result = await client.session.abort({
22
- path: { id: input },
23
- });
24
-
25
- if (result.error) {
26
- throw new Error(
27
- `Failed to abort session (${result.response.status}): ${JSON.stringify(result.error)}`,
28
- );
29
- }
30
-
31
- console.log(`Session aborted successfully.`);
32
- console.log();
33
- }