@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 +58 -30
- package/bin/cheri.js +3 -3
- package/package.json +7 -13
- package/src/commands/agent.js +70 -50
- package/src/commands/memory.js +4 -12
- package/src/commands/usage.js +64 -0
- package/src/lib/api-client.js +40 -0
- package/src/lib/config-store.js +0 -10
- package/src/lib/logger.js +1 -1
- package/src/repl.js +18 -22
- package/src/commands/chat.js +0 -15
- package/src/lib/branding.js +0 -36
- package/src/lib/deepseek-client.js +0 -64
- package/src/lib/providers/anthropic.js +0 -66
- package/src/lib/providers/base.js +0 -34
- package/src/lib/providers/gemini.js +0 -89
- package/src/lib/providers/index.js +0 -47
- package/src/lib/providers/openai.js +0 -105
- package/src/lib/renderer.js +0 -44
- package/src/lib/repl.js +0 -225
- package/src/lib/tools/command-tools.js +0 -34
- package/src/lib/tools/file-tools.js +0 -73
- package/src/lib/tools/index.js +0 -32
- package/src/lib/tools/search-tools.js +0 -95
package/src/lib/branding.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { BaseProvider, SYSTEM_PROMPT } from "./base.js";
|
|
2
|
-
|
|
3
|
-
export class AnthropicProvider extends BaseProvider {
|
|
4
|
-
constructor(apiKey, model = "claude-sonnet-4-20250514") {
|
|
5
|
-
super(apiKey, model);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
async *chat(messages, tools) {
|
|
9
|
-
const { default: Anthropic } = await import("@anthropic-ai/sdk");
|
|
10
|
-
const client = new Anthropic({ apiKey: this.apiKey });
|
|
11
|
-
|
|
12
|
-
const anthropicTools = tools.map((t) => ({
|
|
13
|
-
name: t.name,
|
|
14
|
-
description: t.description,
|
|
15
|
-
input_schema: t.parameters,
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
const stream = await client.messages.stream({
|
|
19
|
-
model: this.model,
|
|
20
|
-
max_tokens: 8192,
|
|
21
|
-
system: SYSTEM_PROMPT,
|
|
22
|
-
messages,
|
|
23
|
-
tools: anthropicTools.length > 0 ? anthropicTools : undefined,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
let currentToolId = null;
|
|
27
|
-
let currentToolName = null;
|
|
28
|
-
let toolInputJson = "";
|
|
29
|
-
|
|
30
|
-
for await (const event of stream) {
|
|
31
|
-
if (event.type === "content_block_start") {
|
|
32
|
-
if (event.content_block.type === "text") {
|
|
33
|
-
// text block starting
|
|
34
|
-
} else if (event.content_block.type === "tool_use") {
|
|
35
|
-
currentToolId = event.content_block.id;
|
|
36
|
-
currentToolName = event.content_block.name;
|
|
37
|
-
toolInputJson = "";
|
|
38
|
-
yield { type: "tool_use_start", id: currentToolId, name: currentToolName };
|
|
39
|
-
}
|
|
40
|
-
} else if (event.type === "content_block_delta") {
|
|
41
|
-
if (event.delta.type === "text_delta") {
|
|
42
|
-
yield { type: "text", content: event.delta.text };
|
|
43
|
-
} else if (event.delta.type === "input_json_delta") {
|
|
44
|
-
toolInputJson += event.delta.partial_json;
|
|
45
|
-
yield { type: "tool_input_delta", content: event.delta.partial_json };
|
|
46
|
-
}
|
|
47
|
-
} else if (event.type === "content_block_stop") {
|
|
48
|
-
if (currentToolId) {
|
|
49
|
-
let input = {};
|
|
50
|
-
try {
|
|
51
|
-
input = JSON.parse(toolInputJson);
|
|
52
|
-
} catch {}
|
|
53
|
-
yield { type: "tool_use_end", id: currentToolId, name: currentToolName, input };
|
|
54
|
-
currentToolId = null;
|
|
55
|
-
currentToolName = null;
|
|
56
|
-
toolInputJson = "";
|
|
57
|
-
}
|
|
58
|
-
} else if (event.type === "message_stop") {
|
|
59
|
-
// done
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const finalMessage = await stream.finalMessage();
|
|
64
|
-
yield { type: "done", stopReason: finalMessage.stop_reason };
|
|
65
|
-
}
|
|
66
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export const SYSTEM_PROMPT = `You are Cheri, an AI coding assistant by HeySalad. You help developers write, debug, and understand code.
|
|
2
|
-
|
|
3
|
-
You have access to tools that let you read files, write files, edit files, run shell commands, search files, and list directories. Use them proactively to help the user.
|
|
4
|
-
|
|
5
|
-
Guidelines:
|
|
6
|
-
- Read files before modifying them to understand the existing code.
|
|
7
|
-
- Use edit_file for targeted changes instead of rewriting entire files.
|
|
8
|
-
- When running commands, explain what you're about to run and why.
|
|
9
|
-
- Be concise but thorough. Show relevant code snippets in your responses.
|
|
10
|
-
- If you're unsure about something, say so rather than guessing.
|
|
11
|
-
- Format responses with markdown for readability.`;
|
|
12
|
-
|
|
13
|
-
export class BaseProvider {
|
|
14
|
-
constructor(apiKey, model) {
|
|
15
|
-
this.apiKey = apiKey;
|
|
16
|
-
this.model = model;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Async generator that yields streaming events:
|
|
21
|
-
* { type: "text", content: string }
|
|
22
|
-
* { type: "tool_use_start", id: string, name: string }
|
|
23
|
-
* { type: "tool_input_delta", content: string }
|
|
24
|
-
* { type: "tool_use_end", id: string, name: string, input: object }
|
|
25
|
-
* { type: "done", stopReason: string }
|
|
26
|
-
*/
|
|
27
|
-
async *chat(messages, tools) {
|
|
28
|
-
throw new Error("chat() must be implemented by subclass");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
getModel() {
|
|
32
|
-
return this.model;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { BaseProvider, SYSTEM_PROMPT } from "./base.js";
|
|
2
|
-
|
|
3
|
-
export class GeminiProvider extends BaseProvider {
|
|
4
|
-
constructor(apiKey, model = "gemini-2.0-flash") {
|
|
5
|
-
super(apiKey, model);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
async *chat(messages, tools) {
|
|
9
|
-
const { GoogleGenerativeAI } = await import("@google/generative-ai");
|
|
10
|
-
const genAI = new GoogleGenerativeAI(this.apiKey);
|
|
11
|
-
|
|
12
|
-
const geminiTools = tools.length > 0 ? [{
|
|
13
|
-
functionDeclarations: tools.map((t) => ({
|
|
14
|
-
name: t.name,
|
|
15
|
-
description: t.description,
|
|
16
|
-
parameters: t.parameters,
|
|
17
|
-
})),
|
|
18
|
-
}] : undefined;
|
|
19
|
-
|
|
20
|
-
const genModel = genAI.getGenerativeModel({
|
|
21
|
-
model: this.model,
|
|
22
|
-
systemInstruction: SYSTEM_PROMPT,
|
|
23
|
-
tools: geminiTools,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// Convert messages to Gemini format
|
|
27
|
-
const history = [];
|
|
28
|
-
for (const msg of messages.slice(0, -1)) {
|
|
29
|
-
const role = msg.role === "assistant" ? "model" : "user";
|
|
30
|
-
if (typeof msg.content === "string") {
|
|
31
|
-
history.push({ role, parts: [{ text: msg.content }] });
|
|
32
|
-
} else if (Array.isArray(msg.content)) {
|
|
33
|
-
const parts = [];
|
|
34
|
-
for (const block of msg.content) {
|
|
35
|
-
if (block.type === "text") {
|
|
36
|
-
parts.push({ text: block.text });
|
|
37
|
-
} else if (block.type === "tool_use") {
|
|
38
|
-
parts.push({ functionCall: { name: block.name, args: block.input } });
|
|
39
|
-
} else if (block.type === "tool_result") {
|
|
40
|
-
const resultText = typeof block.content === "string" ? block.content : JSON.stringify(block.content);
|
|
41
|
-
parts.push({ functionResponse: { name: block.name || "tool", response: { result: resultText } } });
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
if (parts.length > 0) history.push({ role, parts });
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const chat = genModel.startChat({ history });
|
|
49
|
-
|
|
50
|
-
// Get the last message content
|
|
51
|
-
const lastMsg = messages[messages.length - 1];
|
|
52
|
-
const lastContent = typeof lastMsg.content === "string"
|
|
53
|
-
? lastMsg.content
|
|
54
|
-
: lastMsg.content.map((b) => {
|
|
55
|
-
if (b.type === "text") return b.text;
|
|
56
|
-
if (b.type === "tool_result") {
|
|
57
|
-
return typeof b.content === "string" ? b.content : JSON.stringify(b.content);
|
|
58
|
-
}
|
|
59
|
-
return "";
|
|
60
|
-
}).join("\n");
|
|
61
|
-
|
|
62
|
-
const result = await chat.sendMessageStream(lastContent);
|
|
63
|
-
|
|
64
|
-
let hasToolCalls = false;
|
|
65
|
-
|
|
66
|
-
for await (const chunk of result.stream) {
|
|
67
|
-
const text = chunk.text();
|
|
68
|
-
if (text) {
|
|
69
|
-
yield { type: "text", content: text };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Check for function calls
|
|
73
|
-
const candidates = chunk.candidates || [];
|
|
74
|
-
for (const candidate of candidates) {
|
|
75
|
-
for (const part of candidate.content?.parts || []) {
|
|
76
|
-
if (part.functionCall) {
|
|
77
|
-
hasToolCalls = true;
|
|
78
|
-
const id = `gemini_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
79
|
-
yield { type: "tool_use_start", id, name: part.functionCall.name };
|
|
80
|
-
yield { type: "tool_input_delta", content: JSON.stringify(part.functionCall.args) };
|
|
81
|
-
yield { type: "tool_use_end", id, name: part.functionCall.name, input: part.functionCall.args || {} };
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
yield { type: "done", stopReason: hasToolCalls ? "tool_use" : "end_turn" };
|
|
88
|
-
}
|
|
89
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { getConfigValue } from "../config-store.js";
|
|
2
|
-
import { getDefaultModel } from "../branding.js";
|
|
3
|
-
|
|
4
|
-
export async function createProvider(options = {}) {
|
|
5
|
-
const provider = options.provider || getConfigValue("ai.provider") || "anthropic";
|
|
6
|
-
const model = options.model || getConfigValue("ai.model") || getDefaultModel(provider);
|
|
7
|
-
|
|
8
|
-
// Resolve API key: env var takes priority, then config
|
|
9
|
-
const envKeys = {
|
|
10
|
-
anthropic: "ANTHROPIC_API_KEY",
|
|
11
|
-
openai: "OPENAI_API_KEY",
|
|
12
|
-
deepseek: "DEEPSEEK_API_KEY",
|
|
13
|
-
gemini: "GEMINI_API_KEY",
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const apiKey = process.env[envKeys[provider]] || getConfigValue(`ai.keys.${provider}`);
|
|
17
|
-
|
|
18
|
-
if (!apiKey) {
|
|
19
|
-
throw new Error(
|
|
20
|
-
`No API key found for ${provider}. Set it with:\n` +
|
|
21
|
-
` cheri config set ai.keys.${provider} <your-key>\n` +
|
|
22
|
-
`Or set the ${envKeys[provider]} environment variable.`
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Lazy import only the selected provider
|
|
27
|
-
switch (provider) {
|
|
28
|
-
case "anthropic": {
|
|
29
|
-
const { AnthropicProvider } = await import("./anthropic.js");
|
|
30
|
-
return new AnthropicProvider(apiKey, model);
|
|
31
|
-
}
|
|
32
|
-
case "openai": {
|
|
33
|
-
const { OpenAIProvider } = await import("./openai.js");
|
|
34
|
-
return new OpenAIProvider(apiKey, model);
|
|
35
|
-
}
|
|
36
|
-
case "deepseek": {
|
|
37
|
-
const { DeepSeekProvider } = await import("./openai.js");
|
|
38
|
-
return new DeepSeekProvider(apiKey, model);
|
|
39
|
-
}
|
|
40
|
-
case "gemini": {
|
|
41
|
-
const { GeminiProvider } = await import("./gemini.js");
|
|
42
|
-
return new GeminiProvider(apiKey, model);
|
|
43
|
-
}
|
|
44
|
-
default:
|
|
45
|
-
throw new Error(`Unknown provider: ${provider}. Supported: anthropic, openai, deepseek, gemini`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { BaseProvider, SYSTEM_PROMPT } from "./base.js";
|
|
2
|
-
|
|
3
|
-
export class OpenAIProvider extends BaseProvider {
|
|
4
|
-
constructor(apiKey, model = "gpt-4o", baseURL = undefined) {
|
|
5
|
-
super(apiKey, model);
|
|
6
|
-
this.baseURL = baseURL;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
async *chat(messages, tools) {
|
|
10
|
-
const { default: OpenAI } = await import("openai");
|
|
11
|
-
const clientOpts = { apiKey: this.apiKey };
|
|
12
|
-
if (this.baseURL) clientOpts.baseURL = this.baseURL;
|
|
13
|
-
const client = new OpenAI(clientOpts);
|
|
14
|
-
|
|
15
|
-
// Convert from Anthropic message format to OpenAI format
|
|
16
|
-
const openaiMessages = [{ role: "system", content: SYSTEM_PROMPT }];
|
|
17
|
-
for (const msg of messages) {
|
|
18
|
-
if (typeof msg.content === "string") {
|
|
19
|
-
openaiMessages.push({ role: msg.role, content: msg.content });
|
|
20
|
-
} else if (Array.isArray(msg.content)) {
|
|
21
|
-
// Handle tool results and multi-part content
|
|
22
|
-
for (const block of msg.content) {
|
|
23
|
-
if (block.type === "tool_result") {
|
|
24
|
-
openaiMessages.push({
|
|
25
|
-
role: "tool",
|
|
26
|
-
tool_call_id: block.tool_use_id,
|
|
27
|
-
content: typeof block.content === "string" ? block.content : JSON.stringify(block.content),
|
|
28
|
-
});
|
|
29
|
-
} else if (block.type === "text") {
|
|
30
|
-
openaiMessages.push({ role: msg.role, content: block.text });
|
|
31
|
-
} else if (block.type === "tool_use") {
|
|
32
|
-
// Assistant message with tool call
|
|
33
|
-
openaiMessages.push({
|
|
34
|
-
role: "assistant",
|
|
35
|
-
content: null,
|
|
36
|
-
tool_calls: [{
|
|
37
|
-
id: block.id,
|
|
38
|
-
type: "function",
|
|
39
|
-
function: { name: block.name, arguments: JSON.stringify(block.input) },
|
|
40
|
-
}],
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const openaiTools = tools.map((t) => ({
|
|
48
|
-
type: "function",
|
|
49
|
-
function: { name: t.name, description: t.description, parameters: t.parameters },
|
|
50
|
-
}));
|
|
51
|
-
|
|
52
|
-
const stream = await client.chat.completions.create({
|
|
53
|
-
model: this.model,
|
|
54
|
-
messages: openaiMessages,
|
|
55
|
-
tools: openaiTools.length > 0 ? openaiTools : undefined,
|
|
56
|
-
stream: true,
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const toolCalls = {};
|
|
60
|
-
|
|
61
|
-
for await (const chunk of stream) {
|
|
62
|
-
const delta = chunk.choices?.[0]?.delta;
|
|
63
|
-
const finishReason = chunk.choices?.[0]?.finish_reason;
|
|
64
|
-
|
|
65
|
-
if (delta?.content) {
|
|
66
|
-
yield { type: "text", content: delta.content };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (delta?.tool_calls) {
|
|
70
|
-
for (const tc of delta.tool_calls) {
|
|
71
|
-
const idx = tc.index;
|
|
72
|
-
if (!toolCalls[idx]) {
|
|
73
|
-
toolCalls[idx] = { id: tc.id || "", name: "", arguments: "" };
|
|
74
|
-
}
|
|
75
|
-
if (tc.id) toolCalls[idx].id = tc.id;
|
|
76
|
-
if (tc.function?.name) {
|
|
77
|
-
toolCalls[idx].name = tc.function.name;
|
|
78
|
-
yield { type: "tool_use_start", id: toolCalls[idx].id, name: tc.function.name };
|
|
79
|
-
}
|
|
80
|
-
if (tc.function?.arguments) {
|
|
81
|
-
toolCalls[idx].arguments += tc.function.arguments;
|
|
82
|
-
yield { type: "tool_input_delta", content: tc.function.arguments };
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (finishReason) {
|
|
88
|
-
// Emit tool_use_end for any accumulated tool calls
|
|
89
|
-
for (const idx of Object.keys(toolCalls)) {
|
|
90
|
-
const tc = toolCalls[idx];
|
|
91
|
-
let input = {};
|
|
92
|
-
try { input = JSON.parse(tc.arguments); } catch {}
|
|
93
|
-
yield { type: "tool_use_end", id: tc.id, name: tc.name, input };
|
|
94
|
-
}
|
|
95
|
-
yield { type: "done", stopReason: finishReason === "tool_calls" ? "tool_use" : finishReason };
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export class DeepSeekProvider extends OpenAIProvider {
|
|
102
|
-
constructor(apiKey, model = "deepseek-chat") {
|
|
103
|
-
super(apiKey, model, "https://api.deepseek.com");
|
|
104
|
-
}
|
|
105
|
-
}
|
package/src/lib/renderer.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
|
|
3
|
-
let markedRender = null;
|
|
4
|
-
|
|
5
|
-
async function getMarked() {
|
|
6
|
-
if (markedRender) return markedRender;
|
|
7
|
-
const { marked } = await import("marked");
|
|
8
|
-
const { default: TerminalRenderer } = await import("marked-terminal");
|
|
9
|
-
marked.setOptions({
|
|
10
|
-
renderer: new TerminalRenderer({
|
|
11
|
-
reflowText: true,
|
|
12
|
-
width: Math.min(process.stdout.columns || 80, 100),
|
|
13
|
-
tab: 2,
|
|
14
|
-
}),
|
|
15
|
-
});
|
|
16
|
-
markedRender = marked;
|
|
17
|
-
return markedRender;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function renderMarkdown(text) {
|
|
21
|
-
try {
|
|
22
|
-
const marked = await getMarked();
|
|
23
|
-
return marked.parse(text);
|
|
24
|
-
} catch {
|
|
25
|
-
return text;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function renderToolUse(name, input) {
|
|
30
|
-
const shortInput = Object.entries(input || {})
|
|
31
|
-
.map(([k, v]) => {
|
|
32
|
-
const val = typeof v === "string" ? (v.length > 60 ? v.slice(0, 57) + "..." : v) : JSON.stringify(v);
|
|
33
|
-
return `${chalk.dim(k)}=${val}`;
|
|
34
|
-
})
|
|
35
|
-
.join(", ");
|
|
36
|
-
return `${chalk.magenta("🔧")} ${chalk.bold(name)}(${shortInput})`;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function renderToolResult(name, result) {
|
|
40
|
-
if (result.error) {
|
|
41
|
-
return chalk.red(`❌ ${name}: ${result.error}`);
|
|
42
|
-
}
|
|
43
|
-
return chalk.green(`✅ ${name} completed`);
|
|
44
|
-
}
|