@harmonyos-arkts/opencode-plugin 0.0.18 → 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 +233 -182
  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
@@ -31911,11 +31911,10 @@ function createHmAgent() {
31911
31911
  permission: {
31912
31912
  "*": "allow",
31913
31913
  doom_loop: "ask",
31914
- external_directory: {
31915
- "*": "ask"
31916
- },
31917
31914
  "skillSearch": "allow",
31918
31915
  ascf_build: "deny",
31916
+ "harmony-doc-search": "allow",
31917
+ "harmony-doc-view": "allow",
31919
31918
  skill: {
31920
31919
  "*": "deny",
31921
31920
  "harmonyos-atomic-dev": "allow",
@@ -31947,7 +31946,9 @@ function createDesignAgent() {
31947
31946
  "*": "deny",
31948
31947
  "harmonyos-prd-design": "allow"
31949
31948
  },
31950
- "skillSearch": "deny"
31949
+ "skillSearch": "deny",
31950
+ "harmony-doc-search": "deny",
31951
+ "harmony-doc-view": "deny"
31951
31952
  },
31952
31953
  metadata: void 0
31953
31954
  };
@@ -31970,6 +31971,8 @@ function createHmDevelopmentAgent() {
31970
31971
  temperature: 0.3,
31971
31972
  permission: {
31972
31973
  "skillSearch": "allow",
31974
+ "harmony-doc-search": "allow",
31975
+ "harmony-doc-view": "allow",
31973
31976
  skill: {
31974
31977
  "*": "deny",
31975
31978
  "harmonyos-atomic-dev": "allow",
@@ -44874,8 +44877,10 @@ function createHmTemplateTool(managers) {
44874
44877
  return tool({
44875
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.",
44876
44879
  args: {
44877
- filePath: tool.schema.string("Absolute path of the target directory where the template will be created. Defaults to the current working directory if not specified."),
44878
- mode: tool.schema.string("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).")
44879
44884
  },
44880
44885
  execute: async (args, context) => {
44881
44886
  try {
@@ -45379,6 +45384,117 @@ function convert_search_result(searchResults, prefix) {
45379
45384
  });
45380
45385
  }
45381
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
+
45382
45498
  // src/tools/harmony-doc/api-client.ts
45383
45499
  var BASE_URL = "https://svc-drcn.developer.huawei.com/community/servlet";
45384
45500
  var HEADERS = {
@@ -45581,161 +45697,6 @@ function formatUpdateTime(utcTimeStr) {
45581
45697
  return `${y}-${m}-${d} ${h}:${min}`;
45582
45698
  }
45583
45699
 
45584
- // src/tools/skill-search/skill-search-tool.ts
45585
- function skillSearchTool(managers) {
45586
- return tool({
45587
- 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.",
45588
- args: {
45589
- skill_path: tool.schema.string("Absolute path to the harmonyos-atomic-dev skill directory. IMPORTANT: this path should not contain any prefix or suffix like 'file://' or 'SKILL.md'."),
45590
- query: tool.schema.string("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'. "),
45591
- topK: tool.schema.number("Maximum number of top-ranked documents to return. Actual results may be fewer depending on query relevance.").min(1).max(3).default(3)
45592
- },
45593
- execute: async (args, context) => {
45594
- const [localResults, docResults] = await Promise.all([
45595
- searchSkill(args.skill_path, args.query, args.topK).catch(
45596
- (e) => new Error(`Local search failed: ${e instanceof Error ? e.message : String(e)}`)
45597
- ),
45598
- searchDocs(args.query, 5).catch(
45599
- (e) => new Error(`Doc search failed: ${e instanceof Error ? e.message : String(e)}`)
45600
- )
45601
- ]);
45602
- const parts = [];
45603
- if (localResults instanceof Error) {
45604
- parts.push(`## Local Skill Results
45605
-
45606
- ${localResults.message}`);
45607
- } else if (localResults.length === 0) {
45608
- parts.push("## Local Skill Results\n\nNo matching skill documents found.");
45609
- } else {
45610
- const localInfo = localResults.map((r) => {
45611
- const lines = [];
45612
- lines.push(`score ${r.score.toFixed(4)}`);
45613
- if (r.experience_file_path) lines.push(`- experience path: ${r.experience_file_path}`);
45614
- if (r.ets_file_path) lines.push(`- sample code path: ${r.ets_file_path}`);
45615
- if (r.sdk_file_path) lines.push(`- sdk info path: ${r.sdk_file_path}`);
45616
- return lines.join("\n");
45617
- }).join("\n\n");
45618
- parts.push(`## Local Skill Results
45619
-
45620
- ${localInfo}`);
45621
- }
45622
- if (docResults instanceof Error) {
45623
- parts.push(`## Official Documentation
45624
-
45625
- ${docResults.message}`);
45626
- } else if (docResults.length === 0) {
45627
- parts.push("## Official Documentation\n\nNo matching official documents found.");
45628
- } else {
45629
- const docInfo = docResults.map((r, i) => {
45630
- const lines = [];
45631
- let title = r.title;
45632
- if (r.subsection && r.subsection !== r.title) {
45633
- title += ` #${r.subsection}`;
45634
- }
45635
- lines.push(`${i + 1}. **${title}**`);
45636
- let displayUrl = r.url;
45637
- if (r.anchorId) {
45638
- displayUrl += `#${r.anchorId}`;
45639
- }
45640
- lines.push(` URL: ${displayUrl}`);
45641
- if (r.excerpt) {
45642
- const suffix = r.excerptTruncated ? "..." : "";
45643
- lines.push(` ${r.excerpt}${suffix}`);
45644
- }
45645
- if (r.breadcrumb.length > 0) {
45646
- lines.push(` \u6765\u81EA: ${r.breadcrumb.join(" > ")}`);
45647
- }
45648
- return lines.join("\n");
45649
- }).join("\n\n");
45650
- parts.push(
45651
- `## Official Documentation
45652
-
45653
- ${docInfo}
45654
-
45655
- Use harmony-doc-view with a URL above to read any official document in full.`
45656
- );
45657
- }
45658
- return parts.join("\n\n");
45659
- }
45660
- });
45661
- }
45662
-
45663
- // src/tools/html-preview/html-preview-tool.ts
45664
- import { access } from "fs/promises";
45665
- import path7 from "path";
45666
- import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
45667
- var PROTOTYPE_HTML_FILENAME = "prototype.html";
45668
- function toFileUrl(filePath) {
45669
- return pathToFileURL(path7.resolve(filePath)).href;
45670
- }
45671
- async function fileExists(filePath) {
45672
- try {
45673
- await access(filePath);
45674
- return true;
45675
- } catch {
45676
- return false;
45677
- }
45678
- }
45679
- function resolvePrototypePath(directory, input) {
45680
- const trimmed = input.trim();
45681
- if (!trimmed) {
45682
- throw new Error("filePath is required");
45683
- }
45684
- if (trimmed.startsWith("file://")) {
45685
- return fileURLToPath2(trimmed);
45686
- }
45687
- return path7.isAbsolute(trimmed) ? trimmed : path7.resolve(directory, trimmed);
45688
- }
45689
- async function resolvePrototypePreview(directory, filePathInput) {
45690
- const filePath = resolvePrototypePath(directory, filePathInput);
45691
- if (!await fileExists(filePath)) {
45692
- throw new Error(
45693
- `Prototype HTML not found: ${filePath}. Pass the exact local path where ${PROTOTYPE_HTML_FILENAME} was written.`
45694
- );
45695
- }
45696
- return { url: toFileUrl(filePath), filePath };
45697
- }
45698
- function htmlPreviewTool(_managers) {
45699
- return tool({
45700
- 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.",
45701
- args: {
45702
- filePath: tool.schema.string(
45703
- "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."
45704
- ),
45705
- title: tool.schema.optional(
45706
- tool.schema.string("Optional title shown in the preview panel")
45707
- )
45708
- },
45709
- execute: async (args, context) => {
45710
- try {
45711
- log("[html_preview]", { filePath: args.filePath, title: args.title });
45712
- const { url: url3, filePath } = await resolvePrototypePreview(context.directory, args.filePath);
45713
- const title = args.title?.trim() || "Prototype Preview";
45714
- const metadata = {
45715
- url: url3,
45716
- filePath,
45717
- fileName: path7.basename(filePath),
45718
- title
45719
- };
45720
- context.metadata({
45721
- title,
45722
- metadata: {
45723
- htmlPreview: metadata
45724
- }
45725
- });
45726
- return [
45727
- "Prototype preview sent to frontend.",
45728
- `Title: ${title}`,
45729
- `Local path: ${filePath}`,
45730
- `Preview URL: ${url3}`
45731
- ].join("\n");
45732
- } catch (e) {
45733
- return `Error: ${e instanceof Error ? e.message : String(e)}`;
45734
- }
45735
- }
45736
- });
45737
- }
45738
-
45739
45700
  // node_modules/turndown/lib/turndown.es.js
45740
45701
  function extend3(destination) {
45741
45702
  for (var i = 1; i < arguments.length; i++) {
@@ -46786,7 +46747,7 @@ function cleanupMarkdown(md) {
46786
46747
  return out.trim();
46787
46748
  }
46788
46749
 
46789
- // src/tools/harmony-doc/harmony-doc-tool.ts
46750
+ // src/tools/harmony-doc/harmony-doc-view-tool.ts
46790
46751
  var docCache = /* @__PURE__ */ new Map();
46791
46752
  var CACHE_TTL = 10 * 60 * 1e3;
46792
46753
  function getCached(objectId) {
@@ -46803,10 +46764,10 @@ function setCache(objectId, doc, markdown, breadcrumb) {
46803
46764
  }
46804
46765
  function harmonyDocViewTool(_managers) {
46805
46766
  return tool({
46806
- 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.",
46807
46768
  args: {
46808
- url: tool.schema.string("Full URL of the document to view. Must be a developer.huawei.com URL, typically from skillSearch results."),
46809
- page: tool.schema.number("Page number for long documents. Starts at 1.").min(1).default(1)
46769
+ url: tool.schema.string().describe("Full URL of the document to view. Must be a developer.huawei.com URL."),
46770
+ page: tool.schema.number().describe("Page number for long documents. Starts at 1.").min(1).default(1)
46810
46771
  },
46811
46772
  execute: async (args, _context) => {
46812
46773
  return executeView(args.url, args.page);
@@ -46904,13 +46865,67 @@ Source: ${url3}
46904
46865
  }
46905
46866
  }
46906
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
+
46907
46921
  // src/tools/builtin.ts
46908
46922
  function createBuiltinTools(managers) {
46909
46923
  return {
46910
46924
  createHmTemplate: createHmTemplateTool(managers),
46911
46925
  skillSearch: skillSearchTool(managers),
46912
46926
  html_preview: htmlPreviewTool(managers),
46913
- "harmony-doc-view": harmonyDocViewTool(managers)
46927
+ "harmony-doc-view": harmonyDocViewTool(managers),
46928
+ "harmony-doc-search": harmonyDocSearchTool(managers)
46914
46929
  };
46915
46930
  }
46916
46931
 
@@ -47531,6 +47546,7 @@ function createToolHooks(sessionManager, projectDir) {
47531
47546
  injectAiCodeChange(input, output);
47532
47547
  await injectHtmlPreview(input, output, projectDir);
47533
47548
  await updateEtsCount(sessionManager, projectDir, input);
47549
+ markCompactAfterHtmlPreview(input, sessionManager);
47534
47550
  }
47535
47551
  };
47536
47552
  }
@@ -47620,28 +47636,63 @@ function injectAiCodeChange(input, output) {
47620
47636
  log("aiCodeChange after hook error", { tool: input.tool, error: String(e) });
47621
47637
  }
47622
47638
  }
47639
+ function markCompactAfterHtmlPreview(input, sessionManager) {
47640
+ if (input.tool !== "html_preview") return;
47641
+ const sessionID = input.sessionID;
47642
+ if (!sessionID) return;
47643
+ let session = sessionManager.get(sessionID);
47644
+ if (!session) {
47645
+ session = sessionManager.create(sessionID);
47646
+ }
47647
+ session.data.set("compactAfterPreviewActive", true);
47648
+ log("Activated persistent context compaction after html_preview", { sessionID });
47649
+ }
47623
47650
 
47624
47651
  // src/commands/env-check.ts
47625
47652
  import { execSync } from "child_process";
47626
- function runEnvCheck() {
47627
- const checks = [
47628
- { name: "Java", cmd: "java -version" },
47629
- { name: "Node.js", cmd: "node -v" },
47630
- { name: "ohpm", cmd: "ohpm -v" },
47631
- { name: "hvigorw", cmd: "hvigorw -v" }
47632
- ];
47633
- const lines = [];
47634
- lines.push("HarmonyOS Development Environment Check\uFF1A");
47635
- for (const check3 of checks) {
47653
+ import { readFileSync as readFileSync2, unlinkSync as unlinkSync2 } from "fs";
47654
+ import { join as join7 } from "path";
47655
+ import { tmpdir as tmpdir2 } from "os";
47656
+ function runCmdRaw(cmd) {
47657
+ try {
47658
+ const output = execSync(`${cmd} 2>&1`, { timeout: 1e4, encoding: "utf-8" });
47659
+ return output.split(/\r?\n/)[0]?.trim() || void 0;
47660
+ } catch (err) {
47661
+ const output = (err.stderr?.toString() || err.stdout?.toString() || "").trim();
47662
+ return output.split(/\r?\n/)[0]?.trim() || void 0;
47663
+ }
47664
+ }
47665
+ function runCmdToFile(cmd) {
47666
+ const tmpFile = join7(tmpdir2(), `hm-env-${Date.now()}.txt`);
47667
+ try {
47668
+ execSync(`${cmd} > "${tmpFile}" 2>&1`, { timeout: 1e4 });
47669
+ const content = readFileSync2(tmpFile, "utf-8");
47670
+ return content.split(/\r?\n/)[0]?.trim() || void 0;
47671
+ } catch {
47672
+ return void 0;
47673
+ } finally {
47636
47674
  try {
47637
- const output = execSync(`${check3.cmd} 2>&1`, { timeout: 1e4, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
47638
- const firstLine = output.split(/\r?\n/)[0]?.trim() || "";
47639
- const suffix = check3.name === "Java" ? " (requires >= 17)" : "";
47640
- lines.push(`[${check3.name}] ${firstLine}${suffix}`);
47641
- } catch (err) {
47642
- lines.push(`[${check3.name}] NOT FOUND`);
47675
+ unlinkSync2(tmpFile);
47676
+ } catch {
47643
47677
  }
47644
47678
  }
47679
+ }
47680
+ function runCmd(cmd, { retry = false } = {}) {
47681
+ const result = runCmdRaw(cmd) || runCmdToFile(cmd);
47682
+ if (result || !retry) return result;
47683
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 2e3);
47684
+ return runCmdRaw(cmd) || runCmdToFile(cmd);
47685
+ }
47686
+ function runEnvCheck() {
47687
+ const lines = [];
47688
+ lines.push("HarmonyOS Development Environment Check\uFF1A");
47689
+ const java = runCmd("java -version", { retry: true });
47690
+ lines.push(`[Java] ${java ?? "NOT FOUND"}${java ? " (requires >= 17)" : ""}`);
47691
+ lines.push(`[Node.js] ${process.version}`);
47692
+ const ohpm = runCmd("ohpm -v");
47693
+ lines.push(`[ohpm] ${ohpm ?? "NOT FOUND"}`);
47694
+ const hvigorw = runCmd("hvigorw -v") || runCmdToFile("hvigorw -v");
47695
+ lines.push(`[hvigorw] ${hvigorw ?? "NOT FOUND"}`);
47645
47696
  const sdkHome = process.env.DEVECO_SDK_HOME;
47646
47697
  lines.push(`[DEVECO_SDK_HOME] ${sdkHome ?? "NOT SET"}`);
47647
47698
  return lines.join("\n\n");
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.18",
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",