@ksm0709/context 0.0.34 → 0.0.36

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.
@@ -20,7 +20,7 @@ function resolveContextDir(projectDir) {
20
20
  // package.json
21
21
  var package_default = {
22
22
  name: "@ksm0709/context",
23
- version: "0.0.34",
23
+ version: "0.0.36",
24
24
  author: {
25
25
  name: "TaehoKang",
26
26
  email: "ksm07091@gmail.com"
@@ -62,24 +62,23 @@ var package_default = {
62
62
  "@opencode-ai/plugin": ">=1.0.0"
63
63
  },
64
64
  dependencies: {
65
- "@ksm0709/context": "^0.0.33",
66
65
  "@modelcontextprotocol/sdk": "^1.27.1",
67
66
  "jsonc-parser": "^3.0.0"
68
67
  },
69
68
  devDependencies: {
70
- "@opencode-ai/plugin": "^1.2.10",
71
69
  "@eslint/js": "^9.39.1",
70
+ "@opencode-ai/plugin": "^1.2.10",
72
71
  "@types/node": "^20.11.5",
73
72
  "@typescript-eslint/eslint-plugin": "8.47.0",
74
73
  "@typescript-eslint/parser": "8.47.0",
74
+ "@vitest/coverage-v8": "^3.2.4",
75
75
  "bun-types": "latest",
76
76
  eslint: "^9.39.1",
77
77
  "eslint-config-prettier": "10.1.8",
78
78
  "eslint-plugin-prettier": "^5.1.3",
79
79
  prettier: "^3.2.4",
80
80
  "typescript-eslint": "^8.47.0",
81
- vitest: "^3.2.4",
82
- "@vitest/coverage-v8": "^3.2.4"
81
+ vitest: "^3.2.4"
83
82
  }
84
83
  };
85
84
 
@@ -319,6 +318,8 @@ var DEFAULT_DAILY_NOTE_GUIDE = `# \uB370\uC77C\uB9AC \uB178\uD2B8 \uAE30\uB85D \
319
318
  var DEFAULT_NOTE_GUIDE = `# \uC9C0\uC2DD \uB178\uD2B8 \uC791\uC131 \uBC0F \uAD00\uB9AC \uAC00\uC774\uB4DC
320
319
 
321
320
  - [ ] **\uB178\uD2B8 \uC0DD\uC131**: \`context_mcp_create_knowledge_note\` \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD558\uC5EC \uC0DD\uC131\uD558\uC138\uC694.
321
+ - [ ] \uD15C\uD50C\uB9BF \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uBA3C\uC800 \`.context/templates/<template>.md\`\uB97C \uC77D\uACE0, **\uC644\uC131\uB41C markdown \uC804\uCCB4**\uB97C \`content\`\uB85C \uC804\uB2EC\uD558\uC138\uC694.
322
+ - [ ] \uD15C\uD50C\uB9BF \uBAA8\uB4DC\uC5D0\uC11C\uB294 \`tags\`/\`linked_notes\`\uB97C \uB530\uB85C \uB118\uAE30\uC9C0 \uB9C8\uC138\uC694. \uAD00\uB828 \uB178\uD2B8\uC640 \uBA54\uD0C0\uB370\uC774\uD130\uB294 markdown \uBCF8\uBB38\uC5D0 \uC9C1\uC811 \uC791\uC131\uD574\uC57C \uD569\uB2C8\uB2E4.
322
323
  - [ ] \uC81C\uD154\uCE74\uC2A4\uD150(Zettelkasten) 3\uB300 \uC6D0\uCE59 \uC900\uC218:
323
324
  - [ ] \uC6D0\uC790\uC131: \uD55C \uB178\uD2B8\uB2F9 \uD55C \uC8FC\uC81C
324
325
  - [ ] \uC5F0\uACB0: \uACE0\uB9BD\uB41C \uB178\uD2B8 \uBC29\uC9C0
@@ -482,14 +483,17 @@ var STATIC_KNOWLEDGE_CONTEXT = `## Knowledge Context
482
483
  3. **\uC790\uAE30 \uC5B8\uC5B4** -- \uBCF5\uC0AC-\uBD99\uC5EC\uB123\uAE30\uAC00 \uC544\uB2CC, \uD575\uC2EC\uC744 \uC774\uD574\uD558\uACE0 \uAC04\uACB0\uD558\uAC8C \uC11C\uC220\uD558\uC138\uC694.
483
484
 
484
485
  ### MCP Tools
485
- - **\uC9C0\uC2DD \uAD00\uB9AC**: \`context_mcp_search_knowledge\`, \`context_mcp_read_knowledge\`, \`context_mcp_create_knowledge_note\`, \`context_mcp_update_knowledge_note\`
486
+ - **\uC9C0\uC2DD \uD0D0\uC0C9**: \`context_mcp_search_knowledge\`\uB85C \uD6C4\uBCF4 \uB178\uD2B8\uB97C \uCC3E\uACE0, \uACB0\uACFC\uC758 title / description / tags / path / score\uB97C \uBA3C\uC800 \uBE44\uAD50\uD558\uC138\uC694.
487
+ - **\uB178\uD2B8 \uC5F4\uAE30**: \`context_mcp_read_knowledge\`\uB85C \uC120\uD0DD\uD55C \uB178\uD2B8\uB97C \uC5F4\uACE0, \uB05D\uC5D0 \uBD99\uB294 related notes \uBA54\uD0C0\uB370\uC774\uD130\uB85C \uB2E4\uC74C \uD0D0\uC0C9 \uACBD\uB85C\uB97C \uC815\uD558\uC138\uC694.
488
+ - **\uB178\uD2B8 \uC791\uC131/\uC218\uC815**: \`context_mcp_create_knowledge_note\`, \`context_mcp_update_knowledge_note\`
486
489
  - **\uB370\uC77C\uB9AC \uB178\uD2B8**: \`context_mcp_read_daily_note\`, \`context_mcp_append_daily_note\`
487
490
  - **\uC791\uC5C5 \uC644\uB8CC**: \`context_mcp_submit_turn_complete\` (\uC791\uC5C5 \uC885\uB8CC \uC2DC \uD544\uC218 \uD638\uCD9C)
488
491
 
489
492
  ### \uC791\uC5C5 \uC804 \uD544\uC218
490
493
  - **\uB370\uC77C\uB9AC \uB178\uD2B8 \uD655\uC778**: \uAC00\uC7A5 \uCD5C\uADFC\uC758 \uB370\uC77C\uB9AC \uB178\uD2B8\uB97C \uC77D\uACE0 \uC774\uC804 \uC138\uC158\uC758 \uCEE8\uD14D\uC2A4\uD2B8\uC640 \uBBF8\uD574\uACB0 \uC774\uC288\uB97C \uD30C\uC545\uD558\uC138\uC694.
491
494
  - **\uC791\uC5C5 \uC758\uB3C4 \uC120\uC5B8**: \uC791\uC5C5 \uC2DC\uC791 \uC804, \uD604\uC7AC \uC138\uC158\uC758 \uBAA9\uD45C\uC640 \uC791\uC5C5 \uC758\uB3C4\uB97C \uBA85\uD655\uD788 \uD30C\uC545\uD558\uACE0 \uC120\uC5B8\uD558\uC138\uC694.
492
- - **\uC9C0\uC2DD \uAC80\uC0C9**: \uC791\uC5C5\uACFC \uAD00\uB828\uB41C \uBB38\uC11C\uB97C **\uC9C1\uC811 \uBA3C\uC800** \uAC80\uC0C9\uD558\uACE0 \uC77D\uC73C\uC138\uC694.
495
+ - **\uBA54\uD0C0\uB370\uC774\uD130 \uC6B0\uC120 \uAC80\uC0C9**: \uBA3C\uC800 \`search_knowledge\`\uB85C \uAD00\uB828 \uD6C4\uBCF4\uB97C \uCC3E\uACE0, \uBA54\uD0C0\uB370\uC774\uD130\uB97C \uBE44\uAD50\uD55C \uB4A4 \uAC00\uC7A5 \uC720\uB825\uD55C \uB178\uD2B8\uB97C \`read_knowledge\`\uB85C \uC5EC\uC138\uC694.
496
+ - **\uAD00\uB828 \uB178\uD2B8 \uCD94\uC801**: \`read_knowledge\` \uB05D\uC758 related notes \uC139\uC158\uC5D0\uC11C \uC5F0\uACB0\uB41C \uB178\uD2B8\uC758 title / description / tags / path\uB97C \uD655\uC778\uD558\uACE0 \uD544\uC694\uC2DC \uC5F0\uC1C4 \uD0D0\uC0C9\uD558\uC138\uC694.
493
497
  - \uC9C0\uC2DD \uD30C\uC77C\uC5D0 \uAE30\uB85D\uB41C \uC544\uD0A4\uD14D\uCC98 \uACB0\uC815, \uD328\uD134, \uC81C\uC57D\uC0AC\uD56D\uC744 \uBC18\uB4DC\uC2DC \uB530\uB974\uC138\uC694.
494
498
 
495
499
  ### \uAC1C\uBC1C \uC6D0\uCE59
@@ -1,6 +1,6 @@
1
1
  // src/omx/index.ts
2
- import { existsSync as existsSync6, readFileSync as readFileSync5, unlinkSync } from "node:fs";
3
- import { join as join6 } from "node:path";
2
+ import { existsSync as existsSync7, readFileSync as readFileSync6, unlinkSync } from "node:fs";
3
+ import { join as join7 } from "node:path";
4
4
 
5
5
  // src/constants.ts
6
6
  var DEFAULTS = {
@@ -105,7 +105,7 @@ import { join as join3 } from "node:path";
105
105
  // package.json
106
106
  var package_default = {
107
107
  name: "@ksm0709/context",
108
- version: "0.0.34",
108
+ version: "0.0.36",
109
109
  author: {
110
110
  name: "TaehoKang",
111
111
  email: "ksm07091@gmail.com"
@@ -147,24 +147,23 @@ var package_default = {
147
147
  "@opencode-ai/plugin": ">=1.0.0"
148
148
  },
149
149
  dependencies: {
150
- "@ksm0709/context": "^0.0.33",
151
150
  "@modelcontextprotocol/sdk": "^1.27.1",
152
151
  "jsonc-parser": "^3.0.0"
153
152
  },
154
153
  devDependencies: {
155
- "@opencode-ai/plugin": "^1.2.10",
156
154
  "@eslint/js": "^9.39.1",
155
+ "@opencode-ai/plugin": "^1.2.10",
157
156
  "@types/node": "^20.11.5",
158
157
  "@typescript-eslint/eslint-plugin": "8.47.0",
159
158
  "@typescript-eslint/parser": "8.47.0",
159
+ "@vitest/coverage-v8": "^3.2.4",
160
160
  "bun-types": "latest",
161
161
  eslint: "^9.39.1",
162
162
  "eslint-config-prettier": "10.1.8",
163
163
  "eslint-plugin-prettier": "^5.1.3",
164
164
  prettier: "^3.2.4",
165
165
  "typescript-eslint": "^8.47.0",
166
- vitest: "^3.2.4",
167
- "@vitest/coverage-v8": "^3.2.4"
166
+ vitest: "^3.2.4"
168
167
  }
169
168
  };
170
169
 
@@ -404,6 +403,8 @@ var DEFAULT_DAILY_NOTE_GUIDE = `# 데일리 노트 기록 가이드
404
403
  var DEFAULT_NOTE_GUIDE = `# 지식 노트 작성 및 관리 가이드
405
404
 
406
405
  - [ ] **노트 생성**: \`context_mcp_create_knowledge_note\` 도구를 사용하여 생성하세요.
406
+ - [ ] 템플릿 모드에서는 먼저 \`.context/templates/<template>.md\`를 읽고, **완성된 markdown 전체**를 \`content\`로 전달하세요.
407
+ - [ ] 템플릿 모드에서는 \`tags\`/\`linked_notes\`를 따로 넘기지 마세요. 관련 노트와 메타데이터는 markdown 본문에 직접 작성해야 합니다.
407
408
  - [ ] 제텔카스텐(Zettelkasten) 3대 원칙 준수:
408
409
  - [ ] 원자성: 한 노트당 한 주제
409
410
  - [ ] 연결: 고립된 노트 방지
@@ -555,6 +556,59 @@ function injectIntoAgentsMd(agentsMdPath, content) {
555
556
  writeFileAtomically(agentsMdPath, nextContent);
556
557
  }
557
558
 
559
+ // src/shared/codex-settings.ts
560
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
561
+ import { homedir } from "node:os";
562
+ import { isAbsolute, join as join4 } from "node:path";
563
+ var STALE_MOCK_MCP_SERVER_NAME = "mock-mcp";
564
+ var codexConfigPath = join4(homedir(), ".codex", "config.toml");
565
+ function findMcpServerBlockRange(lines, serverName) {
566
+ const header = `[mcp_servers.${serverName}]`;
567
+ const start = lines.findIndex((line) => line.trim() === header);
568
+ if (start === -1) {
569
+ return null;
570
+ }
571
+ let end = lines.length;
572
+ for (let index = start + 1;index < lines.length; index += 1) {
573
+ if (lines[index].startsWith("[")) {
574
+ end = index;
575
+ break;
576
+ }
577
+ }
578
+ return { start, end };
579
+ }
580
+ function resolveFirstArgPath(blockLines) {
581
+ for (const line of blockLines) {
582
+ const match = line.match(/^\s*args\s*=\s*\[\s*["']([^"']+)["']/);
583
+ if (!match) {
584
+ continue;
585
+ }
586
+ return match[1];
587
+ }
588
+ return null;
589
+ }
590
+ function pruneStaleMockMcpServer() {
591
+ if (!existsSync4(codexConfigPath)) {
592
+ return false;
593
+ }
594
+ const content = readFileSync4(codexConfigPath, "utf8");
595
+ const lines = content.split(`
596
+ `);
597
+ const blockRange = findMcpServerBlockRange(lines, STALE_MOCK_MCP_SERVER_NAME);
598
+ if (!blockRange) {
599
+ return false;
600
+ }
601
+ const blockLines = lines.slice(blockRange.start, blockRange.end);
602
+ const firstArgPath = resolveFirstArgPath(blockLines);
603
+ if (!firstArgPath || !isAbsolute(firstArgPath) || existsSync4(firstArgPath)) {
604
+ return false;
605
+ }
606
+ const nextLines = [...lines.slice(0, blockRange.start), ...lines.slice(blockRange.end)];
607
+ writeFileSync3(codexConfigPath, nextLines.join(`
608
+ `), "utf8");
609
+ return true;
610
+ }
611
+
558
612
  // src/shared/knowledge-context.ts
559
613
  var STATIC_KNOWLEDGE_CONTEXT = `## Knowledge Context
560
614
 
@@ -567,14 +621,17 @@ var STATIC_KNOWLEDGE_CONTEXT = `## Knowledge Context
567
621
  3. **자기 언어** -- 복사-붙여넣기가 아닌, 핵심을 이해하고 간결하게 서술하세요.
568
622
 
569
623
  ### MCP Tools
570
- - **지식 관리**: \`context_mcp_search_knowledge\`, \`context_mcp_read_knowledge\`, \`context_mcp_create_knowledge_note\`, \`context_mcp_update_knowledge_note\`
624
+ - **지식 탐색**: \`context_mcp_search_knowledge\`로 후보 노트를 찾고, 결과의 title / description / tags / path / score를 먼저 비교하세요.
625
+ - **노트 열기**: \`context_mcp_read_knowledge\`로 선택한 노트를 열고, 끝에 붙는 related notes 메타데이터로 다음 탐색 경로를 정하세요.
626
+ - **노트 작성/수정**: \`context_mcp_create_knowledge_note\`, \`context_mcp_update_knowledge_note\`
571
627
  - **데일리 노트**: \`context_mcp_read_daily_note\`, \`context_mcp_append_daily_note\`
572
628
  - **작업 완료**: \`context_mcp_submit_turn_complete\` (작업 종료 시 필수 호출)
573
629
 
574
630
  ### 작업 전 필수
575
631
  - **데일리 노트 확인**: 가장 최근의 데일리 노트를 읽고 이전 세션의 컨텍스트와 미해결 이슈를 파악하세요.
576
632
  - **작업 의도 선언**: 작업 시작 전, 현재 세션의 목표와 작업 의도를 명확히 파악하고 선언하세요.
577
- - **지식 검색**: 작업과 관련된 문서를 **직접 먼저** 검색하고 읽으세요.
633
+ - **메타데이터 우선 검색**: 먼저 \`search_knowledge\`로 관련 후보를 찾고, 메타데이터를 비교한 뒤 가장 유력한 노트를 \`read_knowledge\`로 여세요.
634
+ - **관련 노트 추적**: \`read_knowledge\` 끝의 related notes 섹션에서 연결된 노트의 title / description / tags / path를 확인하고 필요시 연쇄 탐색하세요.
578
635
  - 지식 파일에 기록된 아키텍처 결정, 패턴, 제약사항을 반드시 따르세요.
579
636
 
580
637
  ### 개발 원칙
@@ -593,32 +650,32 @@ var STATIC_KNOWLEDGE_CONTEXT = `## Knowledge Context
593
650
  - 필요한 인자: daily_note_update_proof, knowledge_note_proof, quality_check_output, checkpoint_commit_hashes, scope_review_notes`;
594
651
 
595
652
  // src/omx/registry.ts
596
- import { join as join5, dirname as dirname3 } from "node:path";
597
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
653
+ import { join as join6, dirname as dirname3 } from "node:path";
654
+ import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "node:fs";
598
655
  import { execSync } from "node:child_process";
599
- import { homedir } from "node:os";
656
+ import { homedir as homedir2 } from "node:os";
600
657
 
601
658
  // src/shared/mcp-path.ts
602
659
  import { fileURLToPath } from "node:url";
603
- import { dirname as dirname2, join as join4, resolve } from "node:path";
604
- import { existsSync as existsSync4 } from "node:fs";
660
+ import { dirname as dirname2, join as join5, resolve } from "node:path";
661
+ import { existsSync as existsSync5 } from "node:fs";
605
662
  import { createRequire } from "node:module";
606
663
  function resolveMcpPath() {
607
664
  try {
608
665
  const req = createRequire(import.meta.url);
609
666
  const pkgJsonPath = req.resolve("@ksm0709/context/package.json");
610
667
  const pkgRoot = dirname2(pkgJsonPath);
611
- const distMcp = join4(pkgRoot, "dist", "mcp.js");
612
- if (existsSync4(distMcp))
668
+ const distMcp = join5(pkgRoot, "dist", "mcp.js");
669
+ if (existsSync5(distMcp))
613
670
  return distMcp;
614
671
  } catch {}
615
672
  const currentFile = fileURLToPath(import.meta.url);
616
673
  const currentDir = dirname2(currentFile);
617
674
  const distMcpPath = resolve(currentDir, "..", "mcp.js");
618
- if (existsSync4(distMcpPath))
675
+ if (existsSync5(distMcpPath))
619
676
  return distMcpPath;
620
677
  const srcMcpPath = resolve(currentDir, "..", "mcp.ts");
621
- if (existsSync4(srcMcpPath))
678
+ if (existsSync5(srcMcpPath))
622
679
  return srcMcpPath;
623
680
  return distMcpPath;
624
681
  }
@@ -633,23 +690,23 @@ function resolveBunPath() {
633
690
  }
634
691
  function getRegistryPaths() {
635
692
  return [
636
- join5(homedir(), ".omx", "mcp-registry.json"),
637
- join5(homedir(), ".omc", "mcp-registry.json")
693
+ join6(homedir2(), ".omx", "mcp-registry.json"),
694
+ join6(homedir2(), ".omc", "mcp-registry.json")
638
695
  ];
639
696
  }
640
697
  function ensureMcpRegistered(sdkLog) {
641
698
  const registryPaths = getRegistryPaths();
642
699
  let targetPath = registryPaths[0];
643
700
  for (const p of registryPaths) {
644
- if (existsSync5(p)) {
701
+ if (existsSync6(p)) {
645
702
  targetPath = p;
646
703
  break;
647
704
  }
648
705
  }
649
706
  let registry = {};
650
- if (existsSync5(targetPath)) {
707
+ if (existsSync6(targetPath)) {
651
708
  try {
652
- const content = readFileSync4(targetPath, "utf-8");
709
+ const content = readFileSync5(targetPath, "utf-8");
653
710
  registry = JSON.parse(content);
654
711
  } catch (e) {
655
712
  if (sdkLog) {
@@ -678,7 +735,7 @@ function ensureMcpRegistered(sdkLog) {
678
735
  if (changed) {
679
736
  try {
680
737
  mkdirSync3(dirname3(targetPath), { recursive: true });
681
- writeFileSync3(targetPath, JSON.stringify(registry, null, 2), "utf-8");
738
+ writeFileSync4(targetPath, JSON.stringify(registry, null, 2), "utf-8");
682
739
  if (sdkLog) {
683
740
  sdkLog(`[INFO] Registered context-mcp in ${targetPath}`);
684
741
  }
@@ -777,8 +834,11 @@ async function logWarn(sdk, message, meta = {}) {
777
834
  }
778
835
  async function onSessionStart(event, sdk) {
779
836
  const projectDir = resolveProjectDir(event);
837
+ if (pruneStaleMockMcpServer()) {
838
+ await sdk.log.info("Removed stale mock-mcp from ~/.codex/config.toml because its target file is missing.");
839
+ }
780
840
  scaffoldIfNeeded(projectDir);
781
- injectIntoAgentsMd(join6(projectDir, "AGENTS.md"), STATIC_KNOWLEDGE_CONTEXT);
841
+ injectIntoAgentsMd(join7(projectDir, "AGENTS.md"), STATIC_KNOWLEDGE_CONTEXT);
782
842
  await sdk.log.info(`Injected context into AGENTS.md for ${projectDir}`);
783
843
  const wasRegistered = ensureMcpRegistered(sdk.log.info);
784
844
  if (wasRegistered) {
@@ -818,9 +878,9 @@ async function onTurnComplete(event, sdk) {
818
878
  }
819
879
  const followupScopeKey = resolveFollowupScopeKey(event);
820
880
  let pendingFollowupScopes = typeof sdk.state?.read === "function" ? await sdk.state.read(TURN_END_PENDING_SKIP_KEY, {}) ?? {} : {};
821
- const workCompleteFile = join6(projectDir, DEFAULTS.workCompleteFile);
822
- if (existsSync6(workCompleteFile)) {
823
- const content = readFileSync5(workCompleteFile, "utf-8");
881
+ const workCompleteFile = join7(projectDir, DEFAULTS.workCompleteFile);
882
+ if (existsSync7(workCompleteFile)) {
883
+ const content = readFileSync6(workCompleteFile, "utf-8");
824
884
  const { sessionId: fileSessionId, turnId: fileTurnId } = parseWorkComplete(content);
825
885
  const currentScopeId = event.session_id ?? event.thread_id ?? "";
826
886
  if (!fileSessionId || fileSessionId === currentScopeId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ksm0709/context",
3
- "version": "0.0.34",
3
+ "version": "0.0.36",
4
4
  "author": {
5
5
  "name": "TaehoKang",
6
6
  "email": "ksm07091@gmail.com"
@@ -42,23 +42,22 @@
42
42
  "@opencode-ai/plugin": ">=1.0.0"
43
43
  },
44
44
  "dependencies": {
45
- "@ksm0709/context": "^0.0.33",
46
45
  "@modelcontextprotocol/sdk": "^1.27.1",
47
46
  "jsonc-parser": "^3.0.0"
48
47
  },
49
48
  "devDependencies": {
50
- "@opencode-ai/plugin": "^1.2.10",
51
49
  "@eslint/js": "^9.39.1",
50
+ "@opencode-ai/plugin": "^1.2.10",
52
51
  "@types/node": "^20.11.5",
53
52
  "@typescript-eslint/eslint-plugin": "8.47.0",
54
53
  "@typescript-eslint/parser": "8.47.0",
54
+ "@vitest/coverage-v8": "^3.2.4",
55
55
  "bun-types": "latest",
56
56
  "eslint": "^9.39.1",
57
57
  "eslint-config-prettier": "10.1.8",
58
58
  "eslint-plugin-prettier": "^5.1.3",
59
59
  "prettier": "^3.2.4",
60
60
  "typescript-eslint": "^8.47.0",
61
- "vitest": "^3.2.4",
62
- "@vitest/coverage-v8": "^3.2.4"
61
+ "vitest": "^3.2.4"
63
62
  }
64
63
  }