@harmonyos-arkts/opencode-plugin 0.0.19 → 0.0.20-beta

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 +1 -1
  2. package/dist/index.js +180 -160
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  - **HarmonyOS设计专家**:为HarmonyOS应用和原子化服务创建详细的PRD设计文档
9
9
  - **HarmonyOS开发专家**:根据设计文档遵循HarmonyOS规范实现功能
10
10
  - **HarmonyOS构建**:使用harmonyos-hvigor技能构建项目并确保编译成功
11
- - **HarmonyOS文档查询**:通过 `harmony-doc` 工具搜索和查看华为开发者文档(含官方文档与社区内容),支持关键词搜索和分页查看全文
11
+ - **HarmonyOS文档查询**:通过 `harmony-doc-search` 搜索华为官方开发者文档(关键词越精简越准确),通过 `harmony-doc-view` 查看文档全文(支持分页)
12
12
 
13
13
  ## 快速开始
14
14
 
package/dist/index.js CHANGED
@@ -31913,6 +31913,8 @@ function createHmAgent() {
31913
31913
  doom_loop: "ask",
31914
31914
  "skillSearch": "allow",
31915
31915
  ascf_build: "deny",
31916
+ "harmony-doc-search": "allow",
31917
+ "harmony-doc-view": "allow",
31916
31918
  skill: {
31917
31919
  "*": "deny",
31918
31920
  "harmonyos-atomic-dev": "allow",
@@ -31944,7 +31946,9 @@ function createDesignAgent() {
31944
31946
  "*": "deny",
31945
31947
  "harmonyos-prd-design": "allow"
31946
31948
  },
31947
- "skillSearch": "deny"
31949
+ "skillSearch": "deny",
31950
+ "harmony-doc-search": "deny",
31951
+ "harmony-doc-view": "deny"
31948
31952
  },
31949
31953
  metadata: void 0
31950
31954
  };
