@heysalad/cheri-cli 0.4.0 → 0.6.0

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
@@ -1,6 +1,8 @@
1
- # Cheri CLI
1
+ # cheri-cli
2
2
 
3
- AI-powered cloud IDE by [HeySalad](https://heysalad.app). Like Claude Code, but for cloud workspaces.
3
+ CLI for [Cheri](https://cheri.heysalad.app) the AI-powered cloud IDE that never forgets.
4
+
5
+ Manage workspaces, track API usage, and access your AI memory from the terminal.
4
6
 
5
7
  ## Install
6
8
 
@@ -8,53 +10,79 @@ AI-powered cloud IDE by [HeySalad](https://heysalad.app). Like Claude Code, but
8
10
  npm install -g @heysalad/cheri-cli
9
11
  ```
10
12
 
11
- ## Usage
13
+ Requires Node.js 18+.
14
+
15
+ ## Quick Start
12
16
 
13
17
  ```bash
14
- # Login to your Cheri account
18
+ # Authenticate with your Cheri account
15
19
  cheri login
16
20
 
21
+ # Launch a cloud workspace
22
+ cheri workspace launch owner/my-repo
23
+
17
24
  # Check account status
18
25
  cheri status
19
26
 
20
- # Launch a cloud workspace
21
- cheri workspace launch owner/repo
22
-
23
- # List your workspaces
24
- cheri workspace list
27
+ # View API usage and rate limits
28
+ cheri usage
29
+ ```
25
30
 
26
- # Stop a workspace
27
- cheri workspace stop
31
+ ## Commands
32
+
33
+ | Command | Description |
34
+ |---|---|
35
+ | `cheri login` | Authenticate with GitHub |
36
+ | `cheri status` | Show account and workspace status |
37
+ | `cheri usage` | Show API usage and rate limit status |
38
+ | `cheri workspace launch <repo>` | Launch a new cloud workspace |
39
+ | `cheri workspace list` | List all workspaces |
40
+ | `cheri workspace stop <id>` | Stop a running workspace |
41
+ | `cheri workspace status <id>` | Get workspace status |
42
+ | `cheri memory show` | Show current memory entries |
43
+ | `cheri memory add <text>` | Add a memory entry |
44
+ | `cheri memory clear` | Clear all memory |
45
+ | `cheri memory export` | Export memory to JSON |
46
+ | `cheri config list` | Show all configuration |
47
+ | `cheri config get <key>` | Get a config value |
48
+ | `cheri config set <key> <value>` | Set a config value |
49
+ | `cheri init` | Initialize a project |
50
+
51
+ ## Interactive REPL
52
+
53
+ Run `cheri` with no arguments to enter the interactive REPL:
28
54
 
29
- # Initialize AI project config
30
- cheri init
55
+ ```
56
+ $ cheri
57
+ 🍒 cheri > help
58
+ 🍒 cheri > workspace list
59
+ 🍒 cheri > usage
60
+ 🍒 cheri > exit
61
+ ```
31
62
 
32
- # Manage persistent memory
33
- cheri memory show
34
- cheri memory add "Always use TypeScript strict mode"
35
- cheri memory clear
63
+ ## Rate Limits
36
64
 
37
- # View/update configuration
38
- cheri config list
39
- cheri config set apiUrl https://cheri.heysalad.app
40
- ```
65
+ | Plan | Limit |
66
+ |---|---|
67
+ | Free | 100 requests/hour |
68
+ | Pro | 1,000 requests/hour |
41
69
 
42
- ## How it works
70
+ Use `cheri usage` to check your current rate limit status.
43
71
 
44
- 1. **`cheri login`** opens your browser for GitHub OAuth, then you paste your API token
45
- 2. **`cheri workspace launch`** spins up a cloud workspace with code-server (VS Code in browser)
46
- 3. **`cheri memory`** stores persistent context that follows you across sessions
47
- 4. **`cheri init`** creates a local `.ai/` directory with project constitution files
72
+ ## Configuration
48
73
 
49
- ## Requirements
74
+ Config is stored in `~/.cheri/`. Set the API URL if self-hosting:
50
75
 
51
- - Node.js >= 18
76
+ ```bash
77
+ cheri config set apiUrl https://your-instance.example.com
78
+ ```
52
79
 
53
80
  ## Links
54
81
 
55
82
  - [Cheri Cloud IDE](https://cheri.heysalad.app)
56
- - [GitHub](https://github.com/Hey-Salad/cheri-cli)
83
+ - [Dashboard](https://cheri.heysalad.app/dashboard)
84
+ - [GitHub](https://github.com/chilu18/cloud-ide)
57
85
 
58
86
  ## License
59
87
 
60
- MIT - HeySalad
88
+ MIT
package/bin/cheri.js CHANGED
@@ -7,13 +7,13 @@ import { registerStatusCommand } from "../src/commands/status.js";
7
7
  import { registerMemoryCommand } from "../src/commands/memory.js";
8
8
  import { registerConfigCommand } from "../src/commands/config.js";
9
9
  import { registerWorkspaceCommand } from "../src/commands/workspace.js";
10
- import { registerChatCommand } from "../src/commands/chat.js";
10
+ import { registerUsageCommand } from "../src/commands/usage.js";
11
11
  import { registerAgentCommand } from "../src/commands/agent.js";
12
12
 
13
13
  program
14
14
  .name("cheri")
15
15
  .description("Cheri CLI - AI-powered cloud IDE by HeySalad")
16
- .version("0.2.0");
16
+ .version("0.1.0");
17
17
 
18
18
  registerLoginCommand(program);
19
19
  registerInitCommand(program);
@@ -21,7 +21,7 @@ registerStatusCommand(program);
21
21
  registerMemoryCommand(program);
22
22
  registerConfigCommand(program);
23
23
  registerWorkspaceCommand(program);
24
- registerChatCommand(program);
24
+ registerUsageCommand(program);
25
25
  registerAgentCommand(program);
26
26
 
27
27
  // If no args, launch interactive command REPL
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heysalad/cheri-cli",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Cheri CLI - AI-powered cloud IDE by HeySalad. Like Claude Code, but for cloud workspaces.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,15 +8,14 @@
8
8
  },
9
9
  "files": [
10
10
  "bin/",
11
- "src/",
12
- "README.md"
11
+ "src/"
13
12
  ],
14
13
  "scripts": {
15
14
  "start": "node bin/cheri.js",
16
15
  "dev": "node bin/cheri.js",
17
- "release:patch": "npm version patch && npm publish --access public && git push && git push --tags",
18
- "release:minor": "npm version minor && npm publish --access public && git push && git push --tags",
19
- "release:major": "npm version major && npm publish --access public && git push && git push --tags"
16
+ "release:patch": "npm version patch && npm publish && git push && git push --tags",
17
+ "release:minor": "npm version minor && npm publish && git push && git push --tags",
18
+ "release:major": "npm version major && npm publish && git push && git push --tags"
20
19
  },
21
20
  "keywords": [
22
21
  "cloud-ide",
@@ -29,23 +28,18 @@
29
28
  ],
30
29
  "repository": {
31
30
  "type": "git",
32
- "url": "https://github.com/Hey-Salad/cheri-cli.git"
31
+ "url": "https://github.com/chilu18/cloud-ide.git",
32
+ "directory": "cli"
33
33
  },
34
- "homepage": "https://cheri.heysalad.app",
35
34
  "author": "HeySalad",
36
35
  "license": "MIT",
37
36
  "engines": {
38
37
  "node": ">=18"
39
38
  },
40
39
  "dependencies": {
41
- "@anthropic-ai/sdk": "^0.74.0",
42
- "@google/generative-ai": "^0.24.1",
43
40
  "chalk": "^5.3.0",
44
41
  "commander": "^12.1.0",
45
42
  "inquirer": "^9.2.23",
46
- "marked": "^15.0.12",
47
- "marked-terminal": "^7.3.0",
48
- "openai": "^6.22.0",
49
43
  "ora": "^8.0.1"
50
44
  }
51
45
  }
@@ -1,6 +1,4 @@
1
1
  import { apiClient } from "../lib/api-client.js";
2
- import { getConfigValue, setConfigValue } from "../lib/config-store.js";
3
- import { streamChatCompletion } from "../lib/deepseek-client.js";
4
2
  import { log } from "../lib/logger.js";
5
3
  import chalk from "chalk";
6
4
 
@@ -30,9 +28,7 @@ const TOOLS = [
30
28
  description: "Launch a new cloud workspace for a GitHub repository",
31
29
  parameters: {
32
30
  type: "object",
33
- properties: {
34
- repo: { type: "string", description: "GitHub repo in owner/name format" },
35
- },
31
+ properties: { repo: { type: "string", description: "GitHub repo in owner/name format" } },
36
32
  required: ["repo"],
37
33
  },
38
34
  },
@@ -44,9 +40,7 @@ const TOOLS = [
44
40
  description: "Stop and delete a running workspace",
45
41
  parameters: {
46
42
  type: "object",
47
- properties: {
48
- id: { type: "string", description: "Workspace ID to stop" },
49
- },
43
+ properties: { id: { type: "string", description: "Workspace ID to stop" } },
50
44
  required: ["id"],
51
45
  },
52
46
  },
@@ -58,9 +52,7 @@ const TOOLS = [
58
52
  description: "Get the status of a specific workspace",
59
53
  parameters: {
60
54
  type: "object",
61
- properties: {
62
- id: { type: "string", description: "Workspace ID" },
63
- },
55
+ properties: { id: { type: "string", description: "Workspace ID" } },
64
56
  required: ["id"],
65
57
  },
66
58
  },
@@ -111,9 +103,7 @@ const TOOLS = [
111
103
  description: "Get a configuration value by key (dot notation supported)",
112
104
  parameters: {
113
105
  type: "object",
114
- properties: {
115
- key: { type: "string", description: "Config key, e.g. 'ai.provider'" },
116
- },
106
+ properties: { key: { type: "string", description: "Config key, e.g. 'ai.provider'" } },
117
107
  required: ["key"],
118
108
  },
119
109
  },
@@ -156,11 +146,15 @@ async function executeTool(name, args) {
156
146
  return await apiClient.clearMemory();
157
147
  case "get_usage":
158
148
  return await apiClient.getUsage();
159
- case "get_config":
149
+ case "get_config": {
150
+ const { getConfigValue } = await import("../lib/config-store.js");
160
151
  return { key: args.key, value: getConfigValue(args.key) };
161
- case "set_config":
152
+ }
153
+ case "set_config": {
154
+ const { setConfigValue } = await import("../lib/config-store.js");
162
155
  setConfigValue(args.key, args.value);
163
156
  return { key: args.key, value: args.value, status: "updated" };
157
+ }
164
158
  default:
165
159
  return { error: `Unknown tool: ${name}` };
166
160
  }
@@ -169,6 +163,36 @@ async function executeTool(name, args) {
169
163
  }
170
164
  }
171
165
 
166
+ // Parse SSE stream from the cloud proxy
167
+ async function* parseSSEStream(response) {
168
+ const reader = response.body.getReader();
169
+ const decoder = new TextDecoder();
170
+ let buffer = "";
171
+
172
+ try {
173
+ while (true) {
174
+ const { done, value } = await reader.read();
175
+ if (done) break;
176
+
177
+ buffer += decoder.decode(value, { stream: true });
178
+ const lines = buffer.split("\n");
179
+ buffer = lines.pop() || "";
180
+
181
+ for (const line of lines) {
182
+ if (line.startsWith("data: ")) {
183
+ const data = line.slice(6).trim();
184
+ if (data === "[DONE]") return;
185
+ try {
186
+ yield JSON.parse(data);
187
+ } catch {}
188
+ }
189
+ }
190
+ }
191
+ } finally {
192
+ reader.releaseLock();
193
+ }
194
+ }
195
+
172
196
  export async function runAgent(userRequest) {
173
197
  const messages = [
174
198
  { role: "system", content: SYSTEM_PROMPT },
@@ -178,77 +202,73 @@ export async function runAgent(userRequest) {
178
202
  const MAX_ITERATIONS = 10;
179
203
 
180
204
  for (let i = 0; i < MAX_ITERATIONS; i++) {
181
- let textContent = "";
205
+ const response = await apiClient.chatStream(messages, TOOLS);
206
+
207
+ let fullText = "";
182
208
  const toolCalls = {};
183
209
 
184
- for await (const chunk of streamChatCompletion(messages, TOOLS)) {
210
+ for await (const chunk of parseSSEStream(response)) {
185
211
  const delta = chunk.choices?.[0]?.delta;
186
- if (!delta) continue;
212
+ const finishReason = chunk.choices?.[0]?.finish_reason;
187
213
 
188
- // Stream text content to stdout
189
- if (delta.content) {
214
+ if (delta?.content) {
190
215
  process.stdout.write(delta.content);
191
- textContent += delta.content;
216
+ fullText += delta.content;
192
217
  }
193
218
 
194
- // Accumulate tool call deltas
195
- if (delta.tool_calls) {
219
+ if (delta?.tool_calls) {
196
220
  for (const tc of delta.tool_calls) {
197
221
  const idx = tc.index;
198
222
  if (!toolCalls[idx]) {
199
- toolCalls[idx] = { id: tc.id || "", function: { name: "", arguments: "" } };
223
+ toolCalls[idx] = { id: tc.id || "", name: "", arguments: "" };
200
224
  }
201
225
  if (tc.id) toolCalls[idx].id = tc.id;
202
- if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;
203
- if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;
226
+ if (tc.function?.name) toolCalls[idx].name = tc.function.name;
227
+ if (tc.function?.arguments) toolCalls[idx].arguments += tc.function.arguments;
204
228
  }
205
229
  }
230
+
231
+ if (finishReason) break;
206
232
  }
207
233
 
208
234
  const toolCallList = Object.values(toolCalls);
209
235
 
210
236
  // No tool calls — final text response, done
211
237
  if (toolCallList.length === 0) {
212
- if (textContent) process.stdout.write("\n");
238
+ if (fullText) process.stdout.write("\n");
213
239
  return;
214
240
  }
215
241
 
216
- // Append assistant message with tool calls
217
- const assistantMsg = { role: "assistant", content: textContent || null, tool_calls: [] };
218
- for (const tc of toolCallList) {
219
- assistantMsg.tool_calls.push({
220
- id: tc.id,
221
- type: "function",
222
- function: { name: tc.function.name, arguments: tc.function.arguments },
223
- });
224
- }
242
+ if (fullText) process.stdout.write("\n");
243
+
244
+ // Build assistant message with tool calls
245
+ const assistantMsg = { role: "assistant", content: fullText || null };
246
+ assistantMsg.tool_calls = toolCallList.map((tc) => ({
247
+ id: tc.id,
248
+ type: "function",
249
+ function: { name: tc.name, arguments: tc.arguments },
250
+ }));
225
251
  messages.push(assistantMsg);
226
252
 
227
- // Execute each tool call and append results
253
+ // Execute each tool and add results
228
254
  for (const tc of toolCallList) {
229
- const fnName = tc.function.name;
230
- let args = {};
231
- try {
232
- args = JSON.parse(tc.function.arguments || "{}");
233
- } catch {
234
- args = {};
235
- }
255
+ let input = {};
256
+ try { input = JSON.parse(tc.arguments); } catch {}
236
257
 
237
- log.info(`Calling ${chalk.cyan(fnName)}${Object.keys(args).length ? chalk.dim(" " + JSON.stringify(args)) : ""}`);
258
+ log.info(`Calling ${chalk.cyan(tc.name)}${Object.keys(input).length ? chalk.dim(" " + JSON.stringify(input)) : ""}`);
238
259
 
239
- const result = await executeTool(fnName, args);
240
- const resultStr = JSON.stringify(result);
260
+ const result = await executeTool(tc.name, input);
241
261
 
242
262
  if (result.error) {
243
263
  log.error(result.error);
244
264
  } else {
245
- log.success(fnName);
265
+ log.success(tc.name);
246
266
  }
247
267
 
248
268
  messages.push({
249
269
  role: "tool",
250
270
  tool_call_id: tc.id,
251
- content: resultStr,
271
+ content: JSON.stringify(result),
252
272
  });
253
273
  }
254
274
  }
@@ -53,21 +53,13 @@ export async function showMemory(options = {}) {
53
53
  }
54
54
 
55
55
  export async function addMemory(content, category = "general") {
56
- try {
57
- const { entry, count } = await apiClient.addMemory(content, category);
58
- log.success(`Memory saved (${count} total). Category: ${chalk.cyan(entry.category)}`);
59
- } catch (err) {
60
- throw err;
61
- }
56
+ const { entry, count } = await apiClient.addMemory(content, category);
57
+ log.success(`Memory saved (${count} total). Category: ${chalk.cyan(entry.category)}`);
62
58
  }
63
59
 
64
60
  export async function clearMemory() {
65
- try {
66
- await apiClient.clearMemory();
67
- log.success("All memories cleared.");
68
- } catch (err) {
69
- throw err;
70
- }
61
+ await apiClient.clearMemory();
62
+ log.success("All memories cleared.");
71
63
  }
72
64
 
73
65
  export async function exportMemory(options = {}) {
@@ -0,0 +1,64 @@
1
+ import chalk from "chalk";
2
+ import ora from "ora";
3
+ import { apiClient } from "../lib/api-client.js";
4
+ import { log } from "../lib/logger.js";
5
+
6
+ export async function showUsage() {
7
+ log.blank();
8
+ log.brand("Usage");
9
+
10
+ const spinner = ora("Fetching usage data...").start();
11
+
12
+ try {
13
+ const data = await apiClient.getUsage();
14
+ spinner.stop();
15
+
16
+ // Rate limit
17
+ log.header("Rate Limit");
18
+ log.keyValue("Plan", data.plan === "pro" ? chalk.green("Pro") : "Free");
19
+ log.keyValue("Limit", `${data.rateLimit.limit} requests/hour`);
20
+ const remaining = data.rateLimit.remaining;
21
+ const limit = data.rateLimit.limit;
22
+ const remainColor = remaining > limit * 0.5 ? chalk.green : remaining > limit * 0.1 ? chalk.yellow : chalk.red;
23
+ log.keyValue("Remaining", remainColor(`${remaining}`));
24
+ log.keyValue("Resets at", data.rateLimit.resetsAt);
25
+
26
+ // Today's usage
27
+ log.header("Today");
28
+ log.keyValue("Requests", `${data.usage.today.requests}`);
29
+ const endpoints = data.usage.today.endpoints || {};
30
+ if (Object.keys(endpoints).length > 0) {
31
+ for (const [ep, count] of Object.entries(endpoints)) {
32
+ console.log(` ${chalk.dim(ep)} ${chalk.cyan(count)}`);
33
+ }
34
+ }
35
+
36
+ // Summary
37
+ log.header("Summary");
38
+ log.keyValue("Last 7 days", `${data.usage.last7d.requests} requests`);
39
+ log.keyValue("Last 30 days", `${data.usage.last30d.requests} requests`);
40
+ log.keyValue("All time", `${data.summary.totalRequests} requests`);
41
+ if (data.summary.memberSince) {
42
+ log.keyValue("Member since", new Date(data.summary.memberSince).toLocaleDateString());
43
+ }
44
+ } catch (err) {
45
+ spinner.stop();
46
+ log.error(err.message);
47
+ }
48
+
49
+ log.blank();
50
+ }
51
+
52
+ export function registerUsageCommand(program) {
53
+ program
54
+ .command("usage")
55
+ .description("Show API usage and rate limit status")
56
+ .action(async () => {
57
+ try {
58
+ await showUsage();
59
+ } catch (err) {
60
+ log.error(err.message);
61
+ process.exit(1);
62
+ }
63
+ });
64
+ }
@@ -88,4 +88,44 @@ export const apiClient = {
88
88
  async getUsage() {
89
89
  return request("/api/usage");
90
90
  },
91
+
92
+ // AI Chat (streaming)
93
+ async chatStream(messages, tools = [], options = {}) {
94
+ const baseUrl = getBaseUrl();
95
+ const token = getToken();
96
+
97
+ if (!token) {
98
+ throw new Error("Not logged in. Run 'cheri login' first.");
99
+ }
100
+
101
+ const body = {
102
+ messages,
103
+ stream: true,
104
+ };
105
+ if (tools.length > 0) body.tools = tools;
106
+ if (options.model) body.model = options.model;
107
+
108
+ const res = await fetch(`${baseUrl}/api/chat/completions`, {
109
+ method: "POST",
110
+ headers: {
111
+ Authorization: `Bearer ${token}`,
112
+ "Content-Type": "application/json",
113
+ },
114
+ body: JSON.stringify(body),
115
+ });
116
+
117
+ if (!res.ok) {
118
+ const text = await res.text();
119
+ let msg;
120
+ try { msg = JSON.parse(text).error || text; } catch { msg = text; }
121
+ throw new Error(`AI error (${res.status}): ${msg}`);
122
+ }
123
+
124
+ return res;
125
+ },
126
+
127
+ // AI Models
128
+ async getModels() {
129
+ return request("/api/chat/models");
130
+ },
91
131
  };
@@ -74,15 +74,5 @@ function getDefaultConfig() {
74
74
  theme: "dark",
75
75
  fontSize: 14,
76
76
  },
77
- ai: {
78
- provider: "anthropic",
79
- model: "",
80
- keys: {
81
- anthropic: "",
82
- openai: "",
83
- deepseek: "",
84
- gemini: "",
85
- },
86
- },
87
77
  };
88
78
  }
package/src/lib/logger.js CHANGED
@@ -36,7 +36,7 @@ export const log = {
36
36
  console.log(chalk.dim(prefix) + " " + item);
37
37
  });
38
38
  },
39
- banner(version = "0.2.0") {
39
+ banner(version = "0.1.0") {
40
40
  console.log();
41
41
  console.log(` ${chalk.red("🍒")} ${chalk.red.bold("Cheri")}`);
42
42
  console.log(` ${chalk.dim("AI-powered cloud IDE by HeySalad")}`);
package/src/repl.js CHANGED
@@ -9,6 +9,7 @@ import { showStatus } from "./commands/status.js";
9
9
  import { listConfig, getConfigKey, setConfigKey } from "./commands/config.js";
10
10
  import { loginFlow } from "./commands/login.js";
11
11
  import { initProject } from "./commands/init.js";
12
+ import { showUsage } from "./commands/usage.js";
12
13
  import { runAgent } from "./commands/agent.js";
13
14
 
14
15
  const COMMANDS = {
@@ -21,13 +22,13 @@ const COMMANDS = {
21
22
  "memory clear": { args: "", desc: "Clear all memory" },
22
23
  "memory export": { args: "", desc: "Export memory to JSON" },
23
24
  "status": { args: "", desc: "Show account & workspace status" },
25
+ "usage": { args: "", desc: "Show API usage & rate limits" },
24
26
  "config list": { args: "", desc: "Show all configuration" },
25
27
  "config get": { args: "<key>", desc: "Get a config value" },
26
28
  "config set": { args: "<k> <v>",desc: "Set a config value" },
27
29
  "login": { args: "", desc: "Authenticate with Cheri" },
28
30
  "init": { args: "", desc: "Initialize a project" },
29
- "chat": { args: "", desc: "Start AI coding session" },
30
- "agent": { args: "<request>", desc: "AI agent - natural language command" },
31
+ "agent": { args: "<msg>", desc: "AI agent — natural language commands" },
31
32
  "help": { args: "", desc: "Show this help" },
32
33
  "clear": { args: "", desc: "Clear the terminal" },
33
34
  "exit": { args: "", desc: "Exit Cheri" },
@@ -76,6 +77,10 @@ async function dispatch(input) {
76
77
  await showStatus();
77
78
  return;
78
79
 
80
+ case "usage":
81
+ await showUsage();
82
+ return;
83
+
79
84
  case "login":
80
85
  await loginFlow();
81
86
  return;
@@ -84,25 +89,6 @@ async function dispatch(input) {
84
89
  await initProject();
85
90
  return;
86
91
 
87
- case "chat": {
88
- const { startRepl: startChatRepl } = await import("./lib/repl.js");
89
- await startChatRepl({
90
- provider: sub || undefined,
91
- model: rest || undefined,
92
- });
93
- return;
94
- }
95
-
96
- case "agent": {
97
- const agentInput = parts.slice(1).join(" ");
98
- if (!agentInput) {
99
- log.error("Usage: agent <request>");
100
- return;
101
- }
102
- await runAgent(agentInput);
103
- return;
104
- }
105
-
106
92
  case "workspace":
107
93
  switch (sub) {
108
94
  case "launch":
@@ -183,6 +169,16 @@ async function dispatch(input) {
183
169
  return;
184
170
  }
185
171
 
172
+ case "agent": {
173
+ const agentInput = parts.slice(1).join(" ");
174
+ if (!agentInput) {
175
+ log.error("Usage: agent <your request>");
176
+ return;
177
+ }
178
+ await runAgent(agentInput);
179
+ return;
180
+ }
181
+
186
182
  default:
187
183
  log.warn(`Unknown command: '${cmd}'. Type ${chalk.cyan("help")} for available commands.`);
188
184
  }
@@ -225,7 +221,7 @@ export async function startCommandRepl() {
225
221
  process.exit(0);
226
222
  });
227
223
 
228
- // Handle Ctrl+C gracefully — don't crash, just exit
224
+ // Handle Ctrl+C gracefully
229
225
  rl.on("SIGINT", () => {
230
226
  console.log(chalk.dim("\nGoodbye! 🍒\n"));
231
227
  process.exit(0);
@@ -1,15 +0,0 @@
1
- import { startRepl } from "../lib/repl.js";
2
-
3
- export function registerChatCommand(program) {
4
- program
5
- .command("chat")
6
- .description("Start an interactive AI coding session")
7
- .option("-p, --provider <provider>", "AI provider (anthropic, openai, deepseek, gemini)")
8
- .option("-m, --model <model>", "Model to use (overrides provider default)")
9
- .action(async (options) => {
10
- await startRepl({
11
- provider: options.provider,
12
- model: options.model,
13
- });
14
- });
15
- }