sharkcode 0.2.1 → 0.3.1
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 +59 -39
- package/dist/cli.mjs +223 -51
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,49 +1,46 @@
|
|
|
1
1
|
# Shark Code
|
|
2
2
|
|
|
3
|
-
> Local First, open-source AI coding agent.
|
|
3
|
+
> Local First, open-source AI coding agent.
|
|
4
4
|
> A small CLI inspired by Claude Code / OpenCode, with DeepSeek as the default provider.
|
|
5
5
|
|
|
6
6
|
## Install
|
|
7
7
|
|
|
8
|
-
### Global install from npm
|
|
9
|
-
|
|
10
8
|
```bash
|
|
11
9
|
npm install -g sharkcode
|
|
12
10
|
```
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
Launch interactive mode:
|
|
15
13
|
|
|
16
14
|
```bash
|
|
17
|
-
sharkcode
|
|
18
|
-
sharkcode "fix the null pointer bug in auth.ts"
|
|
19
|
-
sharkcode "add error handling to the API routes"
|
|
15
|
+
sharkcode
|
|
20
16
|
```
|
|
21
17
|
|
|
22
|
-
|
|
18
|
+
Or single-shot mode:
|
|
23
19
|
|
|
24
20
|
```bash
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
bun install
|
|
28
|
-
bun run start "explain this codebase"
|
|
21
|
+
sharkcode "explain this codebase"
|
|
22
|
+
sharkcode "fix the null pointer bug in auth.ts"
|
|
29
23
|
```
|
|
30
24
|
|
|
31
|
-
##
|
|
25
|
+
## Providers
|
|
32
26
|
|
|
33
|
-
Shark Code
|
|
27
|
+
Shark Code supports multiple providers. Switch between them anytime with `/provider`.
|
|
34
28
|
|
|
35
|
-
|
|
29
|
+
| Provider | Description | Docs |
|
|
30
|
+
|----------|-------------|------|
|
|
31
|
+
| `deepseek` | DeepSeek 官网 (default) | [platform.deepseek.com](https://platform.deepseek.com) |
|
|
32
|
+
| `ark` | 火山引擎 方舟 Coding Plan | [volcengine.com/activity/codingplan](https://www.volcengine.com/activity/codingplan) |
|
|
36
33
|
|
|
37
|
-
|
|
34
|
+
## Configure
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
export DEEPSEEK_API_KEY=sk-xxxxxx
|
|
41
|
-
```
|
|
36
|
+
### Option 1: Slash commands (recommended)
|
|
42
37
|
|
|
43
|
-
|
|
38
|
+
Start `sharkcode`, then:
|
|
44
39
|
|
|
45
|
-
```
|
|
46
|
-
|
|
40
|
+
```
|
|
41
|
+
◆ /provider ark # switch to 方舟 Coding Plan
|
|
42
|
+
◆ /key sk-xxxxxxxxxx # set API key (saved automatically)
|
|
43
|
+
◆ /model ark-code-latest # optionally change model
|
|
47
44
|
```
|
|
48
45
|
|
|
49
46
|
### Option 2: Config file
|
|
@@ -51,37 +48,60 @@ $env:DEEPSEEK_API_KEY="sk-xxxxxx"
|
|
|
51
48
|
`~/.sharkcode/config.toml`
|
|
52
49
|
|
|
53
50
|
```toml
|
|
54
|
-
[
|
|
51
|
+
[default]
|
|
52
|
+
provider = "deepseek" # or "ark"
|
|
53
|
+
|
|
54
|
+
[providers.deepseek]
|
|
55
|
+
# API key from https://platform.deepseek.com
|
|
55
56
|
key = "sk-xxxxxx"
|
|
56
57
|
model = "deepseek-chat"
|
|
57
|
-
base_url = "https://api.deepseek.com/v1"
|
|
58
|
-
```
|
|
59
58
|
|
|
60
|
-
|
|
59
|
+
[providers.ark]
|
|
60
|
+
# API key from https://ark.cn-beijing.volces.com (方舟 Coding Plan)
|
|
61
|
+
key = "sk-xxxxxx"
|
|
62
|
+
model = "ark-code-latest"
|
|
63
|
+
```
|
|
61
64
|
|
|
62
|
-
|
|
65
|
+
### Option 3: Environment variables
|
|
63
66
|
|
|
64
|
-
```
|
|
65
|
-
|
|
67
|
+
```powershell
|
|
68
|
+
$env:DEEPSEEK_API_KEY="sk-xxxxxx" # deepseek provider
|
|
69
|
+
$env:ARK_API_KEY="sk-xxxxxx" # ark provider
|
|
66
70
|
```
|
|
67
71
|
|
|
68
|
-
##
|
|
72
|
+
## Slash Commands
|
|
73
|
+
|
|
74
|
+
Type `/` commands anytime inside the interactive REPL:
|
|
75
|
+
|
|
76
|
+
| Command | Description |
|
|
77
|
+
|---------|-------------|
|
|
78
|
+
| `/provider` | show current provider & key status |
|
|
79
|
+
| `/provider <name>` | switch provider (`deepseek` \| `ark`) |
|
|
80
|
+
| `/key <api-key>` | set API key for current provider |
|
|
81
|
+
| `/model <model-id>` | set model for current provider |
|
|
82
|
+
| `/clear` | clear conversation history |
|
|
83
|
+
| `/help` | show command list |
|
|
84
|
+
| `/exit` | quit |
|
|
85
|
+
|
|
86
|
+
## Run from source
|
|
69
87
|
|
|
70
88
|
```bash
|
|
71
|
-
|
|
72
|
-
|
|
89
|
+
git clone https://github.com/syy-shark/sharkcode.git
|
|
90
|
+
cd sharkcode
|
|
91
|
+
bun install
|
|
92
|
+
bun run start
|
|
73
93
|
```
|
|
74
94
|
|
|
75
|
-
|
|
95
|
+
## Upgrade
|
|
76
96
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
97
|
+
```bash
|
|
98
|
+
npm update -g sharkcode
|
|
99
|
+
```
|
|
80
100
|
|
|
81
101
|
## How It Works
|
|
82
102
|
|
|
83
|
-
```
|
|
84
|
-
User input
|
|
103
|
+
```
|
|
104
|
+
User input → Prompt + Tools → LLM → Tool execution → Result → Repeat
|
|
85
105
|
```
|
|
86
106
|
|
|
87
107
|
Built-in tools:
|
|
@@ -97,7 +117,7 @@ Built-in tools:
|
|
|
97
117
|
|
|
98
118
|
- Bun + TypeScript
|
|
99
119
|
- Vercel AI SDK
|
|
100
|
-
- DeepSeek API
|
|
120
|
+
- DeepSeek API / 火山引擎 方舟 API
|
|
101
121
|
|
|
102
122
|
## License
|
|
103
123
|
|
package/dist/cli.mjs
CHANGED
|
@@ -3,50 +3,107 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import chalk3 from "chalk";
|
|
5
5
|
import * as readline2 from "readline";
|
|
6
|
+
import { select, input } from "@inquirer/prompts";
|
|
6
7
|
|
|
7
8
|
// src/config.ts
|
|
8
9
|
import { parse } from "smol-toml";
|
|
9
10
|
import { existsSync, readFileSync, mkdirSync, writeFileSync } from "fs";
|
|
10
11
|
import { join } from "path";
|
|
11
12
|
import { homedir } from "os";
|
|
13
|
+
var PROVIDERS = {
|
|
14
|
+
deepseek: {
|
|
15
|
+
baseURL: "https://api.deepseek.com/v1",
|
|
16
|
+
defaultModel: "deepseek-chat",
|
|
17
|
+
label: "DeepSeek \u5B98\u7F51"
|
|
18
|
+
},
|
|
19
|
+
ark: {
|
|
20
|
+
// Coding Plan uses a dedicated endpoint — do NOT use /api/v3 (that's the pay-per-use endpoint)
|
|
21
|
+
baseURL: "https://ark.cn-beijing.volces.com/api/coding/v3",
|
|
22
|
+
defaultModel: "ark-code-latest",
|
|
23
|
+
label: "\u65B9\u821F Coding Plan"
|
|
24
|
+
}
|
|
25
|
+
};
|
|
12
26
|
var CONFIG_DIR = join(homedir(), ".sharkcode");
|
|
13
27
|
var CONFIG_FILE = join(CONFIG_DIR, "config.toml");
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
[
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
function serializeConfig(mc) {
|
|
29
|
+
const dp = mc.providers.deepseek;
|
|
30
|
+
const ap = mc.providers.ark;
|
|
31
|
+
return [
|
|
32
|
+
"# Shark Code Configuration",
|
|
33
|
+
"# https://github.com/syy-shark/sharkcode",
|
|
34
|
+
"",
|
|
35
|
+
"[default]",
|
|
36
|
+
`provider = "${mc.activeProvider}"`,
|
|
37
|
+
"",
|
|
38
|
+
"[providers.deepseek]",
|
|
39
|
+
"# API key from https://platform.deepseek.com",
|
|
40
|
+
`key = "${dp?.key ?? ""}"`,
|
|
41
|
+
`model = "${dp?.model ?? PROVIDERS.deepseek.defaultModel}"`,
|
|
42
|
+
"",
|
|
43
|
+
"[providers.ark]",
|
|
44
|
+
"# API key from https://ark.cn-beijing.volces.com (\u65B9\u821F Coding Plan)",
|
|
45
|
+
`key = "${ap?.key ?? ""}"`,
|
|
46
|
+
`model = "${ap?.model ?? PROVIDERS.ark.defaultModel}"`,
|
|
47
|
+
""
|
|
48
|
+
].join("\n");
|
|
49
|
+
}
|
|
50
|
+
function ensureConfig() {
|
|
25
51
|
if (!existsSync(CONFIG_DIR)) {
|
|
26
52
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
27
53
|
}
|
|
28
54
|
if (!existsSync(CONFIG_FILE)) {
|
|
29
|
-
|
|
55
|
+
const initial = {
|
|
56
|
+
activeProvider: "deepseek",
|
|
57
|
+
providers: {
|
|
58
|
+
deepseek: { key: "", model: PROVIDERS.deepseek.defaultModel },
|
|
59
|
+
ark: { key: "", model: PROVIDERS.ark.defaultModel }
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
writeFileSync(CONFIG_FILE, serializeConfig(initial), "utf-8");
|
|
30
63
|
}
|
|
31
64
|
}
|
|
32
|
-
function
|
|
33
|
-
|
|
65
|
+
function readMultiConfig() {
|
|
66
|
+
ensureConfig();
|
|
34
67
|
let toml = {};
|
|
35
68
|
try {
|
|
36
69
|
const raw = readFileSync(CONFIG_FILE, "utf-8");
|
|
37
70
|
toml = parse(raw);
|
|
38
71
|
} catch {
|
|
39
72
|
}
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
73
|
+
const legacy = toml.api;
|
|
74
|
+
const providersRaw = toml.providers ?? {};
|
|
75
|
+
const defaultSection = toml.default ?? {};
|
|
76
|
+
const deepseekKey = process.env.DEEPSEEK_API_KEY || providersRaw.deepseek?.key || legacy?.key || "";
|
|
77
|
+
const arkKey = process.env.ARK_API_KEY || providersRaw.ark?.key || "";
|
|
78
|
+
const activeProvider = defaultSection.provider || (legacy ? "deepseek" : "deepseek");
|
|
79
|
+
return {
|
|
80
|
+
activeProvider,
|
|
81
|
+
providers: {
|
|
82
|
+
deepseek: {
|
|
83
|
+
key: deepseekKey,
|
|
84
|
+
model: providersRaw.deepseek?.model || legacy?.model || PROVIDERS.deepseek.defaultModel
|
|
85
|
+
},
|
|
86
|
+
ark: {
|
|
87
|
+
key: arkKey,
|
|
88
|
+
model: providersRaw.ark?.model || PROVIDERS.ark.defaultModel
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function saveMultiConfig(mc) {
|
|
94
|
+
ensureConfig();
|
|
95
|
+
writeFileSync(CONFIG_FILE, serializeConfig(mc), "utf-8");
|
|
96
|
+
}
|
|
97
|
+
function resolveConfig(mc) {
|
|
98
|
+
const name = mc.activeProvider;
|
|
99
|
+
const entry = mc.providers[name];
|
|
100
|
+
const meta = PROVIDERS[name];
|
|
101
|
+
return {
|
|
102
|
+
providerName: name,
|
|
103
|
+
apiKey: entry?.key || "",
|
|
104
|
+
model: entry?.model || meta?.defaultModel || "deepseek-chat",
|
|
105
|
+
baseURL: meta?.baseURL || PROVIDERS.deepseek.baseURL
|
|
106
|
+
};
|
|
50
107
|
}
|
|
51
108
|
|
|
52
109
|
// src/agent.ts
|
|
@@ -210,7 +267,8 @@ function createProvider(config) {
|
|
|
210
267
|
const provider = createOpenAI({
|
|
211
268
|
baseURL: config.baseURL,
|
|
212
269
|
apiKey: config.apiKey,
|
|
213
|
-
name
|
|
270
|
+
// ARK requires a non-default name to avoid SDK header overrides
|
|
271
|
+
name: config.providerName === "ark" ? "ark" : "deepseek"
|
|
214
272
|
});
|
|
215
273
|
return provider.chat(config.model);
|
|
216
274
|
}
|
|
@@ -348,6 +406,11 @@ function truncate(str, max) {
|
|
|
348
406
|
|
|
349
407
|
// src/cli.ts
|
|
350
408
|
var PURPLE2 = chalk3.hex("#a855f7");
|
|
409
|
+
var GRAY = chalk3.gray;
|
|
410
|
+
var YELLOW = chalk3.yellow;
|
|
411
|
+
var GREEN = chalk3.green;
|
|
412
|
+
var RED = chalk3.red;
|
|
413
|
+
var CYAN = chalk3.cyan;
|
|
351
414
|
var GLYPHS = {
|
|
352
415
|
S: [[0, 1, 1, 1, 1], [1, 0, 0, 0, 0], [0, 1, 1, 1, 0], [0, 0, 0, 0, 1], [1, 1, 1, 1, 0]],
|
|
353
416
|
H: [[1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1]],
|
|
@@ -376,6 +439,80 @@ function renderWord(word, padLeft = 2) {
|
|
|
376
439
|
return rows;
|
|
377
440
|
}
|
|
378
441
|
var BANNER = ["", ...renderWord("shark", 2), "", ...renderWord("code", 8), ""].join("\n");
|
|
442
|
+
async function showCommandMenu(multiConfig) {
|
|
443
|
+
console.log();
|
|
444
|
+
try {
|
|
445
|
+
const action = await select({
|
|
446
|
+
message: PURPLE2("\u25C6 \u9009\u62E9\u64CD\u4F5C"),
|
|
447
|
+
choices: [
|
|
448
|
+
{ name: "\u{1F50C} \u5207\u6362 / \u914D\u7F6E Provider", value: "provider" },
|
|
449
|
+
{ name: "\u{1F5D1}\uFE0F \u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2", value: "clear" },
|
|
450
|
+
{ name: "\u{1F6AA} \u9000\u51FA", value: "exit" }
|
|
451
|
+
]
|
|
452
|
+
});
|
|
453
|
+
switch (action) {
|
|
454
|
+
case "provider":
|
|
455
|
+
return showSetupFlow(multiConfig);
|
|
456
|
+
case "clear":
|
|
457
|
+
console.log(GRAY("\n \u2713 \u5BF9\u8BDD\u5DF2\u6E05\u7A7A\n"));
|
|
458
|
+
return { multiConfig, config: resolveConfig(multiConfig), clearHistory: true };
|
|
459
|
+
case "exit":
|
|
460
|
+
console.log(GRAY("\nBye! \u{1F988}"));
|
|
461
|
+
return { multiConfig, config: resolveConfig(multiConfig), exit: true };
|
|
462
|
+
}
|
|
463
|
+
} catch {
|
|
464
|
+
console.log(GRAY("\n \u53D6\u6D88\n"));
|
|
465
|
+
}
|
|
466
|
+
return { multiConfig, config: resolveConfig(multiConfig) };
|
|
467
|
+
}
|
|
468
|
+
async function showSetupFlow(multiConfig) {
|
|
469
|
+
console.log();
|
|
470
|
+
try {
|
|
471
|
+
const providerChoices = Object.entries(PROVIDERS).map(([id, meta]) => {
|
|
472
|
+
const hasKey = !!multiConfig.providers[id]?.key;
|
|
473
|
+
const badge = hasKey ? GREEN("\u2713 \u5DF2\u914D\u7F6E") : YELLOW("\u2717 \u672A\u914D\u7F6E");
|
|
474
|
+
return { name: `${meta.label} ${badge}`, value: id };
|
|
475
|
+
});
|
|
476
|
+
const selectedProvider = await select({
|
|
477
|
+
message: PURPLE2("\u25C6 \u9009\u62E9 Provider"),
|
|
478
|
+
choices: providerChoices,
|
|
479
|
+
default: multiConfig.activeProvider
|
|
480
|
+
});
|
|
481
|
+
const currentKey = multiConfig.providers[selectedProvider]?.key ?? "";
|
|
482
|
+
const hint = currentKey ? GRAY("(\u56DE\u8F66\u4FDD\u7559 " + currentKey.slice(0, 6) + "\u2022\u2022\u2022)") : GRAY("(\u5FC5\u586B)");
|
|
483
|
+
const rawKey = await input({
|
|
484
|
+
message: PURPLE2("\u25C6 API Key ") + hint,
|
|
485
|
+
default: currentKey || void 0
|
|
486
|
+
});
|
|
487
|
+
const newKey = rawKey.trim() || currentKey;
|
|
488
|
+
let updated = { ...multiConfig, activeProvider: selectedProvider };
|
|
489
|
+
if (newKey) {
|
|
490
|
+
updated = {
|
|
491
|
+
...updated,
|
|
492
|
+
providers: {
|
|
493
|
+
...updated.providers,
|
|
494
|
+
[selectedProvider]: {
|
|
495
|
+
model: PROVIDERS[selectedProvider]?.defaultModel ?? "",
|
|
496
|
+
...updated.providers[selectedProvider] ?? {},
|
|
497
|
+
key: newKey
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
saveMultiConfig(updated);
|
|
503
|
+
const newConfig = resolveConfig(updated);
|
|
504
|
+
const keyMsg = newKey && newKey !== currentKey ? "\uFF0CAPI Key \u5DF2\u4FDD\u5B58" : "";
|
|
505
|
+
console.log(GREEN(`
|
|
506
|
+
\u2713 \u5DF2\u5207\u6362\u5230 ${PROVIDERS[selectedProvider].label}${keyMsg}`) + "\n");
|
|
507
|
+
if (!newConfig.apiKey) {
|
|
508
|
+
console.log(YELLOW(" \u26A0 \u8FD8\u672A\u586B\u5199 API Key\uFF0C\u65E0\u6CD5\u53D1\u9001\u6D88\u606F\n"));
|
|
509
|
+
}
|
|
510
|
+
return { multiConfig: updated, config: newConfig };
|
|
511
|
+
} catch {
|
|
512
|
+
console.log(GRAY("\n \u53D6\u6D88\n"));
|
|
513
|
+
return { multiConfig, config: resolveConfig(multiConfig) };
|
|
514
|
+
}
|
|
515
|
+
}
|
|
379
516
|
async function readLine(prompt) {
|
|
380
517
|
return new Promise((resolve4) => {
|
|
381
518
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -390,60 +527,95 @@ async function readLine(prompt) {
|
|
|
390
527
|
});
|
|
391
528
|
});
|
|
392
529
|
}
|
|
530
|
+
function statusLine(config) {
|
|
531
|
+
const label = PROVIDERS[config.providerName]?.label ?? config.providerName;
|
|
532
|
+
return PURPLE2(" \u25C6") + GRAY(` ${label}`) + CYAN(` [${config.model}]`) + GRAY(" \u8F93\u5165 / \u8C03\u51FA\u6307\u4EE4\u83DC\u5355\n");
|
|
533
|
+
}
|
|
393
534
|
async function main() {
|
|
394
535
|
const args = process.argv.slice(2);
|
|
395
536
|
if (args[0] === "--help" || args[0] === "-h") {
|
|
396
537
|
console.log(BANNER);
|
|
397
538
|
console.log(PURPLE2(" Usage:"));
|
|
398
|
-
console.log(" " + PURPLE2("sharkcode") +
|
|
399
|
-
console.log(" " + PURPLE2("sharkcode") +
|
|
539
|
+
console.log(" " + PURPLE2("sharkcode") + GRAY(" \u2014 \u4EA4\u4E92\u6A21\u5F0F\uFF08\u76F4\u63A5\u542F\u52A8\uFF09"));
|
|
540
|
+
console.log(" " + PURPLE2("sharkcode") + YELLOW(' "prompt"') + GRAY(" \u2014 \u5355\u6B21\u6267\u884C"));
|
|
541
|
+
console.log(GRAY("\n \u4EA4\u4E92\u6A21\u5F0F\u5185\u8F93\u5165 / \u8C03\u51FA\u6307\u4EE4\u83DC\u5355\n"));
|
|
400
542
|
return;
|
|
401
543
|
}
|
|
402
544
|
if (args[0] === "--version" || args[0] === "-v") {
|
|
403
|
-
console.log("sharkcode v0.
|
|
545
|
+
console.log("sharkcode v0.3.1");
|
|
404
546
|
return;
|
|
405
547
|
}
|
|
406
|
-
const config = loadConfig();
|
|
407
548
|
if (args.length > 0) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
549
|
+
const mc = readMultiConfig();
|
|
550
|
+
const config2 = resolveConfig(mc);
|
|
551
|
+
if (!config2.apiKey) {
|
|
552
|
+
console.error(RED("\u274C \u672A\u914D\u7F6E API Key\u3002\u8BF7\u5148\u8FD0\u884C sharkcode \u5E76\u8F93\u5165 / \u914D\u7F6E Provider\u3002"));
|
|
553
|
+
process.exit(1);
|
|
554
|
+
}
|
|
555
|
+
console.log(
|
|
556
|
+
PURPLE2("\n\u{1F988} SharkCode") + GRAY(` | ${PROVIDERS[config2.providerName]?.label ?? config2.providerName} | ${config2.model}
|
|
557
|
+
`)
|
|
558
|
+
);
|
|
559
|
+
await runAgent([{ role: "user", content: args.join(" ") }], config2);
|
|
412
560
|
return;
|
|
413
561
|
}
|
|
414
562
|
console.log(BANNER);
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
);
|
|
563
|
+
let multiConfig = readMultiConfig();
|
|
564
|
+
let config = resolveConfig(multiConfig);
|
|
565
|
+
console.log(statusLine(config));
|
|
566
|
+
if (!config.apiKey) {
|
|
567
|
+
console.log(
|
|
568
|
+
YELLOW(" \u26A0 \u5C1A\u672A\u914D\u7F6E API Key\u3002") + GRAY("\u8F93\u5165 / \u7136\u540E\u9009\u62E9\u300C\u5207\u6362 / \u914D\u7F6E Provider\u300D\n")
|
|
569
|
+
);
|
|
570
|
+
}
|
|
418
571
|
let messages = [];
|
|
419
572
|
while (true) {
|
|
420
|
-
const
|
|
421
|
-
if (
|
|
422
|
-
console.log(
|
|
573
|
+
const raw = await readLine(PURPLE2("\n\u25C6 "));
|
|
574
|
+
if (raw === null) {
|
|
575
|
+
console.log(GRAY("\nBye! \u{1F988}"));
|
|
423
576
|
break;
|
|
424
577
|
}
|
|
425
|
-
const trimmed =
|
|
578
|
+
const trimmed = raw.trim();
|
|
426
579
|
if (!trimmed) continue;
|
|
427
|
-
if (trimmed === "exit" || trimmed === "quit"
|
|
428
|
-
console.log(
|
|
580
|
+
if (trimmed === "exit" || trimmed === "quit") {
|
|
581
|
+
console.log(GRAY("Bye! \u{1F988}"));
|
|
429
582
|
break;
|
|
430
583
|
}
|
|
584
|
+
if (trimmed === "/") {
|
|
585
|
+
const r = await showCommandMenu(multiConfig);
|
|
586
|
+
multiConfig = r.multiConfig;
|
|
587
|
+
config = r.config;
|
|
588
|
+
if (r.clearHistory) messages = [];
|
|
589
|
+
if (r.exit) break;
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
if (trimmed === "/provider" || trimmed.startsWith("/provider ") || trimmed === "/model" || trimmed.startsWith("/model ") || trimmed === "/key" || trimmed.startsWith("/key ")) {
|
|
593
|
+
const r = await showSetupFlow(multiConfig);
|
|
594
|
+
multiConfig = r.multiConfig;
|
|
595
|
+
config = r.config;
|
|
596
|
+
if (r.exit) break;
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
if (trimmed.startsWith("/")) {
|
|
600
|
+
console.log(GRAY(" \u672A\u77E5\u547D\u4EE4\u3002\u8F93\u5165 / \u8C03\u51FA\u6307\u4EE4\u83DC\u5355\n"));
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
if (!config.apiKey) {
|
|
604
|
+
console.log(YELLOW(" \u26A0 \u8FD8\u672A\u586B\u5199 API Key\u3002\u8F93\u5165 / \u2192 \u5207\u6362 / \u914D\u7F6E Provider\n"));
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
431
607
|
messages.push({ role: "user", content: trimmed });
|
|
432
608
|
try {
|
|
433
609
|
messages = await runAgent(messages, config);
|
|
434
610
|
} catch (err) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
console.error(chalk3.yellow(" Check your API key in ~/.sharkcode/config.toml"));
|
|
440
|
-
}
|
|
441
|
-
messages = messages.slice(0, -1);
|
|
611
|
+
console.error(RED(`
|
|
612
|
+
\u274C ${String(err)}
|
|
613
|
+
`));
|
|
614
|
+
messages.pop();
|
|
442
615
|
}
|
|
443
616
|
}
|
|
444
617
|
}
|
|
445
618
|
main().catch((err) => {
|
|
446
|
-
console.error(chalk3.red(`
|
|
447
|
-
\u274C Fatal: ${err.message}`));
|
|
619
|
+
console.error(chalk3.red(`Fatal: ${String(err)}`));
|
|
448
620
|
process.exit(1);
|
|
449
621
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sharkcode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Local First, open-source AI Coding Agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@ai-sdk/openai": "^3.0.48",
|
|
29
|
+
"@inquirer/prompts": "^8.3.2",
|
|
29
30
|
"ai": "^6.0.141",
|
|
30
31
|
"chalk": "^5.6.2",
|
|
31
32
|
"smol-toml": "^1.6.1",
|