@@ -31967,6 +31971,8 @@ function createHmDevelopmentAgent() {
31967
31971
  temperature: 0.3,
31968
31972
  permission: {
31969
31973
  "skillSearch": "allow",
31974
+ "harmony-doc-search": "allow",
31975
+ "harmony-doc-view": "allow",
31970
31976
  skill: {
31971
31977
  "*": "deny",
31972
31978
  "harmonyos-atomic-dev": "allow",
@@ -44871,8 +44877,10 @@ function createHmTemplateTool(managers) {
44871
44877
  return tool({
44872
44878
  description: "Create an empty HarmonyOS project template. Supports three modes: 'application' (full app), 'module' (sub-module), 'atomic' (atomic service). Defaults to the current working directory unless the user specifies a different directory.",
44873
44879
  args: {
44874
- filePath: tool.schema.string().describe("Absolute path of the target directory where the template will be created. Defaults to the current working directory if not specified."),
44875
- mode: tool.schema.string().describe("Template type: 'application' (\u5E94\u7528), 'module' (\u6A21\u5757), or 'atomic' (\u5143\u670D\u52A1)")
44880
+ filePath: tool.schema.string().describe(
44881
+ "Absolute path of the target directory where the template will be created. Defaults to the current working directory if not specified."
44882
+ ),
44883
+ mode: tool.schema.string().describe("Template type: 'application' (\u5E94\u7528), 'module' (\u6A21\u5757), or 'atomic' (\u5143\u670D\u52A1).")
44876
44884
  },
44877
44885
  execute: async (args, context) => {
44878
44886
  try {
@@ -45376,6 +45384,117 @@ function convert_search_result(searchResults, prefix) {
45376
45384
  });
45377
45385
  }
45378
45386
 
45387
+ // src/tools/skill-search/skill-search-tool.ts
45388
+ function skillSearchTool(_managers) {
45389
+ return tool({
45390
+ description: "Search for relevant documents within the harmonyos-atomic-dev skill directory by keywords. Returns the top K most relevant document snippets from the skill directory, ranked by keyword match frequency. Results include experience_file_path (best practices), ets_file_path (code examples), and sdk_file_path (SDK API info). QUERY TIPS: Decompose your intent into short space-separated keywords for best results. Include: 1) Kit or component name (e.g. ScanKit, AdsKit, ShareKit), 2) specific API or method names (e.g. scanBarcode, loadAd, ShareController), 3) feature keywords (e.g. \u626B\u7801, \u5E7F\u544A\u52A0\u8F7D, \u5206\u4EAB). Example: 'ScanKit \u626B\u7801 scanBarcode startScanForResult' instead of '\u5E2E\u6211\u5B9E\u73B0\u4E00\u4E2A\u626B\u7801\u529F\u80FD'.",
45391
+ args: {
45392
+ skill_path: tool.schema.string().describe(
45393
+ "Absolute path to the harmonyos-atomic-dev skill directory. IMPORTANT: this path should not contain any prefix or suffix like 'file://' or 'SKILL.md'."
45394
+ ),
45395
+ query: tool.schema.string().describe(
45396
+ "A decomposed requirement or intent describing what you want to find, broken down into searchable keywords separated by spaces."
45397
+ ),
45398
+ topK: tool.schema.number().describe("Maximum number of top-ranked documents to return. Actual results may be fewer depending on query relevance.").min(1).max(3).default(3)
45399
+ },
45400
+ execute: async (args, context) => {
45401
+ try {
45402
+ const results = await searchSkill(args.skill_path, args.query, args.topK);
45403
+ if (results.length === 0) {
45404
+ return "No relative items found in the skill directory.";
45405
+ }
45406
+ const info = results.map((r) => {
45407
+ const lines = [];
45408
+ lines.push(`score ${r.score.toFixed(4)}`);
45409
+ if (r.experience_file_path) lines.push(`- experience path: ${r.experience_file_path}`);
45410
+ if (r.ets_file_path) lines.push(`- sample code path: ${r.ets_file_path}`);
45411
+ if (r.sdk_file_path) lines.push(`- sdk info path: ${r.sdk_file_path}`);
45412
+ return lines.join("\n");
45413
+ }).join("\n\n");
45414
+ return `## Results
45415
+
45416
+ ${info}`;
45417
+ } catch (e) {
45418
+ return `Error: ${e instanceof Error ? e.message : String(e)}`;
45419
+ }
45420
+ }
45421
+ });
45422
+ }
45423
+
45424
+ // src/tools/html-preview/html-preview-tool.ts
45425
+ import { access } from "fs/promises";
45426
+ import path7 from "path";
45427
+ import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
45428
+ var PROTOTYPE_HTML_FILENAME = "prototype.html";
45429
+ function toFileUrl(filePath) {
45430
+ return pathToFileURL(path7.resolve(filePath)).href;
45431
+ }
45432
+ async function fileExists(filePath) {
45433
+ try {
45434
+ await access(filePath);
45435
+ return true;
45436
+ } catch {
45437
+ return false;
45438
+ }
45439
+ }
45440
+ function resolvePrototypePath(directory, input) {
45441
+ const trimmed = input.trim();
45442
+ if (!trimmed) {
45443
+ throw new Error("filePath is required");
45444
+ }
45445
+ if (trimmed.startsWith("file://")) {
45446
+ return fileURLToPath2(trimmed);
45447
+ }
45448
+ return path7.isAbsolute(trimmed) ? trimmed : path7.resolve(directory, trimmed);
45449
+ }
45450
+ async function resolvePrototypePreview(directory, filePathInput) {
45451
+ const filePath = resolvePrototypePath(directory, filePathInput);
45452
+ if (!await fileExists(filePath)) {
45453
+ throw new Error(
45454
+ `Prototype HTML not found: ${filePath}. Pass the exact local path where ${PROTOTYPE_HTML_FILENAME} was written.`
45455
+ );
45456
+ }
45457
+ return { url: toFileUrl(filePath), filePath };
45458
+ }
45459
+ function htmlPreviewTool(_managers) {
45460
+ return tool({
45461
+ description: "Send the UX prototype preview to the frontend. Call this immediately after writing prototype.html. You MUST pass filePath with the exact local path of the written file so the frontend can locate and preview it.",
45462
+ args: {
45463
+ filePath: tool.schema.string().describe(
45464
+ "Local path to prototype.html. Use the same path from the write step, e.g. prototype.html, /Users/yanqing/coding/your-project/prototype.html, or file:///.../prototype.html."
45465
+ ),
45466
+ title: tool.schema.optional(tool.schema.string().describe("Title shown in the preview panel."))
45467
+ },
45468
+ execute: async (args, context) => {
45469
+ try {
45470
+ log("[html_preview]", { filePath: args.filePath, title: args.title });
45471
+ const { url: url3, filePath } = await resolvePrototypePreview(context.directory, args.filePath);
45472
+ const title = args.title?.trim() || "Prototype Preview";
45473
+ const metadata = {
45474
+ url: url3,
45475
+ filePath,
45476
+ fileName: path7.basename(filePath),
45477
+ title
45478
+ };
45479
+ context.metadata({
45480
+ title,
45481
+ metadata: {
45482
+ htmlPreview: metadata
45483
+ }
45484
+ });
45485
+ return [
45486
+ "Prototype preview sent to frontend.",
45487
+ `Title: ${title}`,
45488
+ `Local path: ${filePath}`,
45489
+ `Preview URL: ${url3}`
45490
+ ].join("\n");
45491
+ } catch (e) {
45492
+ return `Error: ${e instanceof Error ? e.message : String(e)}`;
45493
+ }
45494
+ }
45495
+ });
45496
+ }
45497
+
45379
45498
  // src/tools/harmony-doc/api-client.ts
45380
45499
  var BASE_URL = "https://svc-drcn.developer.huawei.com/community/servlet";
45381
45500
  var HEADERS = {
@@ -45578,159 +45697,6 @@ function formatUpdateTime(utcTimeStr) {
45578
45697
  return `${y}-${m}-${d} ${h}:${min}`;
45579
45698
  }
45580
45699
 
45581
- // src/tools/skill-search/skill-search-tool.ts
45582
- function skillSearchTool(managers) {
45583
- return tool({
45584
- description: "Search HarmonyOS development knowledge across BOTH local skill documents AND official Huawei documentation. Returns two sections: (1) Local Skill Results \u2014 file paths to curated code examples, best practices, and SDK API info from the harmonyos-atomic-dev skill directory; (2) Official Documentation \u2014 search results from developer.huawei.com with titles, URLs, breadcrumbs, and excerpts. Use this tool for any HarmonyOS API, component, Kit, or development pattern lookup. After reviewing results, read local skill files for code patterns and use harmony-doc-view to read full official documents by URL. IMPORTANT: This tool already searches official documentation \u2014 do NOT also call harmony-doc-view for the same purpose. Only use harmony-doc-view to read a specific document page from the URLs listed below. Each scenario only supports ONE tool call. Do NOT call this tool multiple times.",
45585
- args: {
45586
- skill_path: tool.schema.string().describe("The COMPLETE absolute filesystem path to the harmonyos-atomic-dev skill directory (e.g. '/home/user/.opencode/skills/harmonyos-atomic-dev'). Do NOT pass just the skill name 'harmonyos-atomic-dev' \u2014 you MUST resolve and provide the full absolute path. Do NOT include any prefix like 'file://' or suffix like 'SKILL.md' or 'index.json'."),
45587
- query: tool.schema.string().describe("A decomposed requirement or intent describing what you want to find, broken down into searchable keywords separated by spaces. QUERY TIPS: For best results, decompose your intent into short space-separated keywords instead of long sentences. Include: 1) Kit or component name (e.g. ScanKit, AdsKit, ShareKit), 2) specific API or method names (e.g. scanBarcode, loadAd, ShareController), 3) feature keywords (e.g. \u626B\u7801, \u5E7F\u544A\u52A0\u8F7D, \u5206\u4EAB). Example: 'ScanKit \u626B\u7801 scanBarcode startScanForResult' instead of '\u5E2E\u6211\u5B9E\u73B0\u4E00\u4E2A\u626B\u7801\u529F\u80FD'."),
45588
- topK: tool.schema.number().describe("Maximum number of top-ranked documents to return. Actual results may be fewer depending on query relevance.").min(1).max(3).default(3)
45589
- },
45590
- execute: async (args, context) => {
45591
- const [localResults, docResults] = await Promise.all([
45592
- searchSkill(args.skill_path, args.query, args.topK).catch(
45593
- (e) => new Error(`Local search failed: ${e instanceof Error ? e.message : String(e)}`)
45594
- ),
45595
- searchDocs(args.query, 5).catch(
45596
- (e) => new Error(`Doc search failed: ${e instanceof Error ? e.message : String(e)}`)
45597
- )
45598
- ]);
45599
- const parts = [];
45600
- if (localResults instanceof Error) {
45601
- parts.push(`## Local Skill Results
45602
-
45603
- ${localResults.message}`);
45604
- } else if (localResults.length === 0) {
45605
- parts.push("## Local Skill Results\n\nNo matching skill documents found.");
45606
- } else {
45607
- const localInfo = localResults.map((r) => {
45608
- const lines = [];
45609
- lines.push(`score ${r.score.toFixed(4)}`);
45610
- if (r.experience_file_path) lines.push(`- experience path: ${r.experience_file_path}`);
45611
- if (r.ets_file_path) lines.push(`- sample code path: ${r.ets_file_path}`);
45612
- if (r.sdk_file_path) lines.push(`- sdk info path: ${r.sdk_file_path}`);
45613
- return lines.join("\n");
45614
- }).join("\n\n");
45615
- parts.push(`## Local Skill Results
45616
-
45617
- ${localInfo}`);
45618
- }
45619
- if (docResults instanceof Error) {
45620
- parts.push(`## Official Documentation
45621
-
45622
- ${docResults.message}`);
45623
- } else if (docResults.length === 0) {
45624
- parts.push("## Official Documentation\n\nNo matching official documents found.");
45625
- } else {
45626
- const docInfo = docResults.map((r, i) => {
45627
- const lines = [];
45628
- let title = r.title;
45629
- if (r.subsection && r.subsection !== r.title) {
45630
- title += ` #${r.subsection}`;
45631
- }
45632
- lines.push(`${i + 1}. **${title}**`);
45633
- let displayUrl = r.url;
45634
- if (r.anchorId) {
45635
- displayUrl += `#${r.anchorId}`;
45636
- }
45637
- lines.push(` URL: ${displayUrl}`);
45638
- if (r.excerpt) {
45639
- const suffix = r.excerptTruncated ? "..." : "";
45640
- lines.push(` ${r.excerpt}${suffix}`);
45641
- }
45642
- if (r.breadcrumb.length > 0) {
45643
- lines.push(` \u6765\u81EA: ${r.breadcrumb.join(" > ")}`);
45644
- }
45645
- return lines.join("\n");
45646
- }).join("\n\n");
45647
- parts.push(
45648
- `## Official Documentation
45649
-
45650
- ${docInfo}
45651
-
45652
- Use harmony-doc-view with a URL above to read any official document in full.`
45653
- );
45654
- }
45655
- return parts.join("\n\n");
45656
- }
45657
- });
45658
- }
45659
-
45660
- // src/tools/html-preview/html-preview-tool.ts
45661
- import { access } from "fs/promises";
45662
- import path7 from "path";
45663
- import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
45664
- var PROTOTYPE_HTML_FILENAME = "prototype.html";
45665
- function toFileUrl(filePath) {
45666
- return pathToFileURL(path7.resolve(filePath)).href;
45667
- }
45668
- async function fileExists(filePath) {
45669
- try {
45670
- await access(filePath);
45671
- return true;
45672
- } catch {
45673
- return false;
45674
- }
45675
- }
45676
- function resolvePrototypePath(directory, input) {
45677
- const trimmed = input.trim();
45678
- if (!trimmed) {
45679
- throw new Error("filePath is required");
45680
- }
45681
- if (trimmed.startsWith("file://")) {
45682
- return fileURLToPath2(trimmed);
45683
- }
45684
- return path7.isAbsolute(trimmed) ? trimmed : path7.resolve(directory, trimmed);
45685
- }
45686
- async function resolvePrototypePreview(directory, filePathInput) {
45687
- const filePath = resolvePrototypePath(directory, filePathInput);
45688
- if (!await fileExists(filePath)) {
45689
- throw new Error(
45690
- `Prototype HTML not found: ${filePath}. Pass the exact local path where ${PROTOTYPE_HTML_FILENAME} was written.`
45691
- );
45692
- }
45693
- return { url: toFileUrl(filePath), filePath };
45694
- }
45695
- function htmlPreviewTool(_managers) {
45696
- return tool({
45697
- description: "Send the UX prototype preview to the frontend. Call this immediately after writing prototype.html. You MUST pass filePath with the exact local path of the written file so the frontend can locate and preview it.",
45698
- args: {
45699
- filePath: tool.schema.string().describe("Local path to prototype.html. Use the same path from the write step, e.g. prototype.html, /Users/yanqing/coding/your-project/prototype.html, or file:///.../prototype.html."),
45700
- title: tool.schema.optional(
45701
- tool.schema.string().describe("Optional title shown in the preview panel")
45702
- )
45703
- },
45704
- execute: async (args, context) => {
45705
- try {
45706
- log("[html_preview]", { filePath: args.filePath, title: args.title });
45707
- const { url: url3, filePath } = await resolvePrototypePreview(context.directory, args.filePath);
45708
- const title = args.title?.trim() || "Prototype Preview";
45709
- const metadata = {
45710
- url: url3,
45711
- filePath,
45712
- fileName: path7.basename(filePath),
45713
- title
45714
- };
45715
- context.metadata({
45716
- title,
45717
- metadata: {
45718
- htmlPreview: metadata
45719
- }
45720
- });
45721
- return [
45722
- "Prototype preview sent to frontend.",
45723
- `Title: ${title}`,
45724
- `Local path: ${filePath}`,
45725
- `Preview URL: ${url3}`
45726
- ].join("\n");
45727
- } catch (e) {
45728
- return `Error: ${e instanceof Error ? e.message : String(e)}`;
45729
- }
45730
- }
45731
- });
45732
- }
45733
-
45734
45700
  // node_modules/turndown/lib/turndown.es.js
45735
45701
  function extend3(destination) {
45736
45702
  for (var i = 1; i < arguments.length; i++) {
@@ -46781,7 +46747,7 @@ function cleanupMarkdown(md) {
46781
46747
  return out.trim();
46782
46748
  }
46783
46749
 
46784
- // src/tools/harmony-doc/harmony-doc-tool.ts
46750
+ // src/tools/harmony-doc/harmony-doc-view-tool.ts
46785
46751
  var docCache = /* @__PURE__ */ new Map();
46786
46752
  var CACHE_TTL = 10 * 60 * 1e3;
46787
46753
  function getCached(objectId) {
@@ -46798,9 +46764,9 @@ function setCache(objectId, doc, markdown, breadcrumb) {
46798
46764
  }
46799
46765
  function harmonyDocViewTool(_managers) {
46800
46766
  return tool({
46801
- description: "View a HarmonyOS official documentation page by URL with full Markdown content and pagination. Use this tool to read official API references, guides, and best practices from developer.huawei.com. Typically used with URLs obtained from skillSearch's Official Documentation results. Supports pagination for long documents \u2014 use the 'page' parameter to continue reading.",
46767
+ description: "View a HarmonyOS official documentation page by URL with full Markdown content and pagination. Use this tool to read official API references, guides, and best practices from developer.huawei.com. Typically used with URLs obtained from harmony-doc-search results. Supports pagination for long documents \u2014 use the 'page' parameter to continue reading.",
46802
46768
  args: {
46803
- url: tool.schema.string().describe("Full URL of the document to view. Must be a developer.huawei.com URL, typically from skillSearch results."),
46769
+ url: tool.schema.string().describe("Full URL of the document to view. Must be a developer.huawei.com URL."),
46804
46770
  page: tool.schema.number().describe("Page number for long documents. Starts at 1.").min(1).default(1)
46805
46771
  },
46806
46772
  execute: async (args, _context) => {
@@ -46899,13 +46865,67 @@ Source: ${url3}
46899
46865
  }
46900
46866
  }
46901
46867
 
46868
+ // src/tools/harmony-doc/harmony-doc-search-tool.ts
46869
+ var DEFAULT_SEARCH_COUNT = 5;
46870
+ function harmonyDocSearchTool(_managers) {
46871
+ return tool({
46872
+ description: "Search official HarmonyOS documentation from developer.huawei.com. This tool requires network access and takes several seconds to respond. Covers any HarmonyOS-related knowledge: API references, Kit capabilities, ArkUI components, ArkTS syntax, development guides, best practices, error codes, migration guides, sample code references. IMPORTANT: Use concise keywords for best results \u2014 fewer words = more precise matches. Example: 'ScanKit scanBarcode' instead of 'how to implement barcode scanning with ScanKit'. After reviewing results, use harmony-doc-view to read any document in full by URL.",
46873
+ args: {
46874
+ keyword: tool.schema.string().describe(
46875
+ "Concise search keyword for official HarmonyOS documentation. Keep it short and specific \u2014 prefer 1-3 keywords over long sentences. Supports any HarmonyOS-related query: Kit/Component names (ScanKit, AdsKit, List, Column), API names (scanBarcode, ShareController, @State, @Prop), ArkTS syntax (@Entry, @Builder, @Extend), feature keywords (\u626B\u7801, \u5E7F\u544A, \u5206\u4EAB, \u52A8\u753B, \u7F51\u7EDC\u8BF7\u6C42), error codes, migration topics, or any development concept."
46876
+ )
46877
+ },
46878
+ execute: async (args, _context) => {
46879
+ const docResults = await searchDocs(
46880
+ args.keyword,
46881
+ DEFAULT_SEARCH_COUNT
46882
+ ).catch(
46883
+ (e) => new Error(
46884
+ `Doc search failed: ${e instanceof Error ? e.message : String(e)}`
46885
+ )
46886
+ );
46887
+ if (docResults instanceof Error) {
46888
+ return docResults.message;
46889
+ }
46890
+ if (docResults.length === 0) {
46891
+ return `No matching official documents found for "${args.keyword}".`;
46892
+ }
46893
+ const docInfo = docResults.map((r, i) => {
46894
+ const lines = [];
46895
+ let title = r.title;
46896
+ if (r.subsection && r.subsection !== r.title) {
46897
+ title += ` #${r.subsection}`;
46898
+ }
46899
+ lines.push(`${i + 1}. **${title}**`);
46900
+ let displayUrl = r.url;
46901
+ if (r.anchorId) {
46902
+ displayUrl += `#${r.anchorId}`;
46903
+ }
46904
+ lines.push(` URL: ${displayUrl}`);
46905
+ if (r.excerpt) {
46906
+ const suffix = r.excerptTruncated ? "..." : "";
46907
+ lines.push(` ${r.excerpt}${suffix}`);
46908
+ }
46909
+ if (r.breadcrumb.length > 0) {
46910
+ lines.push(` \u6765\u81EA: ${r.breadcrumb.join(" > ")}`);
46911
+ }
46912
+ return lines.join("\n");
46913
+ }).join("\n\n");
46914
+ return `${docInfo}
46915
+
46916
+ Use harmony-doc-view with a URL above to read any official document in full.`;
46917
+ }
46918
+ });
46919
+ }
46920
+
46902
46921
  // src/tools/builtin.ts
46903
46922
  function createBuiltinTools(managers) {
46904
46923
  return {
46905
46924
  createHmTemplate: createHmTemplateTool(managers),
46906
46925
  skillSearch: skillSearchTool(managers),
46907
46926
  html_preview: htmlPreviewTool(managers),
46908
- "harmony-doc-view": harmonyDocViewTool(managers)
46927
+ "harmony-doc-view": harmonyDocViewTool(managers),
46928
+ "harmony-doc-search": harmonyDocSearchTool(managers)
46909
46929
  };
46910
46930
  }
46911
46931
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@harmonyos-arkts/opencode-plugin",
4
- "version": "0.0.19",
4
+ "version": "0.0.20-beta",
5
5
  "description": "HarmonyOS Full-Lifecycle Development Assistant. Specialized in the complete development lifecycle of HarmonyOS applications, including project creation, UI development, state management, network requests, data storage, permission requests, performance optimization, testing, and release.",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "ArkTs"
31
31
  ],
32
32
  "dependencies": {
33
- "@opencode-ai/sdk": "latest",
33
+ "@opencode-ai/sdk": "1.15.1",
34
34
  "diff": "7.0.0",
35
35
  "env-paths": "4.0.0",
36
36
  "jsonc-parser": "3.3.1",