sharkcode 0.2.1 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +59 -39
  2. package/dist/cli.mjs +261 -40
  3. package/package.json +1 -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
- Then run:
12
+ Launch interactive mode:
15
13
 
16
14
  ```bash
17
- sharkcode "explain this codebase"
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
- ### Run from source
18
+ Or single-shot mode:
23
19
 
24
20
  ```bash
25
- git clone https://github.com/syy-shark/sharkcode.git
26
- cd sharkcode
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
- ## Configure API Key
25
+ ## Providers
32
26
 
33
- Shark Code reads `DEEPSEEK_API_KEY` from either an environment variable or `~/.sharkcode/config.toml`.
27
+ Shark Code supports multiple providers. Switch between them anytime with `/provider`.
34
28
 
35
- ### Option 1: Environment variable
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
- macOS / Linux:
34
+ ## Configure
38
35
 
39
- ```bash
40
- export DEEPSEEK_API_KEY=sk-xxxxxx
41
- ```
36
+ ### Option 1: Slash commands (recommended)
42
37
 
43
- Windows PowerShell:
38
+ Start `sharkcode`, then:
44
39
 
45
- ```powershell
46
- $env:DEEPSEEK_API_KEY="sk-xxxxxx"
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
- [api]
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
- ## Upgrade
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
- When you publish a new version to npm, users can upgrade with:
65
+ ### Option 3: Environment variables
63
66
 
64
- ```bash
65
- npm update -g sharkcode
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
- ## Publish
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
- npm login
72
- npm publish
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
- Every code update is published as a new npm version. Typical flow:
95
+ ## Upgrade
76
96
 
77
- 1. Update code.
78
- 2. Bump `version` in `package.json`.
79
- 3. Run `npm publish`.
97
+ ```bash
98
+ npm update -g sharkcode
99
+ ```
80
100
 
81
101
  ## How It Works
82
102
 
