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.
Files changed (2) hide show
  1. package/dist/cli.mjs +102 -151
  2. 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 printSlashHelp() {
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
- function handleSlash(input, multiConfig) {
459
- const parts = input.trim().split(/\s+/);
460
- const cmd = parts[0].toLowerCase();
461
- const arg = parts.slice(1).join(" ");
462
- if (cmd === "/" || cmd === "/help") {
463
- printSlashHelp();
464
- return { multiConfig, config: resolveConfig(multiConfig) };
465
- }
466
- if (cmd === "/exit" || cmd === "/quit") {
467
- console.log(GRAY("Bye! \u{1F988}"));
468
- return { multiConfig, config: resolveConfig(multiConfig), exit: true };
469
- }
470
- if (cmd === "/clear") {
471
- console.log(GRAY(" \u2713 Conversation cleared."));
472
- return { multiConfig, config: resolveConfig(multiConfig), clearHistory: true };
473
- }
474
- if (cmd === "/provider") {
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
- const name = arg.toLowerCase();
496
- if (!PROVIDERS[name]) {
497
- console.log(RED(` \u2717 Unknown provider: "${name}". Available: ${Object.keys(PROVIDERS).join(", ")}`));
498
- return { multiConfig, config: resolveConfig(multiConfig) };
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
- console.log(
504
- GREEN(`
505
- \u2713 Switched to ${PROVIDERS[name].label}`) + GRAY(` (${name}) \u2014 model: ${newConfig.model}`)
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(` \u26A0 No API key set. Use /key <your-key> to configure it.
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 providerLabel = PROVIDERS[config.providerName]?.label ?? config.providerName;
583
- return PURPLE2(" \u25C6") + GRAY(` ${providerLabel}`) + CYAN(` [${config.model}]`) + GRAY(" type /help for commands\n");
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(" \u2014 interactive mode"));
591
- console.log(" " + PURPLE2("sharkcode") + YELLOW(' "prompt"') + GRAY(" \u2014 single-shot mode"));
592
- console.log("\n " + PURPLE2("Slash commands (in interactive mode):"));
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.0");
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 No API key found for provider: " + config2.providerName));
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(PURPLE2("\n\u{1F988} SharkCode") + GRAY(` | ${PROVIDERS[config2.providerName]?.label ?? config2.providerName} | model: ${config2.model}
609
- `));
610
- const messages2 = [{ role: "user", content: args.join(" ") }];
611
- await runAgent(messages2, config2);
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 No API key configured for ") + PURPLE2(PROVIDERS[config.providerName]?.label ?? config.providerName) + YELLOW(".")
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 input = await readLine(PURPLE2("\n\u25C6 "));
627
- if (input === null) {
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 = input.trim();
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
- const result = handleSlash(trimmed, multiConfig);
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 Error: ${error.message}`));
659
- if (error.message?.includes("401") || error.message?.includes("Unauthorized")) {
660
- console.error(YELLOW(" Check your API key with /key <your-api-key>"));
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(RED(`
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.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",