sharkcode 0.3.0 → 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/dist/cli.mjs +102 -151
- package/package.json +2 -1
package/dist/cli.mjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
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";
|
|
@@ -438,131 +439,79 @@ function renderWord(word, padLeft = 2) {
|
|
|
438
439
|
return rows;
|
|
439
440
|
}
|
|
440
441
|
var BANNER = ["", ...renderWord("shark", 2), "", ...renderWord("code", 8), ""].join("\n");
|
|
441
|
-
function
|
|
442
|
-
const pad = (s, n) => s + " ".repeat(Math.max(0, n - s.length));
|
|
443
|
-
console.log(PURPLE2("\n \u25C6 Slash Commands\n"));
|
|
444
|
-
const cmds = [
|
|
445
|
-
["/provider", "show current provider & list options"],
|
|
446
|
-
["/provider <name>", "switch provider (deepseek | ark)"],
|
|
447
|
-
["/key <api-key>", "set API key for current provider"],
|
|
448
|
-
["/model <model-id>", "set model for current provider"],
|
|
449
|
-
["/clear", "clear conversation history"],
|
|
450
|
-
["/help", "show this menu"],
|
|
451
|
-
["/exit", "quit"]
|
|
452
|
-
];
|
|
453
|
-
for (const [cmd, desc] of cmds) {
|
|
454
|
-
console.log(" " + PURPLE2(pad(cmd, 26)) + GRAY(desc));
|
|
455
|
-
}
|
|
442
|
+
async function showCommandMenu(multiConfig) {
|
|
456
443
|
console.log();
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
if (!arg) {
|
|
476
|
-
const current = multiConfig.activeProvider;
|
|
477
|
-
console.log(PURPLE2("\n \u25C6 Providers\n"));
|
|
478
|
-
for (const [name2, meta] of Object.entries(PROVIDERS)) {
|
|
479
|
-
const entry = multiConfig.providers[name2];
|
|
480
|
-
const active = name2 === current;
|
|
481
|
-
const hasKey = !!entry?.key;
|
|
482
|
-
const marker = active ? PURPLE2("\u25B6") : " ";
|
|
483
|
-
const keyStatus = hasKey ? GREEN("\u2713 key set") : YELLOW("\u2717 no key");
|
|
484
|
-
console.log(
|
|
485
|
-
` ${marker} ${active ? PURPLE2(name2) : GRAY(name2)} ${GRAY(meta.label)} ${keyStatus}` + (active ? ` ${GRAY("model: " + (entry?.model ?? meta.defaultModel))}` : "")
|
|
486
|
-
);
|
|
487
|
-
}
|
|
488
|
-
console.log(
|
|
489
|
-
`
|
|
490
|
-
${GRAY("Usage:")} ${PURPLE2("/provider deepseek")} ${GRAY("or")} ${PURPLE2("/provider ark")}
|
|
491
|
-
`
|
|
492
|
-
);
|
|
493
|
-
return { multiConfig, config: resolveConfig(multiConfig) };
|
|
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 };
|
|
494
462
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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
|
+
};
|
|
499
501
|
}
|
|
500
|
-
const updated = { ...multiConfig, activeProvider: name };
|
|
501
502
|
saveMultiConfig(updated);
|
|
502
503
|
const newConfig = resolveConfig(updated);
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
\u2713
|
|
506
|
-
);
|
|
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
507
|
if (!newConfig.apiKey) {
|
|
508
|
-
console.log(YELLOW(
|
|
509
|
-
`));
|
|
510
|
-
} else {
|
|
511
|
-
console.log();
|
|
508
|
+
console.log(YELLOW(" \u26A0 \u8FD8\u672A\u586B\u5199 API Key\uFF0C\u65E0\u6CD5\u53D1\u9001\u6D88\u606F\n"));
|
|
512
509
|
}
|
|
513
510
|
return { multiConfig: updated, config: newConfig };
|
|
511
|
+
} catch {
|
|
512
|
+
console.log(GRAY("\n \u53D6\u6D88\n"));
|
|
513
|
+
return { multiConfig, config: resolveConfig(multiConfig) };
|
|
514
514
|
}
|
|
515
|
-
if (cmd === "/key") {
|
|
516
|
-
if (!arg) {
|
|
517
|
-
console.log(YELLOW(` Usage: /key <your-api-key>`));
|
|
518
|
-
return { multiConfig, config: resolveConfig(multiConfig) };
|
|
519
|
-
}
|
|
520
|
-
const name = multiConfig.activeProvider;
|
|
521
|
-
const updated = {
|
|
522
|
-
...multiConfig,
|
|
523
|
-
providers: {
|
|
524
|
-
...multiConfig.providers,
|
|
525
|
-
[name]: {
|
|
526
|
-
...multiConfig.providers[name] ?? { model: PROVIDERS[name]?.defaultModel ?? "" },
|
|
527
|
-
key: arg
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
};
|
|
531
|
-
saveMultiConfig(updated);
|
|
532
|
-
const masked = arg.slice(0, 6) + "\u2022".repeat(Math.max(0, arg.length - 10)) + arg.slice(-4);
|
|
533
|
-
console.log(GREEN(` \u2713 API key saved for ${name}: ${GRAY(masked)}
|
|
534
|
-
`));
|
|
535
|
-
return { multiConfig: updated, config: resolveConfig(updated) };
|
|
536
|
-
}
|
|
537
|
-
if (cmd === "/model") {
|
|
538
|
-
if (!arg) {
|
|
539
|
-
const name2 = multiConfig.activeProvider;
|
|
540
|
-
const current = multiConfig.providers[name2]?.model ?? PROVIDERS[name2]?.defaultModel;
|
|
541
|
-
console.log(GRAY(` Current model: `) + PURPLE2(current ?? "unknown"));
|
|
542
|
-
console.log(GRAY(` Usage: /model <model-id>
|
|
543
|
-
`));
|
|
544
|
-
return { multiConfig, config: resolveConfig(multiConfig) };
|
|
545
|
-
}
|
|
546
|
-
const name = multiConfig.activeProvider;
|
|
547
|
-
const updated = {
|
|
548
|
-
...multiConfig,
|
|
549
|
-
providers: {
|
|
550
|
-
...multiConfig.providers,
|
|
551
|
-
[name]: {
|
|
552
|
-
...multiConfig.providers[name] ?? { key: "" },
|
|
553
|
-
model: arg
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
};
|
|
557
|
-
saveMultiConfig(updated);
|
|
558
|
-
console.log(GREEN(` \u2713 Model set to ${PURPLE2(arg)} for ${name}
|
|
559
|
-
`));
|
|
560
|
-
return { multiConfig: updated, config: resolveConfig(updated) };
|
|
561
|
-
}
|
|
562
|
-
console.log(RED(` \u2717 Unknown command: "${cmd}"`));
|
|
563
|
-
console.log(GRAY(` Type /help to see available commands.
|
|
564
|
-
`));
|
|
565
|
-
return { multiConfig, config: resolveConfig(multiConfig) };
|
|
566
515
|
}
|
|
567
516
|
async function readLine(prompt) {
|
|
568
517
|
return new Promise((resolve4) => {
|
|
@@ -579,36 +528,35 @@ async function readLine(prompt) {
|
|
|
579
528
|
});
|
|
580
529
|
}
|
|
581
530
|
function statusLine(config) {
|
|
582
|
-
const
|
|
583
|
-
return PURPLE2(" \u25C6") + GRAY(` ${
|
|
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");
|
|
584
533
|
}
|
|
585
534
|
async function main() {
|
|
586
535
|
const args = process.argv.slice(2);
|
|
587
536
|
if (args[0] === "--help" || args[0] === "-h") {
|
|
588
537
|
console.log(BANNER);
|
|
589
538
|
console.log(PURPLE2(" Usage:"));
|
|
590
|
-
console.log(" " + PURPLE2("sharkcode") + GRAY("
|
|
591
|
-
console.log(" " + PURPLE2("sharkcode") + YELLOW(' "prompt"') + GRAY("
|
|
592
|
-
console.log("\n
|
|
593
|
-
printSlashHelp();
|
|
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"));
|
|
594
542
|
return;
|
|
595
543
|
}
|
|
596
544
|
if (args[0] === "--version" || args[0] === "-v") {
|
|
597
|
-
console.log("sharkcode v0.3.
|
|
545
|
+
console.log("sharkcode v0.3.1");
|
|
598
546
|
return;
|
|
599
547
|
}
|
|
600
548
|
if (args.length > 0) {
|
|
601
549
|
const mc = readMultiConfig();
|
|
602
550
|
const config2 = resolveConfig(mc);
|
|
603
551
|
if (!config2.apiKey) {
|
|
604
|
-
console.error(RED("\u274C
|
|
605
|
-
console.error(GRAY(" Run sharkcode in interactive mode and use /key <your-key>"));
|
|
552
|
+
console.error(RED("\u274C \u672A\u914D\u7F6E API Key\u3002\u8BF7\u5148\u8FD0\u884C sharkcode \u5E76\u8F93\u5165 / \u914D\u7F6E Provider\u3002"));
|
|
606
553
|
process.exit(1);
|
|
607
554
|
}
|
|
608
|
-
console.log(
|
|
609
|
-
`
|
|
610
|
-
|
|
611
|
-
|
|
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);
|
|
612
560
|
return;
|
|
613
561
|
}
|
|
614
562
|
console.log(BANNER);
|
|
@@ -617,54 +565,57 @@ async function main() {
|
|
|
617
565
|
console.log(statusLine(config));
|
|
618
566
|
if (!config.apiKey) {
|
|
619
567
|
console.log(
|
|
620
|
-
YELLOW(" \u26A0
|
|
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")
|
|
621
569
|
);
|
|
622
|
-
console.log(GRAY(" Use /key <your-api-key> to set it, or /provider to switch.\n"));
|
|
623
570
|
}
|
|
624
571
|
let messages = [];
|
|
625
572
|
while (true) {
|
|
626
|
-
const
|
|
627
|
-
if (
|
|
573
|
+
const raw = await readLine(PURPLE2("\n\u25C6 "));
|
|
574
|
+
if (raw === null) {
|
|
628
575
|
console.log(GRAY("\nBye! \u{1F988}"));
|
|
629
576
|
break;
|
|
630
577
|
}
|
|
631
|
-
const trimmed =
|
|
578
|
+
const trimmed = raw.trim();
|
|
632
579
|
if (!trimmed) continue;
|
|
633
580
|
if (trimmed === "exit" || trimmed === "quit") {
|
|
634
581
|
console.log(GRAY("Bye! \u{1F988}"));
|
|
635
582
|
break;
|
|
636
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
|
+
}
|
|
637
599
|
if (trimmed.startsWith("/")) {
|
|
638
|
-
|
|
639
|
-
if (!result) continue;
|
|
640
|
-
multiConfig = result.multiConfig;
|
|
641
|
-
config = result.config;
|
|
642
|
-
if (result.clearHistory) messages = [];
|
|
643
|
-
if (result.exit) break;
|
|
600
|
+
console.log(GRAY(" \u672A\u77E5\u547D\u4EE4\u3002\u8F93\u5165 / \u8C03\u51FA\u6307\u4EE4\u83DC\u5355\n"));
|
|
644
601
|
continue;
|
|
645
602
|
}
|
|
646
603
|
if (!config.apiKey) {
|
|
647
|
-
console.log(
|
|
648
|
-
YELLOW("\n \u26A0 No API key for ") + PURPLE2(PROVIDERS[config.providerName]?.label ?? config.providerName) + YELLOW(". Set it with /key <your-api-key>\n")
|
|
649
|
-
);
|
|
604
|
+
console.log(YELLOW(" \u26A0 \u8FD8\u672A\u586B\u5199 API Key\u3002\u8F93\u5165 / \u2192 \u5207\u6362 / \u914D\u7F6E Provider\n"));
|
|
650
605
|
continue;
|
|
651
606
|
}
|
|
652
607
|
messages.push({ role: "user", content: trimmed });
|
|
653
608
|
try {
|
|
654
609
|
messages = await runAgent(messages, config);
|
|
655
610
|
} catch (err) {
|
|
656
|
-
const error = err;
|
|
657
611
|
console.error(RED(`
|
|
658
|
-
\u274C
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}
|
|
662
|
-
messages = messages.slice(0, -1);
|
|
612
|
+
\u274C ${String(err)}
|
|
613
|
+
`));
|
|
614
|
+
messages.pop();
|
|
663
615
|
}
|
|
664
616
|
}
|
|
665
617
|
}
|
|
666
618
|
main().catch((err) => {
|
|
667
|
-
console.error(
|
|
668
|
-
\u274C Fatal: ${err.message}`));
|
|
619
|
+
console.error(chalk3.red(`Fatal: ${String(err)}`));
|
|
669
620
|
process.exit(1);
|
|
670
621
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sharkcode",
|
|
3
|
-
"version": "0.3.
|
|
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",
|