dskcode 0.1.13 → 0.1.15

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 CHANGED
@@ -6,7 +6,7 @@
6
6
  [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
7
7
  [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
8
8
 
9
- ![dskcode](https://raw.githubusercontent.com/Awu12277/deepseek-agent-cli/refs/heads/main/public/dscoderun.gif)
9
+ ![dskcode](https://raw.githubusercontent.com/Awu12277/deepseek-agent-cli/refs/heads/main/public/dskcoderun.gif)
10
10
  ---
11
11
 
12
12
  ## 特性
package/dist/index.js CHANGED
@@ -487,9 +487,210 @@ async function promptForApiKey() {
487
487
  });
488
488
  }
489
489
 
490
+ // src/cli/skill-import.ts
491
+ import { createInterface as createInterface2 } from "readline";
492
+ import { existsSync as existsSync2, statSync, lstatSync } from "fs";
493
+ import { mkdir as mkdir2, readdir, cp, access, realpath } from "fs/promises";
494
+ import { join as join2 } from "path";
495
+ import chalk3 from "chalk";
496
+ function getClaudeSkillsDir() {
497
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
498
+ return join2(home, ".claude", "skills");
499
+ }
500
+ function getDskcodeSkillsDir() {
501
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
502
+ return join2(home, ".dskcode", "skills");
503
+ }
504
+ async function isSkillDir(dir) {
505
+ try {
506
+ await access(join2(dir, "SKILL.md"));
507
+ return true;
508
+ } catch {
509
+ return false;
510
+ }
511
+ }
512
+ async function listClaudeSkills() {
513
+ const claudeDir = getClaudeSkillsDir();
514
+ if (!existsSync2(claudeDir)) return [];
515
+ let entries;
516
+ try {
517
+ entries = await readdir(claudeDir);
518
+ } catch {
519
+ return [];
520
+ }
521
+ const skills = [];
522
+ for (const name of entries) {
523
+ const full = join2(claudeDir, name);
524
+ const stat = statSync(full);
525
+ if (stat.isDirectory() && await isSkillDir(full)) {
526
+ skills.push(name);
527
+ }
528
+ }
529
+ return skills;
530
+ }
531
+ function askConfirm(question) {
532
+ const rl = createInterface2({
533
+ input: process.stdin,
534
+ output: process.stdout
535
+ });
536
+ return new Promise((resolve) => {
537
+ let resolved = false;
538
+ const cleanup = () => {
539
+ if (resolved) return;
540
+ resolved = true;
541
+ process.stdin.removeListener("keypress", onKeypress);
542
+ rl.close();
543
+ };
544
+ const onKeypress = (_, key) => {
545
+ if (key.ctrl && key.name === "c") {
546
+ cleanup();
547
+ resolve(false);
548
+ }
549
+ };
550
+ process.stdin.on("keypress", onKeypress);
551
+ rl.question(question, (answer) => {
552
+ cleanup();
553
+ const trimmed = answer.trim().toLowerCase();
554
+ resolve(trimmed === "y" || trimmed === "yes");
555
+ });
556
+ });
557
+ }
558
+ async function importClaudeSkills(skillNames) {
559
+ const claudeDir = getClaudeSkillsDir();
560
+ const dskcodeDir = getDskcodeSkillsDir();
561
+ await mkdir2(dskcodeDir, { recursive: true });
562
+ const imported = [];
563
+ const skipped = [];
564
+ for (const name of skillNames) {
565
+ const src = join2(claudeDir, name);
566
+ const dest = join2(dskcodeDir, name);
567
+ if (existsSync2(dest)) {
568
+ skipped.push(name);
569
+ continue;
570
+ }
571
+ const realSrc = lstatSync(src).isSymbolicLink() ? await realpath(src) : src;
572
+ await cp(realSrc, dest, { recursive: true, dereference: true });
573
+ imported.push(name);
574
+ }
575
+ return { imported, skipped };
576
+ }
577
+ async function countDskcodeSkills() {
578
+ const dskcodeDir = getDskcodeSkillsDir();
579
+ if (!existsSync2(dskcodeDir)) return 0;
580
+ let entries;
581
+ try {
582
+ entries = await readdir(dskcodeDir);
583
+ } catch {
584
+ return 0;
585
+ }
586
+ let count = 0;
587
+ for (const name of entries) {
588
+ const full = join2(dskcodeDir, name);
589
+ if (statSync(full).isDirectory() && await isSkillDir(full)) {
590
+ count++;
591
+ }
592
+ }
593
+ return count;
594
+ }
595
+ function getProjectSkillDir(cwd) {
596
+ return join2(cwd, ".dskcode", "skill");
597
+ }
598
+ async function hasProjectLocalSkills(cwd) {
599
+ const count = await countProjectLocalSkills(cwd);
600
+ return count > 0;
601
+ }
602
+ async function countProjectLocalSkills(cwd) {
603
+ const skillDir = getProjectSkillDir(cwd);
604
+ if (!existsSync2(skillDir)) return 0;
605
+ let entries;
606
+ try {
607
+ entries = await readdir(skillDir);
608
+ } catch {
609
+ return 0;
610
+ }
611
+ let count = 0;
612
+ for (const name of entries) {
613
+ const full = join2(skillDir, name);
614
+ const stat = statSync(full);
615
+ if (stat.isDirectory() && await isSkillDir(full)) {
616
+ count++;
617
+ }
618
+ }
619
+ return count;
620
+ }
621
+ async function hasDskcodeSkills() {
622
+ const dskcodeDir = getDskcodeSkillsDir();
623
+ if (!existsSync2(dskcodeDir)) return false;
624
+ let entries;
625
+ try {
626
+ entries = await readdir(dskcodeDir);
627
+ } catch {
628
+ return false;
629
+ }
630
+ for (const name of entries) {
631
+ const full = join2(dskcodeDir, name);
632
+ const stat = statSync(full);
633
+ if (stat.isDirectory() && await isSkillDir(full)) {
634
+ return true;
635
+ }
636
+ }
637
+ return false;
638
+ }
639
+ async function promptImportClaudeSkills(cwd) {
640
+ if (cwd) {
641
+ const hasLocal = await hasProjectLocalSkills(cwd);
642
+ if (hasLocal) {
643
+ console.log(chalk3.dim(" \u68C0\u6D4B\u5230\u9879\u76EE\u672C\u5730 .dskcode/skill\uFF0C\u8DF3\u8FC7\u5BFC\u5165 Claude Code skill\n"));
644
+ return;
645
+ }
646
+ }
647
+ const hasGlobal = await hasDskcodeSkills();
648
+ if (hasGlobal) {
649
+ return;
650
+ }
651
+ const skills = await listClaudeSkills();
652
+ if (skills.length === 0) return;
653
+ console.log(chalk3.cyan(`
654
+ \u2726 \u68C0\u6D4B\u5230\u4F60\u5728 Claude Code \u4E2D\u5B89\u88C5\u4E86 ${String(skills.length)} \u4E2A skill\uFF1A`));
655
+ const preview = skills.slice(0, 5).map((s) => chalk3.dim(` \xB7 ${s}`)).join("\n");
656
+ console.log(preview);
657
+ if (skills.length > 5) {
658
+ console.log(chalk3.dim(` \xB7 ...\u7B49 ${String(skills.length)} \u4E2A`));
659
+ }
660
+ console.log(chalk3.dim(` \u6E90\u76EE\u5F55: ${getClaudeSkillsDir()}`));
661
+ console.log(chalk3.dim(` \u76EE\u6807\u76EE\u5F55: ${getDskcodeSkillsDir()}
662
+ `));
663
+ const confirmed = await askConfirm(
664
+ ` ${chalk3.cyan("\u{1F4E6}")} \u662F\u5426\u5C06\u8FD9\u4E9B skill \u5BFC\u5165\u5230 dskcode\uFF1F${chalk3.dim("[y/N] ")} `
665
+ );
666
+ if (!confirmed) {
667
+ console.log(chalk3.dim(" \u5DF2\u8DF3\u8FC7 skill \u5BFC\u5165\n"));
668
+ return;
669
+ }
670
+ try {
671
+ const { imported, skipped } = await importClaudeSkills(skills);
672
+ if (imported.length > 0) {
673
+ console.log(
674
+ chalk3.green(` \u2714 \u5DF2\u5BFC\u5165 ${String(imported.length)} \u4E2A skill \u5230 ~/.dskcode/skills`)
675
+ );
676
+ }
677
+ if (skipped.length > 0) {
678
+ console.log(
679
+ chalk3.yellow(` \u26A0 ${String(skipped.length)} \u4E2A skill \u5DF2\u5B58\u5728\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${skipped.join(", ")}`)
680
+ );
681
+ }
682
+ console.log("");
683
+ } catch (err) {
684
+ console.log(
685
+ chalk3.red(` \u2716 \u5BFC\u5165 skill \u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`)
686
+ );
687
+ console.log(chalk3.dim(" \u4F60\u53EF\u4EE5\u7A0D\u540E\u624B\u52A8\u590D\u5236 ~/.claude/skills \u5230 ~/.dskcode/skills\n"));
688
+ }
689
+ }
690
+
490
691
  // src/cli/index.tsx
491
692
  import { readFile as readFile3 } from "fs/promises";
492
- import { join as join3 } from "path";
693
+ import { join as join4 } from "path";
493
694
 
494
695
  // src/ui/RenderScope.tsx
495
696
  import { render } from "ink";
@@ -567,8 +768,8 @@ function useDoubleCtrlC(onExit) {
567
768
  import { Box as Box4, Text as Text5 } from "ink";
568
769
 
569
770
  // src/provider/cost-tracker.ts
570
- import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
571
- import { join as join2 } from "path";
771
+ import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
772
+ import { join as join3 } from "path";
572
773
  var CostTracker = class {
573
774
  #costDir;
574
775
  #budgetLimit;
@@ -586,7 +787,7 @@ var CostTracker = class {
586
787
  #flushInProgress = false;
587
788
  constructor(options = {}) {
588
789
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
589
- this.#costDir = options.costDir ?? join2(home, ".dskcode", "costs");
790
+ this.#costDir = options.costDir ?? join3(home, ".dskcode", "costs");
590
791
  this.#budgetLimit = options.budgetLimit ?? 0;
591
792
  this.#tokenBudgetLimit = options.tokenBudgetLimit ?? 0;
592
793
  this.#onBudgetExceeded = options.onBudgetExceeded;
@@ -800,7 +1001,7 @@ var CostTracker = class {
800
1001
  }
801
1002
  /** 从磁盘加载成本存储 */
802
1003
  async #loadStore() {
803
- const filePath = join2(this.#costDir, "history.json");
1004
+ const filePath = join3(this.#costDir, "history.json");
804
1005
  try {
805
1006
  const raw = await readFile2(filePath, "utf-8");
806
1007
  return JSON.parse(raw);
@@ -819,8 +1020,8 @@ var CostTracker = class {
819
1020
  for (const date of datesToRemove) {
820
1021
  delete store.daily[date];
821
1022
  }
822
- await mkdir2(this.#costDir, { recursive: true });
823
- const filePath = join2(this.#costDir, "history.json");
1023
+ await mkdir3(this.#costDir, { recursive: true });
1024
+ const filePath = join3(this.#costDir, "history.json");
824
1025
  await writeFile2(filePath, JSON.stringify(store, null, 2), "utf-8");
825
1026
  }
826
1027
  };
@@ -1435,7 +1636,7 @@ function pickRandom(arr) {
1435
1636
  return arr[Math.floor(Math.random() * arr.length)];
1436
1637
  }
1437
1638
  function ChatSession({
1438
- providerCount,
1639
+ skillCount,
1439
1640
  toolCount,
1440
1641
  verbose,
1441
1642
  apiKey,
@@ -1822,8 +2023,8 @@ function ChatSession({
1822
2023
  /* @__PURE__ */ jsxs5(Text6, { color: "#00ff41", children: [
1823
2024
  " \u2714 ",
1824
2025
  "\u5DF2\u52A0\u8F7D ",
1825
- providerCount,
1826
- " \u4E2A Provider"
2026
+ skillCount,
2027
+ " \u4E2A Skill"
1827
2028
  ] }),
1828
2029
  /* @__PURE__ */ jsxs5(Text6, { color: "#00ffff", children: [
1829
2030
  " \u2139 ",
@@ -2822,7 +3023,7 @@ function initGames() {
2822
3023
 
2823
3024
  // src/cli/index.tsx
2824
3025
  import { render as render4 } from "ink";
2825
- import chalk3 from "chalk";
3026
+ import chalk4 from "chalk";
2826
3027
 
2827
3028
  // src/stock/StockList.tsx
2828
3029
  import { Box as Box9, Text as Text10, useInput as useInput5 } from "ink";
@@ -3155,18 +3356,24 @@ function createCli() {
3155
3356
  const key = await promptForApiKey();
3156
3357
  if (!key) process.exit(1);
3157
3358
  const savedPath = await saveApiKey(key);
3158
- console.log(` ${chalk3.green("\u2714")} API Key \u5DF2\u4FDD\u5B58\u5230 ${chalk3.dim(savedPath)}
3359
+ console.log(` ${chalk4.green("\u2714")} API Key \u5DF2\u4FDD\u5B58\u5230 ${chalk4.dim(savedPath)}
3159
3360
  `);
3160
3361
  const result = await loadAndValidate();
3161
3362
  ctx = { ...ctx, config: result.config };
3162
3363
  }
3364
+ await promptImportClaudeSkills(process.cwd());
3163
3365
  const costTracker = new CostTracker({
3164
3366
  budgetLimit: ctx?.config.budgetLimit ?? 0,
3165
3367
  tokenBudgetLimit: ctx?.config.tokenBudgetLimit ?? 0
3166
3368
  });
3167
3369
  startChat(ctx, costTracker);
3168
3370
  });
3169
- function startChat(ctx, costTracker) {
3371
+ async function startChat(ctx, costTracker) {
3372
+ const [globalSkillCount, localSkillCount] = await Promise.all([
3373
+ countDskcodeSkills(),
3374
+ countProjectLocalSkills(process.cwd())
3375
+ ]);
3376
+ const skillCount = globalSkillCount + localSkillCount;
3170
3377
  const defaultProvider = ctx?.config.providers.find(
3171
3378
  (p) => p.name === (ctx?.config.defaultProvider ?? "deepseek")
3172
3379
  );
@@ -3175,7 +3382,7 @@ function createCli() {
3175
3382
  /* @__PURE__ */ jsx10(
3176
3383
  ChatSession,
3177
3384
  {
3178
- providerCount: ctx?.config.providers.length ?? 1,
3385
+ skillCount,
3179
3386
  toolCount: ctx?.config.tools.length ?? 0,
3180
3387
  verbose: ctx?.verbose ?? false,
3181
3388
  apiKey: defaultProvider?.apiKey,
@@ -3276,7 +3483,7 @@ compdef _dskcode_completion dskcode`);
3276
3483
  program2.command("stock").description("\u67E5\u770B\u81EA\u9009\u80A1\u5B9E\u65F6\u884C\u60C5").argument("[codes...]", "\u80A1\u7968\u4EE3\u7801\uFF08\u7A7A\u683C\u5206\u9694\uFF09\uFF0C\u5982 513090 600519").action(async function(codes) {
3277
3484
  const ctx = this.dskcodeCtx;
3278
3485
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
3279
- const globalConfigPath = join3(home, ".dskcode", "settings.json");
3486
+ const globalConfigPath = join4(home, ".dskcode", "settings.json");
3280
3487
  let globalConfigHasStock = false;
3281
3488
  try {
3282
3489
  const raw = await readFile3(globalConfigPath, "utf-8");
@@ -3292,8 +3499,8 @@ compdef _dskcode_completion dskcode`);
3292
3499
  { code: "sh601899" }
3293
3500
  ];
3294
3501
  const savedPath = await saveStockConfig(defaultSymbols);
3295
- console.log(`${chalk3.green("\u2714")} \u5DF2\u751F\u6210\u81EA\u9009\u80A1\u914D\u7F6E: ${chalk3.dim(savedPath)}`);
3296
- console.log(`${chalk3.dim(" \u63D0\u793A: \u53EF\u7F16\u8F91\u4E0A\u8FF0\u6587\u4EF6\u81EA\u5B9A\u4E49\u81EA\u9009\u80A1\u5217\u8868")}
3502
+ console.log(`${chalk4.green("\u2714")} \u5DF2\u751F\u6210\u81EA\u9009\u80A1\u914D\u7F6E: ${chalk4.dim(savedPath)}`);
3503
+ console.log(`${chalk4.dim(" \u63D0\u793A: \u53EF\u7F16\u8F91\u4E0A\u8FF0\u6587\u4EF6\u81EA\u5B9A\u4E49\u81EA\u9009\u80A1\u5217\u8868")}
3297
3504
  `);
3298
3505
  }
3299
3506
  const freshResult = await loadAndValidate();
@@ -3347,7 +3554,7 @@ compdef _dskcode_completion dskcode`);
3347
3554
  });
3348
3555
  if (selectedGame) {
3349
3556
  console.log(`
3350
- \u542F\u52A8\u6E38\u620F: ${chalk3.green(selectedGame.name)}
3557
+ \u542F\u52A8\u6E38\u620F: ${chalk4.green(selectedGame.name)}
3351
3558
  `);
3352
3559
  await selectedGame.play();
3353
3560
  }