@heysalad/cheri-cli 0.3.0 → 0.4.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 +31 -59
- package/bin/cheri.js +5 -3
- package/package.json +13 -7
- package/src/commands/agent.js +272 -0
- package/src/commands/chat.js +15 -0
- package/src/commands/memory.js +12 -4
- package/src/lib/branding.js +36 -0
- package/src/lib/config-store.js +10 -0
- package/src/lib/deepseek-client.js +64 -0
- package/src/lib/logger.js +1 -1
- package/src/lib/providers/anthropic.js +66 -0
- package/src/lib/providers/base.js +34 -0
- package/src/lib/providers/gemini.js +89 -0
- package/src/lib/providers/index.js +47 -0
- package/src/lib/providers/openai.js +105 -0
- package/src/lib/renderer.js +44 -0
- package/src/lib/repl.js +225 -0
- package/src/lib/tools/command-tools.js +34 -0
- package/src/lib/tools/file-tools.js +73 -0
- package/src/lib/tools/index.js +32 -0
- package/src/lib/tools/search-tools.js +95 -0
- package/src/repl.js +23 -7
- package/src/commands/usage.js +0 -64
package/README.md
CHANGED
|
@@ -1,88 +1,60 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Cheri CLI
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Manage workspaces, track API usage, and access your AI memory from the terminal.
|
|
3
|
+
AI-powered cloud IDE by [HeySalad](https://heysalad.app). Like Claude Code, but for cloud workspaces.
|
|
6
4
|
|
|
7
5
|
## Install
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
|
-
npm install -g cheri-cli
|
|
8
|
+
npm install -g @heysalad/cheri-cli
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
## Quick Start
|
|
11
|
+
## Usage
|
|
16
12
|
|
|
17
13
|
```bash
|
|
18
|
-
#
|
|
14
|
+
# Login to your Cheri account
|
|
19
15
|
cheri login
|
|
20
16
|
|
|
21
|
-
# Launch a cloud workspace
|
|
22
|
-
cheri workspace launch owner/my-repo
|
|
23
|
-
|
|
24
17
|
# Check account status
|
|
25
18
|
cheri status
|
|
26
19
|
|
|
27
|
-
#
|
|
28
|
-
cheri
|
|
29
|
-
```
|
|
20
|
+
# Launch a cloud workspace
|
|
21
|
+
cheri workspace launch owner/repo
|
|
30
22
|
|
|
31
|
-
|
|
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:
|
|
23
|
+
# List your workspaces
|
|
24
|
+
cheri workspace list
|
|
54
25
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
🍒 cheri > exit
|
|
61
|
-
```
|
|
26
|
+
# Stop a workspace
|
|
27
|
+
cheri workspace stop
|
|
28
|
+
|
|
29
|
+
# Initialize AI project config
|
|
30
|
+
cheri init
|
|
62
31
|
|
|
63
|
-
|
|
32
|
+
# Manage persistent memory
|
|
33
|
+
cheri memory show
|
|
34
|
+
cheri memory add "Always use TypeScript strict mode"
|
|
35
|
+
cheri memory clear
|
|
64
36
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
37
|
+
# View/update configuration
|
|
38
|
+
cheri config list
|
|
39
|
+
cheri config set apiUrl https://cheri.heysalad.app
|
|
40
|
+
```
|
|
69
41
|
|
|
70
|
-
|
|
42
|
+
## How it works
|
|
71
43
|
|
|
72
|
-
|
|
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
|
|
73
48
|
|
|
74
|
-
|
|
49
|
+
## Requirements
|
|
75
50
|
|
|
76
|
-
|
|
77
|
-
cheri config set apiUrl https://your-instance.example.com
|
|
78
|
-
```
|
|
51
|
+
- Node.js >= 18
|
|
79
52
|
|
|
80
53
|
## Links
|
|
81
54
|
|
|
82
55
|
- [Cheri Cloud IDE](https://cheri.heysalad.app)
|
|
83
|
-
- [
|
|
84
|
-
- [GitHub](https://github.com/chilu18/cloud-ide)
|
|
56
|
+
- [GitHub](https://github.com/Hey-Salad/cheri-cli)
|
|
85
57
|
|
|
86
58
|
## License
|
|
87
59
|
|
|
88
|
-
MIT
|
|
60
|
+
MIT - HeySalad
|
package/bin/cheri.js
CHANGED
|
@@ -7,12 +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 {
|
|
10
|
+
import { registerChatCommand } from "../src/commands/chat.js";
|
|
11
|
+
import { registerAgentCommand } from "../src/commands/agent.js";
|
|
11
12
|
|
|
12
13
|
program
|
|
13
14
|
.name("cheri")
|
|
14
15
|
.description("Cheri CLI - AI-powered cloud IDE by HeySalad")
|
|
15
|
-
.version("0.
|
|
16
|
+
.version("0.2.0");
|
|
16
17
|
|
|
17
18
|
registerLoginCommand(program);
|
|
18
19
|
registerInitCommand(program);
|
|
@@ -20,7 +21,8 @@ registerStatusCommand(program);
|
|
|
20
21
|
registerMemoryCommand(program);
|
|
21
22
|
registerConfigCommand(program);
|
|
22
23
|
registerWorkspaceCommand(program);
|
|
23
|
-
|
|
24
|
+
registerChatCommand(program);
|
|
25
|
+
registerAgentCommand(program);
|
|
24
26
|
|
|
25
27
|
// If no args, launch interactive command REPL
|
|
26
28
|
if (!process.argv.slice(2).length) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heysalad/cheri-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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,14 +8,15 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin/",
|
|
11
|
-
"src/"
|
|
11
|
+
"src/",
|
|
12
|
+
"README.md"
|
|
12
13
|
],
|
|
13
14
|
"scripts": {
|
|
14
15
|
"start": "node bin/cheri.js",
|
|
15
16
|
"dev": "node bin/cheri.js",
|
|
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"
|
|
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"
|
|
19
20
|
},
|
|
20
21
|
"keywords": [
|
|
21
22
|
"cloud-ide",
|
|
@@ -28,18 +29,23 @@
|
|
|
28
29
|
],
|
|
29
30
|
"repository": {
|
|
30
31
|
"type": "git",
|
|
31
|
-
"url": "https://github.com/
|
|
32
|
-
"directory": "cli"
|
|
32
|
+
"url": "https://github.com/Hey-Salad/cheri-cli.git"
|
|
33
33
|
},
|
|
34
|
+
"homepage": "https://cheri.heysalad.app",
|
|
34
35
|
"author": "HeySalad",
|
|
35
36
|
"license": "MIT",
|
|
36
37
|
"engines": {
|
|
37
38
|
"node": ">=18"
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
41
|
+
"@anthropic-ai/sdk": "^0.74.0",
|
|
42
|
+
"@google/generative-ai": "^0.24.1",
|
|
40
43
|
"chalk": "^5.3.0",
|
|
41
44
|
"commander": "^12.1.0",
|
|
42
45
|
"inquirer": "^9.2.23",
|
|
46
|
+
"marked": "^15.0.12",
|
|
47
|
+
"marked-terminal": "^7.3.0",
|
|
48
|
+
"openai": "^6.22.0",
|
|
43
49
|
"ora": "^8.0.1"
|
|
44
50
|
}
|
|
45
51
|
}
|
|
@@ -0,0 +1,272 @@
|
|
|
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
|
+
import { log } from "../lib/logger.js";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
|
|
7
|
+
const SYSTEM_PROMPT = `You are Cheri Agent, an AI assistant for the Cheri cloud IDE platform. You help users manage cloud workspaces, memory, configuration, and their account. Use the provided tools to get real data — never guess or fabricate information. Be concise. After performing actions, briefly summarize what happened and the result.`;
|
|
8
|
+
|
|
9
|
+
const TOOLS = [
|
|
10
|
+
{
|
|
11
|
+
type: "function",
|
|
12
|
+
function: {
|
|
13
|
+
name: "get_account_info",
|
|
14
|
+
description: "Get the current user's account information",
|
|
15
|
+
parameters: { type: "object", properties: {}, required: [] },
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
type: "function",
|
|
20
|
+
function: {
|
|
21
|
+
name: "list_workspaces",
|
|
22
|
+
description: "List all cloud workspaces for the current user",
|
|
23
|
+
parameters: { type: "object", properties: {}, required: [] },
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: "function",
|
|
28
|
+
function: {
|
|
29
|
+
name: "create_workspace",
|
|
30
|
+
description: "Launch a new cloud workspace for a GitHub repository",
|
|
31
|
+
parameters: {
|
|
32
|
+
type: "object",
|
|
33
|
+
properties: {
|
|
34
|
+
repo: { type: "string", description: "GitHub repo in owner/name format" },
|
|
35
|
+
},
|
|
36
|
+
required: ["repo"],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: "function",
|
|
42
|
+
function: {
|
|
43
|
+
name: "stop_workspace",
|
|
44
|
+
description: "Stop and delete a running workspace",
|
|
45
|
+
parameters: {
|
|
46
|
+
type: "object",
|
|
47
|
+
properties: {
|
|
48
|
+
id: { type: "string", description: "Workspace ID to stop" },
|
|
49
|
+
},
|
|
50
|
+
required: ["id"],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: "function",
|
|
56
|
+
function: {
|
|
57
|
+
name: "get_workspace_status",
|
|
58
|
+
description: "Get the status of a specific workspace",
|
|
59
|
+
parameters: {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: {
|
|
62
|
+
id: { type: "string", description: "Workspace ID" },
|
|
63
|
+
},
|
|
64
|
+
required: ["id"],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: "function",
|
|
70
|
+
function: {
|
|
71
|
+
name: "get_memory",
|
|
72
|
+
description: "Retrieve all stored memory entries",
|
|
73
|
+
parameters: { type: "object", properties: {}, required: [] },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
type: "function",
|
|
78
|
+
function: {
|
|
79
|
+
name: "add_memory",
|
|
80
|
+
description: "Add a new memory entry for the user",
|
|
81
|
+
parameters: {
|
|
82
|
+
type: "object",
|
|
83
|
+
properties: {
|
|
84
|
+
content: { type: "string", description: "Memory content to store" },
|
|
85
|
+
category: { type: "string", description: "Optional category (defaults to 'general')" },
|
|
86
|
+
},
|
|
87
|
+
required: ["content"],
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
type: "function",
|
|
93
|
+
function: {
|
|
94
|
+
name: "clear_memory",
|
|
95
|
+
description: "Clear all stored memory entries",
|
|
96
|
+
parameters: { type: "object", properties: {}, required: [] },
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: "function",
|
|
101
|
+
function: {
|
|
102
|
+
name: "get_usage",
|
|
103
|
+
description: "Get the user's API usage and rate limit statistics",
|
|
104
|
+
parameters: { type: "object", properties: {}, required: [] },
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
type: "function",
|
|
109
|
+
function: {
|
|
110
|
+
name: "get_config",
|
|
111
|
+
description: "Get a configuration value by key (dot notation supported)",
|
|
112
|
+
parameters: {
|
|
113
|
+
type: "object",
|
|
114
|
+
properties: {
|
|
115
|
+
key: { type: "string", description: "Config key, e.g. 'ai.provider'" },
|
|
116
|
+
},
|
|
117
|
+
required: ["key"],
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
type: "function",
|
|
123
|
+
function: {
|
|
124
|
+
name: "set_config",
|
|
125
|
+
description: "Set a configuration value",
|
|
126
|
+
parameters: {
|
|
127
|
+
type: "object",
|
|
128
|
+
properties: {
|
|
129
|
+
key: { type: "string", description: "Config key" },
|
|
130
|
+
value: { type: "string", description: "Value to set" },
|
|
131
|
+
},
|
|
132
|
+
required: ["key", "value"],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
async function executeTool(name, args) {
|
|
139
|
+
try {
|
|
140
|
+
switch (name) {
|
|
141
|
+
case "get_account_info":
|
|
142
|
+
return await apiClient.getMe();
|
|
143
|
+
case "list_workspaces":
|
|
144
|
+
return await apiClient.listWorkspaces();
|
|
145
|
+
case "create_workspace":
|
|
146
|
+
return await apiClient.createWorkspace(args.repo);
|
|
147
|
+
case "stop_workspace":
|
|
148
|
+
return await apiClient.deleteWorkspace(args.id);
|
|
149
|
+
case "get_workspace_status":
|
|
150
|
+
return await apiClient.getWorkspaceStatus(args.id);
|
|
151
|
+
case "get_memory":
|
|
152
|
+
return await apiClient.getMemory();
|
|
153
|
+
case "add_memory":
|
|
154
|
+
return await apiClient.addMemory(args.content, args.category);
|
|
155
|
+
case "clear_memory":
|
|
156
|
+
return await apiClient.clearMemory();
|
|
157
|
+
case "get_usage":
|
|
158
|
+
return await apiClient.getUsage();
|
|
159
|
+
case "get_config":
|
|
160
|
+
return { key: args.key, value: getConfigValue(args.key) };
|
|
161
|
+
case "set_config":
|
|
162
|
+
setConfigValue(args.key, args.value);
|
|
163
|
+
return { key: args.key, value: args.value, status: "updated" };
|
|
164
|
+
default:
|
|
165
|
+
return { error: `Unknown tool: ${name}` };
|
|
166
|
+
}
|
|
167
|
+
} catch (err) {
|
|
168
|
+
return { error: err.message };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export async function runAgent(userRequest) {
|
|
173
|
+
const messages = [
|
|
174
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
175
|
+
{ role: "user", content: userRequest },
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
const MAX_ITERATIONS = 10;
|
|
179
|
+
|
|
180
|
+
for (let i = 0; i < MAX_ITERATIONS; i++) {
|
|
181
|
+
let textContent = "";
|
|
182
|
+
const toolCalls = {};
|
|
183
|
+
|
|
184
|
+
for await (const chunk of streamChatCompletion(messages, TOOLS)) {
|
|
185
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
186
|
+
if (!delta) continue;
|
|
187
|
+
|
|
188
|
+
// Stream text content to stdout
|
|
189
|
+
if (delta.content) {
|
|
190
|
+
process.stdout.write(delta.content);
|
|
191
|
+
textContent += delta.content;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Accumulate tool call deltas
|
|
195
|
+
if (delta.tool_calls) {
|
|
196
|
+
for (const tc of delta.tool_calls) {
|
|
197
|
+
const idx = tc.index;
|
|
198
|
+
if (!toolCalls[idx]) {
|
|
199
|
+
toolCalls[idx] = { id: tc.id || "", function: { name: "", arguments: "" } };
|
|
200
|
+
}
|
|
201
|
+
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;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const toolCallList = Object.values(toolCalls);
|
|
209
|
+
|
|
210
|
+
// No tool calls — final text response, done
|
|
211
|
+
if (toolCallList.length === 0) {
|
|
212
|
+
if (textContent) process.stdout.write("\n");
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
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
|
+
}
|
|
225
|
+
messages.push(assistantMsg);
|
|
226
|
+
|
|
227
|
+
// Execute each tool call and append results
|
|
228
|
+
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
|
+
}
|
|
236
|
+
|
|
237
|
+
log.info(`Calling ${chalk.cyan(fnName)}${Object.keys(args).length ? chalk.dim(" " + JSON.stringify(args)) : ""}`);
|
|
238
|
+
|
|
239
|
+
const result = await executeTool(fnName, args);
|
|
240
|
+
const resultStr = JSON.stringify(result);
|
|
241
|
+
|
|
242
|
+
if (result.error) {
|
|
243
|
+
log.error(result.error);
|
|
244
|
+
} else {
|
|
245
|
+
log.success(fnName);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
messages.push({
|
|
249
|
+
role: "tool",
|
|
250
|
+
tool_call_id: tc.id,
|
|
251
|
+
content: resultStr,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
log.warn("Agent reached maximum iterations (10). Stopping.");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function registerAgentCommand(program) {
|
|
260
|
+
program
|
|
261
|
+
.command("agent")
|
|
262
|
+
.argument("<request...>")
|
|
263
|
+
.description("AI agent — natural language command interface")
|
|
264
|
+
.action(async (requestParts) => {
|
|
265
|
+
const request = requestParts.join(" ");
|
|
266
|
+
try {
|
|
267
|
+
await runAgent(request);
|
|
268
|
+
} catch (err) {
|
|
269
|
+
log.error(err.message);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
}
|
package/src/commands/memory.js
CHANGED
|
@@ -53,13 +53,21 @@ export async function showMemory(options = {}) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export async function addMemory(content, category = "general") {
|
|
56
|
-
|
|
57
|
-
|
|
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
|
+
}
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
export async function clearMemory() {
|
|
61
|
-
|
|
62
|
-
|
|
65
|
+
try {
|
|
66
|
+
await apiClient.clearMemory();
|
|
67
|
+
log.success("All memories cleared.");
|
|
68
|
+
} catch (err) {
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
export async function exportMemory(options = {}) {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { getConfigValue } from "./config-store.js";
|
|
3
|
+
|
|
4
|
+
const CHERRY_ART = `
|
|
5
|
+
${chalk.red("🍒🍒")}
|
|
6
|
+
${chalk.red("🍒 🍒")}
|
|
7
|
+
`;
|
|
8
|
+
|
|
9
|
+
export function showStartupScreen(options = {}) {
|
|
10
|
+
const provider = options.provider || getConfigValue("ai.provider") || "anthropic";
|
|
11
|
+
const model = options.model || getConfigValue("ai.model") || getDefaultModel(provider);
|
|
12
|
+
const cwd = process.cwd();
|
|
13
|
+
const version = "0.2.0";
|
|
14
|
+
|
|
15
|
+
console.log(CHERRY_ART);
|
|
16
|
+
console.log(chalk.bold(` cheri v${version}`));
|
|
17
|
+
console.log(chalk.dim(" AI coding agent by HeySalad"));
|
|
18
|
+
console.log();
|
|
19
|
+
console.log(` ${chalk.dim("Provider:")} ${chalk.cyan(provider)}`);
|
|
20
|
+
console.log(` ${chalk.dim("Model:")} ${chalk.cyan(model)}`);
|
|
21
|
+
console.log(` ${chalk.dim("Directory:")} ${chalk.cyan(cwd)}`);
|
|
22
|
+
console.log();
|
|
23
|
+
console.log(chalk.dim(" Type your request. /help for commands, Ctrl+C to exit."));
|
|
24
|
+
console.log(chalk.dim(" " + "─".repeat(48)));
|
|
25
|
+
console.log();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getDefaultModel(provider) {
|
|
29
|
+
const defaults = {
|
|
30
|
+
anthropic: "claude-sonnet-4-20250514",
|
|
31
|
+
openai: "gpt-4o",
|
|
32
|
+
deepseek: "deepseek-chat",
|
|
33
|
+
gemini: "gemini-2.0-flash",
|
|
34
|
+
};
|
|
35
|
+
return defaults[provider] || "unknown";
|
|
36
|
+
}
|
package/src/lib/config-store.js
CHANGED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { getConfigValue } from "./config-store.js";
|
|
2
|
+
|
|
3
|
+
export function getApiKey() {
|
|
4
|
+
const key = getConfigValue("deepseekApiKey") || getConfigValue("ai.keys.deepseek");
|
|
5
|
+
if (!key) {
|
|
6
|
+
throw new Error(
|
|
7
|
+
"DeepSeek API key not set. Run: cheri config set deepseekApiKey sk-..."
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
return key;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getModel() {
|
|
14
|
+
return getConfigValue("agent.model") || "deepseek-chat";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function* streamChatCompletion(messages, tools) {
|
|
18
|
+
const apiKey = getApiKey();
|
|
19
|
+
const model = getModel();
|
|
20
|
+
|
|
21
|
+
const body = {
|
|
22
|
+
model,
|
|
23
|
+
messages,
|
|
24
|
+
stream: true,
|
|
25
|
+
};
|
|
26
|
+
if (tools && tools.length > 0) {
|
|
27
|
+
body.tools = tools;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const res = await fetch("https://api.deepseek.com/chat/completions", {
|
|
31
|
+
method: "POST",
|
|
32
|
+
headers: {
|
|
33
|
+
"Content-Type": "application/json",
|
|
34
|
+
Authorization: `Bearer ${apiKey}`,
|
|
35
|
+
},
|
|
36
|
+
body: JSON.stringify(body),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
const text = await res.text();
|
|
41
|
+
throw new Error(`DeepSeek API error (${res.status}): ${text}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const decoder = new TextDecoder();
|
|
45
|
+
let buffer = "";
|
|
46
|
+
|
|
47
|
+
for await (const chunk of res.body) {
|
|
48
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
49
|
+
const lines = buffer.split("\n");
|
|
50
|
+
buffer = lines.pop();
|
|
51
|
+
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
const trimmed = line.trim();
|
|
54
|
+
if (!trimmed || !trimmed.startsWith("data: ")) continue;
|
|
55
|
+
const data = trimmed.slice(6);
|
|
56
|
+
if (data === "[DONE]") return;
|
|
57
|
+
try {
|
|
58
|
+
yield JSON.parse(data);
|
|
59
|
+
} catch {
|
|
60
|
+
// skip malformed chunks
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
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.
|
|
39
|
+
banner(version = "0.2.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")}`);
|