83
- ```text
84
- User input -> Prompt + Tools -> LLM -> Tool execution -> Result -> Repeat
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
@@ -9,44 +9,100 @@ import { parse } from "smol-toml";
9
9
  import { existsSync, readFileSync, mkdirSync, writeFileSync } from "fs";
10
10
  import { join } from "path";
11
11
  import { homedir } from "os";
12
+ var PROVIDERS = {
13
+ deepseek: {
14
+ baseURL: "https://api.deepseek.com/v1",
15
+ defaultModel: "deepseek-chat",
16
+ label: "DeepSeek \u5B98\u7F51"
17
+ },
18
+ ark: {
19
+ // Coding Plan uses a dedicated endpoint — do NOT use /api/v3 (that's the pay-per-use endpoint)
20
+ baseURL: "https://ark.cn-beijing.volces.com/api/coding/v3",
21
+ defaultModel: "ark-code-latest",
22
+ label: "\u65B9\u821F Coding Plan"
23
+ }
24
+ };
12
25
  var CONFIG_DIR = join(homedir(), ".sharkcode");
13
26
  var CONFIG_FILE = join(CONFIG_DIR, "config.toml");
14
- var DEFAULT_CONFIG = `# Shark Code Configuration
15
- # https://github.com/syy-ex/sharkcode
16
-
17
- [api]
18
- # Get your API key from https://platform.deepseek.com
19
- # Or set DEEPSEEK_API_KEY environment variable
20
- key = ""
21
- model = "deepseek-chat"
22
- base_url = "https://api.deepseek.com/v1"
23
- `;
24
- function ensureConfigDir() {
27
+ function serializeConfig(mc) {
28
+ const dp = mc.providers.deepseek;
29
+ const ap = mc.providers.ark;
30
+ return [
31
+ "# Shark Code Configuration",
32
+ "# https://github.com/syy-shark/sharkcode",
33
+ "",
34
+ "[default]",
35
+ `provider = "${mc.activeProvider}"`,
36
+ "",
37
+ "[providers.deepseek]",
38
+ "# API key from https://platform.deepseek.com",
39
+ `key = "${dp?.key ?? ""}"`,
40
+ `model = "${dp?.model ?? PROVIDERS.deepseek.defaultModel}"`,
41
+ "",
42
+ "[providers.ark]",
43
+ "# API key from https://ark.cn-beijing.volces.com (\u65B9\u821F Coding Plan)",
44
+ `key = "${ap?.key ?? ""}"`,
45
+ `model = "${ap?.model ?? PROVIDERS.ark.defaultModel}"`,
46
+ ""
47
+ ].join("\n");
48
+ }
49
+ function ensureConfig() {
25
50
  if (!existsSync(CONFIG_DIR)) {
26
51
  mkdirSync(CONFIG_DIR, { recursive: true });
27
52
  }
28
53
  if (!existsSync(CONFIG_FILE)) {
29
- writeFileSync(CONFIG_FILE, DEFAULT_CONFIG, "utf-8");
54
+ const initial = {
55
+ activeProvider: "deepseek",
56
+ providers: {
57
+ deepseek: { key: "", model: PROVIDERS.deepseek.defaultModel },
58
+ ark: { key: "", model: PROVIDERS.ark.defaultModel }
59
+ }
60
+ };
61
+ writeFileSync(CONFIG_FILE, serializeConfig(initial), "utf-8");
30
62
  }
31
63
  }
32
- function loadConfig() {
33
- ensureConfigDir();
64
+ function readMultiConfig() {
65
+ ensureConfig();
34
66
  let toml = {};
35
67
  try {
36
68
  const raw = readFileSync(CONFIG_FILE, "utf-8");
37
69
  toml = parse(raw);
38
70
  } catch {
39
71
  }
40
- const api = toml.api ?? {};
41
- const apiKey = process.env.DEEPSEEK_API_KEY || api.key || "";
42
- const model = api.model || "deepseek-chat";
43
- const baseURL = api.base_url || "https://api.deepseek.com/v1";
44
- if (!apiKey) {
45
- console.error("\u274C No API key found.");
46
- console.error(` Set DEEPSEEK_API_KEY env var or edit ${CONFIG_FILE}`);
47
- process.exit(1);
48
- }
49
- return { apiKey, model, baseURL };
72
+ const legacy = toml.api;
73
+ const providersRaw = toml.providers ?? {};
74
+ const defaultSection = toml.default ?? {};
75
+ const deepseekKey = process.env.DEEPSEEK_API_KEY || providersRaw.deepseek?.key || legacy?.key || "";
76
+ const arkKey = process.env.ARK_API_KEY || providersRaw.ark?.key || "";
77
+ const activeProvider = defaultSection.provider || (legacy ? "deepseek" : "deepseek");
78
+ return {
79
+ activeProvider,
80
+ providers: {
81
+ deepseek: {
82
+ key: deepseekKey,
83
+ model: providersRaw.deepseek?.model || legacy?.model || PROVIDERS.deepseek.defaultModel
84
+ },
85
+ ark: {
86
+ key: arkKey,
87
+ model: providersRaw.ark?.model || PROVIDERS.ark.defaultModel
88
+ }
89
+ }
90
+ };
91
+ }
92
+ function saveMultiConfig(mc) {
93
+ ensureConfig();
94
+ writeFileSync(CONFIG_FILE, serializeConfig(mc), "utf-8");
95
+ }
96
+ function resolveConfig(mc) {
97
+ const name = mc.activeProvider;
98
+ const entry = mc.providers[name];
99
+ const meta = PROVIDERS[name];
100
+ return {
101
+ providerName: name,
102
+ apiKey: entry?.key || "",
103
+ model: entry?.model || meta?.defaultModel || "deepseek-chat",
104
+ baseURL: meta?.baseURL || PROVIDERS.deepseek.baseURL
105
+ };
50
106
  }
51
107
 
52
108
  // src/agent.ts
@@ -210,7 +266,8 @@ function createProvider(config) {
210
266
  const provider = createOpenAI({
211
267
  baseURL: config.baseURL,
212
268
  apiKey: config.apiKey,
213
- name: "deepseek"
269
+ // ARK requires a non-default name to avoid SDK header overrides
270
+ name: config.providerName === "ark" ? "ark" : "deepseek"
214
271
  });
215
272
  return provider.chat(config.model);
216
273
  }
@@ -348,6 +405,11 @@ function truncate(str, max) {
348
405
 
349
406
  // src/cli.ts
350
407
  var PURPLE2 = chalk3.hex("#a855f7");
408
+ var GRAY = chalk3.gray;
409
+ var YELLOW = chalk3.yellow;
410
+ var GREEN = chalk3.green;
411
+ var RED = chalk3.red;
412
+ var CYAN = chalk3.cyan;
351
413
  var GLYPHS = {
352
414
  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
415
  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 +438,132 @@ function renderWord(word, padLeft = 2) {
376
438
  return rows;
377
439
  }
378
440
  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
+ }
456
+ 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) };
494
+ }
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) };
499
+ }
500
+ const updated = { ...multiConfig, activeProvider: name };
501
+ saveMultiConfig(updated);
502
+ const newConfig = resolveConfig(updated);
503
+ console.log(
504
+ GREEN(`
505
+ \u2713 Switched to ${PROVIDERS[name].label}`) + GRAY(` (${name}) \u2014 model: ${newConfig.model}`)
506
+ );
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();
512
+ }
513
+ return { multiConfig: updated, config: newConfig };
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
+ }
379
567
  async function readLine(prompt) {
380
568
  return new Promise((resolve4) => {
381
569
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
@@ -390,60 +578,93 @@ async function readLine(prompt) {
390
578
  });
391
579
  });
392
580
  }
581
+ 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");
584
+ }
393
585
  async function main() {
394
586
  const args = process.argv.slice(2);
395
587
  if (args[0] === "--help" || args[0] === "-h") {
396
588
  console.log(BANNER);
397
589
  console.log(PURPLE2(" Usage:"));
398
- console.log(" " + PURPLE2("sharkcode") + chalk3.gray(" \u2014 interactive mode"));
399
- console.log(" " + PURPLE2("sharkcode") + chalk3.yellow(' "prompt"') + chalk3.gray(" \u2014 single-shot mode\n"));
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();
400
594
  return;
401
595
  }
402
596
  if (args[0] === "--version" || args[0] === "-v") {
403
- console.log("sharkcode v0.2.0");
597
+ console.log("sharkcode v0.3.0");
404
598
  return;
405
599
  }
406
- const config = loadConfig();
407
600
  if (args.length > 0) {
408
- console.log(PURPLE2("\n\u{1F988} SharkCode") + chalk3.gray(` | model: ${config.model}
601
+ const mc = readMultiConfig();
602
+ const config2 = resolveConfig(mc);
603
+ 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>"));
606
+ process.exit(1);
607
+ }
608
+ console.log(PURPLE2("\n\u{1F988} SharkCode") + GRAY(` | ${PROVIDERS[config2.providerName]?.label ?? config2.providerName} | model: ${config2.model}
409
609
  `));
410
610
  const messages2 = [{ role: "user", content: args.join(" ") }];
411
- await runAgent(messages2, config);
611
+ await runAgent(messages2, config2);
412
612
  return;
413
613
  }
414
614
  console.log(BANNER);
415
- console.log(
416
- PURPLE2(" \u25C6") + chalk3.gray(` model: ${config.model}`) + chalk3.gray(' type "exit" to quit\n')
417
- );
615
+ let multiConfig = readMultiConfig();
616
+ let config = resolveConfig(multiConfig);
617
+ console.log(statusLine(config));
618
+ if (!config.apiKey) {
619
+ console.log(
620
+ YELLOW(" \u26A0 No API key configured for ") + PURPLE2(PROVIDERS[config.providerName]?.label ?? config.providerName) + YELLOW(".")
621
+ );
622
+ console.log(GRAY(" Use /key <your-api-key> to set it, or /provider to switch.\n"));
623
+ }
418
624
  let messages = [];
419
625
  while (true) {
420
626
  const input = await readLine(PURPLE2("\n\u25C6 "));
421
627
  if (input === null) {
422
- console.log(chalk3.gray("\nBye! \u{1F988}"));
628
+ console.log(GRAY("\nBye! \u{1F988}"));
423
629
  break;
424
630
  }
425
631
  const trimmed = input.trim();
426
632
  if (!trimmed) continue;
427
- if (trimmed === "exit" || trimmed === "quit" || trimmed === "/exit") {
428
- console.log(chalk3.gray("Bye! \u{1F988}"));
633
+ if (trimmed === "exit" || trimmed === "quit") {
634
+ console.log(GRAY("Bye! \u{1F988}"));
429
635
  break;
430
636
  }
637
+ 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;
644
+ continue;
645
+ }
646
+ 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
+ );
650
+ continue;
651
+ }
431
652
  messages.push({ role: "user", content: trimmed });
432
653
  try {
433
654
  messages = await runAgent(messages, config);
434
655
  } catch (err) {
435
656
  const error = err;
436
- console.error(chalk3.red(`
657
+ console.error(RED(`
437
658
  \u274C Error: ${error.message}`));
438
659
  if (error.message?.includes("401") || error.message?.includes("Unauthorized")) {
439
- console.error(chalk3.yellow(" Check your API key in ~/.sharkcode/config.toml"));
660
+ console.error(YELLOW(" Check your API key with /key <your-api-key>"));
440
661
  }
441
662
  messages = messages.slice(0, -1);
442
663
  }
443
664
  }
444
665
  }
445
666
  main().catch((err) => {
446
- console.error(chalk3.red(`
667
+ console.error(RED(`
447
668
  \u274C Fatal: ${err.message}`));
448
669
  process.exit(1);
449
670
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sharkcode",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Local First, open-source AI Coding Agent",
5
5
  "type": "module",
6
6
  "bin": {