@harmonyos-arkts/opencode-plugin 0.0.10 → 0.0.11
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 +1 -1
- package/dist/index.js +445 -114
- package/package.json +1 -1
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` 工具搜索和查看华为开发者文档(含官方文档与社区内容),支持关键词搜索和分页查看全文
|
|
12
12
|
|
|
13
13
|
## 快速开始
|
|
14
14
|
|
package/dist/index.js
CHANGED
|
@@ -45368,23 +45368,63 @@ function skillSearchTool(managers) {
|
|
|
45368
45368
|
var BASE_URL = "https://svc-drcn.developer.huawei.com/community/servlet";
|
|
45369
45369
|
var HEADERS = {
|
|
45370
45370
|
"Content-Type": "application/json",
|
|
45371
|
-
|
|
45371
|
+
Referer: "https://developer.huawei.com/consumer/cn/doc/",
|
|
45372
45372
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
|
|
45373
45373
|
};
|
|
45374
|
+
var MAX_RETRIES = 2;
|
|
45375
|
+
var RETRY_DELAY_MS = 1e3;
|
|
45376
|
+
async function sleep(ms) {
|
|
45377
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
45378
|
+
}
|
|
45374
45379
|
async function post(path7, body) {
|
|
45375
|
-
|
|
45376
|
-
|
|
45377
|
-
|
|
45378
|
-
|
|
45379
|
-
|
|
45380
|
-
|
|
45381
|
-
|
|
45382
|
-
|
|
45383
|
-
|
|
45384
|
-
|
|
45385
|
-
|
|
45380
|
+
let lastError;
|
|
45381
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
45382
|
+
try {
|
|
45383
|
+
const res = await fetch(BASE_URL + path7, {
|
|
45384
|
+
method: "POST",
|
|
45385
|
+
headers: HEADERS,
|
|
45386
|
+
body: JSON.stringify(body)
|
|
45387
|
+
});
|
|
45388
|
+
if (!res.ok) {
|
|
45389
|
+
const retriable = res.status === 429 || res.status >= 500;
|
|
45390
|
+
if (retriable && attempt < MAX_RETRIES) {
|
|
45391
|
+
lastError = new Error(`API request failed: ${res.status} ${res.statusText} for ${path7}`);
|
|
45392
|
+
await sleep(RETRY_DELAY_MS * (attempt + 1));
|
|
45393
|
+
continue;
|
|
45394
|
+
}
|
|
45395
|
+
throw new Error(`API request failed: ${res.status} ${res.statusText} for ${path7}`);
|
|
45396
|
+
}
|
|
45397
|
+
const data = await res.json();
|
|
45398
|
+
if (data.code && data.code !== 0 && data.code !== "0") {
|
|
45399
|
+
throw new Error(`API error [${data.code}]: ${data.message} for ${path7}`);
|
|
45400
|
+
}
|
|
45401
|
+
return data;
|
|
45402
|
+
} catch (e) {
|
|
45403
|
+
if (e instanceof TypeError && e.message.includes("fetch")) {
|
|
45404
|
+
lastError = e;
|
|
45405
|
+
if (attempt < MAX_RETRIES) {
|
|
45406
|
+
await sleep(RETRY_DELAY_MS * (attempt + 1));
|
|
45407
|
+
continue;
|
|
45408
|
+
}
|
|
45409
|
+
}
|
|
45410
|
+
throw e;
|
|
45411
|
+
}
|
|
45386
45412
|
}
|
|
45387
|
-
|
|
45413
|
+
throw lastError ?? new Error("Request failed after retries");
|
|
45414
|
+
}
|
|
45415
|
+
var CATALOG_DISPLAY_NAMES = {
|
|
45416
|
+
"harmonyos-guides": "\u6307\u5357",
|
|
45417
|
+
"harmonyos-references": "API\u53C2\u8003",
|
|
45418
|
+
"harmonyos-best-practices": "\u6700\u4F73\u5B9E\u8DF5",
|
|
45419
|
+
"harmonyos-faqs": "FAQ",
|
|
45420
|
+
"design-guides": "\u8BBE\u8BA1\u6307\u5357",
|
|
45421
|
+
"atomic-guides": "\u6307\u5357",
|
|
45422
|
+
"atomic-references": "API\u53C2\u8003",
|
|
45423
|
+
"atomic-faqs": "FAQ",
|
|
45424
|
+
"atomic-ascf": "ASCF\u6846\u67B6"
|
|
45425
|
+
};
|
|
45426
|
+
function catalogDisplayName(catalogName) {
|
|
45427
|
+
return CATALOG_DISPLAY_NAMES[catalogName] || "";
|
|
45388
45428
|
}
|
|
45389
45429
|
async function searchDocs(keyword, maxResults = 10) {
|
|
45390
45430
|
const data = await post(
|
|
@@ -45405,12 +45445,29 @@ async function searchDocs(keyword, maxResults = 10) {
|
|
|
45405
45445
|
}
|
|
45406
45446
|
);
|
|
45407
45447
|
return (data.resultList ?? []).map((r) => {
|
|
45448
|
+
let subsection = "";
|
|
45449
|
+
let anchorId = "";
|
|
45450
|
+
if (r.anchorHighlightInfo) {
|
|
45451
|
+
subsection = r.anchorHighlightInfo.replace(/<\/?em>/g, "");
|
|
45452
|
+
}
|
|
45408
45453
|
let breadcrumb = [];
|
|
45409
45454
|
try {
|
|
45410
45455
|
const ext = typeof r.metaData?.ext === "string" ? JSON.parse(r.metaData.ext) : r.metaData?.ext ?? {};
|
|
45411
45456
|
const nodeNames = Array.isArray(ext.nodeNames) ? ext.nodeNames : [];
|
|
45457
|
+
const catalogName = typeof ext.catalogName === "string" ? ext.catalogName : "";
|
|
45412
45458
|
if (nodeNames.length > 0) {
|
|
45413
|
-
|
|
45459
|
+
const rootName = catalogDisplayName(catalogName) || catalogName;
|
|
45460
|
+
breadcrumb = rootName ? [rootName, ...nodeNames] : nodeNames;
|
|
45461
|
+
}
|
|
45462
|
+
if (subsection) {
|
|
45463
|
+
try {
|
|
45464
|
+
const anchors = typeof ext.anchorList === "string" ? JSON.parse(ext.anchorList) : ext.anchorList;
|
|
45465
|
+
if (Array.isArray(anchors)) {
|
|
45466
|
+
const match = anchors.find((a) => a.title === subsection);
|
|
45467
|
+
if (match) anchorId = match.anchorId ?? "";
|
|
45468
|
+
}
|
|
45469
|
+
} catch {
|
|
45470
|
+
}
|
|
45414
45471
|
}
|
|
45415
45472
|
} catch {
|
|
45416
45473
|
}
|
|
@@ -45420,15 +45477,31 @@ async function searchDocs(keyword, maxResults = 10) {
|
|
|
45420
45477
|
excerpt: (r.content ?? "").replace(/\n/g, " ").substring(0, 200),
|
|
45421
45478
|
breadcrumb,
|
|
45422
45479
|
contentType: r.metaData?.type ?? 0,
|
|
45423
|
-
timestamp: r.metaData?.timestamp ?? ""
|
|
45480
|
+
timestamp: r.metaData?.timestamp ?? "",
|
|
45481
|
+
subsection,
|
|
45482
|
+
anchorId
|
|
45424
45483
|
};
|
|
45425
45484
|
});
|
|
45426
45485
|
}
|
|
45427
45486
|
function objectIdFromUrl(url3) {
|
|
45428
45487
|
try {
|
|
45429
|
-
const
|
|
45488
|
+
const parsed = new URL(url3);
|
|
45489
|
+
const pathname = parsed.pathname.replace(/\/$/, "");
|
|
45490
|
+
const queryId = parsed.searchParams.get("objectId") || parsed.searchParams.get("id");
|
|
45491
|
+
if (queryId) return queryId;
|
|
45430
45492
|
const parts = pathname.split("/");
|
|
45431
|
-
|
|
45493
|
+
const lastSegment = parts[parts.length - 1];
|
|
45494
|
+
if (lastSegment && lastSegment.length > 2) return lastSegment;
|
|
45495
|
+
if (parts.length >= 2) {
|
|
45496
|
+
const secondLast = parts[parts.length - 2];
|
|
45497
|
+
if (secondLast && secondLast.length > 2) return secondLast;
|
|
45498
|
+
}
|
|
45499
|
+
const hash3 = parsed.hash;
|
|
45500
|
+
if (hash3) {
|
|
45501
|
+
const hashId = hash3.replace(/^#/, "");
|
|
45502
|
+
if (hashId.length > 5) return hashId;
|
|
45503
|
+
}
|
|
45504
|
+
return null;
|
|
45432
45505
|
} catch {
|
|
45433
45506
|
return null;
|
|
45434
45507
|
}
|
|
@@ -45442,50 +45515,72 @@ async function fetchDocument(objectId, language = "cn") {
|
|
|
45442
45515
|
if (!v) {
|
|
45443
45516
|
throw new Error(`Empty response for objectId: ${objectId}`);
|
|
45444
45517
|
}
|
|
45518
|
+
const html = v.content?.content ?? "";
|
|
45445
45519
|
return {
|
|
45446
45520
|
title: v.title ?? "",
|
|
45447
|
-
html
|
|
45521
|
+
html,
|
|
45448
45522
|
objectId,
|
|
45449
45523
|
catalogName: v.catalogName ?? "",
|
|
45450
45524
|
displayUpdateTime: v.displayUpdateTime ?? ""
|
|
45451
45525
|
};
|
|
45452
45526
|
}
|
|
45453
|
-
|
|
45454
|
-
|
|
45455
|
-
|
|
45456
|
-
|
|
45457
|
-
|
|
45458
|
-
|
|
45459
|
-
|
|
45460
|
-
|
|
45461
|
-
|
|
45462
|
-
|
|
45463
|
-
|
|
45464
|
-
|
|
45465
|
-
|
|
45466
|
-
|
|
45467
|
-
|
|
45468
|
-
|
|
45469
|
-
|
|
45470
|
-
|
|
45471
|
-
|
|
45472
|
-
|
|
45473
|
-
parts.push(` Path: ${r.breadcrumb.join(" > ")}`);
|
|
45474
|
-
}
|
|
45475
|
-
parts.push(` URL: ${r.url}`);
|
|
45476
|
-
if (r.excerpt) {
|
|
45477
|
-
parts.push(` ${r.excerpt}`);
|
|
45478
|
-
}
|
|
45479
|
-
return parts.join("\n");
|
|
45480
|
-
}).join("\n\n");
|
|
45481
|
-
return `Found ${results.length} results for "${args.query}":
|
|
45482
|
-
|
|
45483
|
-
${formatted}`;
|
|
45484
|
-
} catch (e) {
|
|
45485
|
-
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
45527
|
+
var treeCache = /* @__PURE__ */ new Map();
|
|
45528
|
+
async function getCatalogTree(language, catalogName, objectId) {
|
|
45529
|
+
const body = { language, catalogName };
|
|
45530
|
+
if (objectId) body.objectId = objectId;
|
|
45531
|
+
const data = await post(
|
|
45532
|
+
"/consumer/cn/documentPortal/getCatalogTree",
|
|
45533
|
+
body
|
|
45534
|
+
);
|
|
45535
|
+
return data.value?.catalogTreeList ?? [];
|
|
45536
|
+
}
|
|
45537
|
+
async function resolveBreadcrumb(language, catalogName, relateDocument) {
|
|
45538
|
+
if (!relateDocument) return [];
|
|
45539
|
+
const cacheKey = `${language}:${catalogName}`;
|
|
45540
|
+
if (!treeCache.has(cacheKey)) {
|
|
45541
|
+
const nodes = await getCatalogTree(language, catalogName);
|
|
45542
|
+
const map4 = /* @__PURE__ */ new Map();
|
|
45543
|
+
const walk = (list) => {
|
|
45544
|
+
for (const node of list) {
|
|
45545
|
+
map4.set(node.nodeId, { name: node.nodeName, parent: node.parent, rel: node.relateDocument });
|
|
45546
|
+
if (node.children?.length) walk(node.children);
|
|
45486
45547
|
}
|
|
45548
|
+
};
|
|
45549
|
+
walk(nodes);
|
|
45550
|
+
treeCache.set(cacheKey, map4);
|
|
45551
|
+
}
|
|
45552
|
+
const map3 = treeCache.get(cacheKey);
|
|
45553
|
+
let target;
|
|
45554
|
+
for (const node of map3.values()) {
|
|
45555
|
+
if (node.rel === relateDocument) {
|
|
45556
|
+
target = node;
|
|
45557
|
+
break;
|
|
45487
45558
|
}
|
|
45488
|
-
}
|
|
45559
|
+
}
|
|
45560
|
+
if (!target) return [];
|
|
45561
|
+
const breadcrumb = [];
|
|
45562
|
+
let current = target;
|
|
45563
|
+
while (current) {
|
|
45564
|
+
breadcrumb.unshift(current.name);
|
|
45565
|
+
current = current.parent ? map3.get(current.parent) : void 0;
|
|
45566
|
+
}
|
|
45567
|
+
const rootName = catalogDisplayName(catalogName);
|
|
45568
|
+
if (rootName && breadcrumb[0] !== rootName) {
|
|
45569
|
+
breadcrumb.unshift(rootName);
|
|
45570
|
+
}
|
|
45571
|
+
return breadcrumb;
|
|
45572
|
+
}
|
|
45573
|
+
function formatUpdateTime(utcTimeStr) {
|
|
45574
|
+
if (!utcTimeStr) return "";
|
|
45575
|
+
const dt = /* @__PURE__ */ new Date(utcTimeStr.replace(" ", "T") + "Z");
|
|
45576
|
+
if (isNaN(dt.getTime())) return utcTimeStr;
|
|
45577
|
+
const bj = new Date(dt.getTime() + 8 * 60 * 60 * 1e3);
|
|
45578
|
+
const y = bj.getUTCFullYear();
|
|
45579
|
+
const m = String(bj.getUTCMonth() + 1).padStart(2, "0");
|
|
45580
|
+
const d = String(bj.getUTCDate()).padStart(2, "0");
|
|
45581
|
+
const h = String(bj.getUTCHours()).padStart(2, "0");
|
|
45582
|
+
const min = String(bj.getUTCMinutes()).padStart(2, "0");
|
|
45583
|
+
return `${y}-${m}-${d} ${h}:${min}`;
|
|
45489
45584
|
}
|
|
45490
45585
|
|
|
45491
45586
|
// node_modules/turndown/lib/turndown.es.js
|
|
@@ -46176,18 +46271,61 @@ var removeUIParagraphs = {
|
|
|
46176
46271
|
return "";
|
|
46177
46272
|
}
|
|
46178
46273
|
};
|
|
46274
|
+
var NAV_SHORT_TEXT = /* @__PURE__ */ new Set([
|
|
46275
|
+
"\u7B80\u4F53\u4E2D\u6587",
|
|
46276
|
+
"English",
|
|
46277
|
+
"\u4E0B\u8F7D App",
|
|
46278
|
+
"\u63A2\u7D22",
|
|
46279
|
+
"\u8BBE\u8BA1",
|
|
46280
|
+
"\u5F00\u53D1",
|
|
46281
|
+
"\u5206\u53D1",
|
|
46282
|
+
"\u63A8\u5E7F\u4E0E\u53D8\u73B0",
|
|
46283
|
+
"\u751F\u6001\u5408\u4F5C",
|
|
46284
|
+
"\u652F\u6301",
|
|
46285
|
+
"\u66F4\u591A",
|
|
46286
|
+
"\u7ACB\u5373\u767B\u5F55",
|
|
46287
|
+
"\u8F93\u5165\u5173\u952E\u5B57\u641C\u7D22",
|
|
46288
|
+
"Hello\uFF0C",
|
|
46289
|
+
"\u6B22\u8FCE\u6765\u5230\u5F00\u53D1\u8005\u8054\u76DF",
|
|
46290
|
+
"CTRL+K",
|
|
46291
|
+
"Created with Pixso",
|
|
46292
|
+
"\u7248\u672C\u8BF4\u660E",
|
|
46293
|
+
"\u6307\u5357",
|
|
46294
|
+
"API\u53C2\u8003",
|
|
46295
|
+
"\u6700\u4F73\u5B9E\u8DF5",
|
|
46296
|
+
"FAQ",
|
|
46297
|
+
"\u53D8\u66F4\u9884\u544A",
|
|
46298
|
+
"\u591A\u8BBE\u5907\u573A\u666F",
|
|
46299
|
+
"\u624B\u673A",
|
|
46300
|
+
"\u5E94\u7528\u8D28\u91CF",
|
|
46301
|
+
"\u6280\u672F\u8D28\u91CF",
|
|
46302
|
+
"\u5F00\u53D1\u8005\u80FD\u529B\u8BA4\u8BC1",
|
|
46303
|
+
"\u6211\u7684",
|
|
46304
|
+
"\u7BA1\u7406\u4E2D\u5FC3",
|
|
46305
|
+
"\u4E2A\u4EBA\u4E2D\u5FC3",
|
|
46306
|
+
"\u6211\u7684\u5B66\u5802",
|
|
46307
|
+
"\u6211\u7684\u6536\u85CF",
|
|
46308
|
+
"\u6211\u7684\u6D3B\u52A8",
|
|
46309
|
+
"\u6211\u7684\u5DE5\u5355"
|
|
46310
|
+
]);
|
|
46179
46311
|
var removeNavigation = {
|
|
46180
46312
|
filter(node) {
|
|
46181
46313
|
if (node.nodeType !== 1) return false;
|
|
46314
|
+
const text = (node.textContent ?? "").trim();
|
|
46315
|
+
if (text.length <= 20 && NAV_SHORT_TEXT.has(text)) {
|
|
46316
|
+
return true;
|
|
46317
|
+
}
|
|
46318
|
+
if (node.nodeName === "P" && text.length === 0) {
|
|
46319
|
+
const hasContent = node.querySelector("img, code, pre, table");
|
|
46320
|
+
if (!hasContent) return true;
|
|
46321
|
+
}
|
|
46182
46322
|
const cls = node.className?.toLowerCase() ?? "";
|
|
46183
46323
|
const id = node.id?.toLowerCase() ?? "";
|
|
46184
46324
|
const unwantedClasses = [
|
|
46185
46325
|
"top-bar",
|
|
46186
46326
|
"menu-bar",
|
|
46187
|
-
"sidebar",
|
|
46188
46327
|
"search-bar",
|
|
46189
46328
|
"breadcrumb",
|
|
46190
|
-
"toolbar",
|
|
46191
46329
|
"footer-nav",
|
|
46192
46330
|
"main-nav",
|
|
46193
46331
|
"side-nav",
|
|
@@ -46204,7 +46342,29 @@ var removeNavigation = {
|
|
|
46204
46342
|
"feedback-section",
|
|
46205
46343
|
"qrcode-section"
|
|
46206
46344
|
];
|
|
46207
|
-
|
|
46345
|
+
const classList = cls.split(/\s+/);
|
|
46346
|
+
const matchesClass = classList.some(
|
|
46347
|
+
(c) => unwantedClasses.some((u) => c === u || c.startsWith(u + "-") || c.endsWith("-" + u))
|
|
46348
|
+
);
|
|
46349
|
+
const matchesId = unwantedClasses.some(
|
|
46350
|
+
(u) => id === u || id.startsWith(u + "-") || id.endsWith("-" + u)
|
|
46351
|
+
);
|
|
46352
|
+
return matchesClass || matchesId;
|
|
46353
|
+
},
|
|
46354
|
+
replacement() {
|
|
46355
|
+
return "";
|
|
46356
|
+
}
|
|
46357
|
+
};
|
|
46358
|
+
var removeCodeUI = {
|
|
46359
|
+
filter(node) {
|
|
46360
|
+
if (node.nodeType === 3) {
|
|
46361
|
+
return isUnwantedText(node.textContent ?? "");
|
|
46362
|
+
}
|
|
46363
|
+
if (node.nodeType === 1 && ["P", "DIV", "SPAN"].includes(node.nodeName)) {
|
|
46364
|
+
const text = (node.textContent ?? "").trim();
|
|
46365
|
+
return isUnwantedText(text);
|
|
46366
|
+
}
|
|
46367
|
+
return false;
|
|
46208
46368
|
},
|
|
46209
46369
|
replacement() {
|
|
46210
46370
|
return "";
|
|
@@ -46240,6 +46400,18 @@ var huaweiCodeBlock = {
|
|
|
46240
46400
|
return "\n```" + language + "\n" + lines.join("\n") + "\n```\n";
|
|
46241
46401
|
}
|
|
46242
46402
|
};
|
|
46403
|
+
function inferLanguage(codeText) {
|
|
46404
|
+
if (codeText.includes("import ") || codeText.includes("export ") || codeText.includes("interface ")) {
|
|
46405
|
+
return "typescript";
|
|
46406
|
+
}
|
|
46407
|
+
if (codeText.includes("#include") || codeText.includes("int main")) {
|
|
46408
|
+
return "cpp";
|
|
46409
|
+
}
|
|
46410
|
+
if (codeText.includes("public class")) {
|
|
46411
|
+
return "java";
|
|
46412
|
+
}
|
|
46413
|
+
return "";
|
|
46414
|
+
}
|
|
46243
46415
|
var genericPre = {
|
|
46244
46416
|
filter(node) {
|
|
46245
46417
|
return node.nodeName === "PRE" && !node.querySelector("ol.linenums");
|
|
@@ -46257,6 +46429,9 @@ var genericPre = {
|
|
|
46257
46429
|
const codeMatch = (codeElem.className ?? "").match(/language-(\w+)/);
|
|
46258
46430
|
if (codeMatch) language = codeMatch[1];
|
|
46259
46431
|
}
|
|
46432
|
+
if (!language && codeText) {
|
|
46433
|
+
language = inferLanguage(codeText);
|
|
46434
|
+
}
|
|
46260
46435
|
const cleanLines = codeText.split("\n").filter((line) => {
|
|
46261
46436
|
const trimmed = line.trim();
|
|
46262
46437
|
return !trimmed || !isUnwantedText(trimmed);
|
|
@@ -46264,6 +46439,30 @@ var genericPre = {
|
|
|
46264
46439
|
return "\n```" + language + "\n" + cleanLines.join("\n") + "\n```\n";
|
|
46265
46440
|
}
|
|
46266
46441
|
};
|
|
46442
|
+
var standardCodeBlock = {
|
|
46443
|
+
filter(node) {
|
|
46444
|
+
return node.nodeName === "PRE" && !!node.firstChild && node.firstChild.nodeName === "CODE";
|
|
46445
|
+
},
|
|
46446
|
+
replacement(content, node) {
|
|
46447
|
+
const codeNode = node.firstChild;
|
|
46448
|
+
const lang = codeNode.className || codeNode.getAttribute("class") || "";
|
|
46449
|
+
const langMatch = lang.match(/language-(\w+)|hljs language-(\w+)/);
|
|
46450
|
+
const language = langMatch ? langMatch[1] || langMatch[2] : "";
|
|
46451
|
+
const codeContent = codeNode.textContent || content;
|
|
46452
|
+
const cleanContent = codeContent.split("\n").filter((line) => {
|
|
46453
|
+
const trimmed = line.trim();
|
|
46454
|
+
return !isUnwantedText(trimmed);
|
|
46455
|
+
}).join("\n");
|
|
46456
|
+
return "\n```" + language + "\n" + cleanContent + "\n```\n";
|
|
46457
|
+
}
|
|
46458
|
+
};
|
|
46459
|
+
function cleanCellText(text) {
|
|
46460
|
+
let cleaned = text.replace(/\s+/g, " ");
|
|
46461
|
+
for (const t of UI_TEXT) {
|
|
46462
|
+
cleaned = cleaned.replaceAll(t, "");
|
|
46463
|
+
}
|
|
46464
|
+
return cleaned.trim();
|
|
46465
|
+
}
|
|
46267
46466
|
var harmonyTable = {
|
|
46268
46467
|
filter(node) {
|
|
46269
46468
|
return node.nodeName === "TABLE";
|
|
@@ -46282,7 +46481,7 @@ var harmonyTable = {
|
|
|
46282
46481
|
}
|
|
46283
46482
|
const code = cell.querySelector("code");
|
|
46284
46483
|
if (code) return `\`${code.textContent?.trim() ?? ""}\``;
|
|
46285
|
-
return (cell.textContent ?? "")
|
|
46484
|
+
return cleanCellText(cell.textContent ?? "");
|
|
46286
46485
|
});
|
|
46287
46486
|
if (cells.length === 0) return;
|
|
46288
46487
|
md += "| " + cells.join(" | ") + " |\n";
|
|
@@ -46294,6 +46493,49 @@ var harmonyTable = {
|
|
|
46294
46493
|
return md + "\n";
|
|
46295
46494
|
}
|
|
46296
46495
|
};
|
|
46496
|
+
var divTable = {
|
|
46497
|
+
filter(node) {
|
|
46498
|
+
if (node.nodeType !== 1 || node.nodeName !== "DIV") return false;
|
|
46499
|
+
const cls = (node.className ?? "").toLowerCase();
|
|
46500
|
+
const tableClasses = ["table", "tbl", "data-table", "table-container", "table-wrap"];
|
|
46501
|
+
if (!tableClasses.some((c) => cls.includes(c))) return false;
|
|
46502
|
+
const rowDivs = Array.from(node.children).filter((child) => {
|
|
46503
|
+
const childClass = (child.className ?? "").toLowerCase();
|
|
46504
|
+
return childClass.includes("tr") || childClass.includes("row") || childClass.includes("table-row");
|
|
46505
|
+
});
|
|
46506
|
+
return rowDivs.length > 0;
|
|
46507
|
+
},
|
|
46508
|
+
replacement(content, node) {
|
|
46509
|
+
const rows = Array.from(node.children).filter((child) => {
|
|
46510
|
+
const childClass = (child.className ?? "").toLowerCase();
|
|
46511
|
+
return childClass.includes("tr") || childClass.includes("row") || childClass.includes("table-row");
|
|
46512
|
+
});
|
|
46513
|
+
if (rows.length === 0) return content;
|
|
46514
|
+
let md = "\n";
|
|
46515
|
+
rows.forEach((row, rowIndex) => {
|
|
46516
|
+
const cells = Array.from(row.children).filter((child) => {
|
|
46517
|
+
const childClass = (child.className ?? "").toLowerCase();
|
|
46518
|
+
return childClass.includes("td") || childClass.includes("th") || childClass.includes("cell");
|
|
46519
|
+
}).map((cell) => {
|
|
46520
|
+
const link = cell.querySelector("a");
|
|
46521
|
+
if (link?.getAttribute("href")) {
|
|
46522
|
+
const href = link.getAttribute("href");
|
|
46523
|
+
const full = href.startsWith("http") ? href : `https://developer.huawei.com${href}`;
|
|
46524
|
+
return `[${link.textContent?.trim() ?? ""}](${full})`;
|
|
46525
|
+
}
|
|
46526
|
+
const code = cell.querySelector("code");
|
|
46527
|
+
if (code) return `\`${code.textContent?.trim() ?? ""}\``;
|
|
46528
|
+
return cleanCellText(cell.textContent ?? "");
|
|
46529
|
+
});
|
|
46530
|
+
if (cells.length === 0) return;
|
|
46531
|
+
md += "| " + cells.join(" | ") + " |\n";
|
|
46532
|
+
if (rowIndex === 0) {
|
|
46533
|
+
md += "| " + cells.map(() => "---").join(" | ") + " |\n";
|
|
46534
|
+
}
|
|
46535
|
+
});
|
|
46536
|
+
return md + "\n";
|
|
46537
|
+
}
|
|
46538
|
+
};
|
|
46297
46539
|
var processLinks = {
|
|
46298
46540
|
filter(node) {
|
|
46299
46541
|
return node.nodeName === "A" && !!node.getAttribute("href");
|
|
@@ -46310,10 +46552,13 @@ function addCustomRules(td) {
|
|
|
46310
46552
|
["removeCodeUIContainers", removeCodeUIContainers],
|
|
46311
46553
|
["removeUIParagraphs", removeUIParagraphs],
|
|
46312
46554
|
["removeNavigation", removeNavigation],
|
|
46555
|
+
["removeCodeUI", removeCodeUI],
|
|
46313
46556
|
["removePlatformBadge", removePlatformBadge],
|
|
46314
46557
|
["huaweiCodeBlock", huaweiCodeBlock],
|
|
46315
46558
|
["genericPre", genericPre],
|
|
46559
|
+
["standardCodeBlock", standardCodeBlock],
|
|
46316
46560
|
["harmonyTable", harmonyTable],
|
|
46561
|
+
["divTable", divTable],
|
|
46317
46562
|
["processLinks", processLinks]
|
|
46318
46563
|
];
|
|
46319
46564
|
for (const [name, rule] of rules2) {
|
|
@@ -46388,7 +46633,7 @@ function cleanupMarkdown(md) {
|
|
|
46388
46633
|
return out.trim();
|
|
46389
46634
|
}
|
|
46390
46635
|
|
|
46391
|
-
// src/tools/harmony-doc/doc-
|
|
46636
|
+
// src/tools/harmony-doc/harmony-doc-tool.ts
|
|
46392
46637
|
var docCache = /* @__PURE__ */ new Map();
|
|
46393
46638
|
var CACHE_TTL = 10 * 60 * 1e3;
|
|
46394
46639
|
function getCached(objectId) {
|
|
@@ -46400,71 +46645,158 @@ function getCached(objectId) {
|
|
|
46400
46645
|
}
|
|
46401
46646
|
return entry;
|
|
46402
46647
|
}
|
|
46403
|
-
function setCache(objectId, doc, markdown) {
|
|
46404
|
-
docCache.set(objectId, { doc, markdown, fetchedAt: Date.now() });
|
|
46648
|
+
function setCache(objectId, doc, markdown, breadcrumb) {
|
|
46649
|
+
docCache.set(objectId, { doc, markdown, breadcrumb, fetchedAt: Date.now() });
|
|
46405
46650
|
}
|
|
46406
|
-
function
|
|
46651
|
+
function harmonyDocTool(_managers) {
|
|
46407
46652
|
return tool({
|
|
46408
|
-
description: "
|
|
46653
|
+
description: "HarmonyOS developer documentation tool with two actions: 'search' and 'view'. Use 'search' to find documents by keyword; use 'view' to read a specific document by URL. Typical workflow: search first, then view the relevant result. NOTE: This tool makes network requests and may take longer to respond. Prefer using skillSearch first for component usage, Kit capabilities, and common development patterns \u2014 only fall back to this tool when skillSearch does not cover the topic.",
|
|
46409
46654
|
args: {
|
|
46410
|
-
|
|
46411
|
-
|
|
46655
|
+
action: tool.schema.enum(["search", "view"]).describe("Action to perform: 'search' to find documents by keyword, 'view' to read a document by URL."),
|
|
46656
|
+
query: tool.schema.string("Search keyword or phrase (for action='search'). Use specific terms, e.g. 'Button component', 'router navigation'.").optional(),
|
|
46657
|
+
maxResults: tool.schema.number("Maximum search results to return (for action='search').").min(1).max(20).default(10),
|
|
46658
|
+
url: tool.schema.string("Full URL of the document to view (for action='view'). Must be a developer.huawei.com URL from search results.").optional(),
|
|
46659
|
+
page: tool.schema.number("Page number for long documents (for action='view'). Starts at 1.").min(1).default(1)
|
|
46412
46660
|
},
|
|
46413
46661
|
execute: async (args, _context) => {
|
|
46414
|
-
|
|
46415
|
-
|
|
46416
|
-
|
|
46417
|
-
|
|
46418
|
-
|
|
46419
|
-
|
|
46420
|
-
|
|
46421
|
-
|
|
46422
|
-
|
|
46423
|
-
|
|
46424
|
-
|
|
46425
|
-
|
|
46426
|
-
|
|
46427
|
-
|
|
46428
|
-
|
|
46429
|
-
|
|
46430
|
-
|
|
46431
|
-
|
|
46432
|
-
|
|
46433
|
-
|
|
46434
|
-
|
|
46435
|
-
}
|
|
46436
|
-
|
|
46437
|
-
|
|
46438
|
-
|
|
46439
|
-
|
|
46440
|
-
|
|
46441
|
-
|
|
46442
|
-
|
|
46443
|
-
|
|
46444
|
-
|
|
46662
|
+
if (args.action === "search") {
|
|
46663
|
+
return executeSearch(args.query, args.maxResults);
|
|
46664
|
+
}
|
|
46665
|
+
return executeView(args.url, args.page);
|
|
46666
|
+
}
|
|
46667
|
+
});
|
|
46668
|
+
}
|
|
46669
|
+
async function executeSearch(query, maxResults) {
|
|
46670
|
+
if (!query) {
|
|
46671
|
+
return "Error: 'query' is required when action is 'search'.";
|
|
46672
|
+
}
|
|
46673
|
+
try {
|
|
46674
|
+
log("[harmony-doc search]", { query, maxResults });
|
|
46675
|
+
const results = await searchDocs(query, maxResults ?? 10);
|
|
46676
|
+
if (results.length === 0) {
|
|
46677
|
+
return `No results found for "${query}". Try different keywords.`;
|
|
46678
|
+
}
|
|
46679
|
+
const formatted = results.map((r, i) => {
|
|
46680
|
+
const parts = [];
|
|
46681
|
+
let title = r.title;
|
|
46682
|
+
if (r.subsection && r.subsection !== r.title) {
|
|
46683
|
+
title += ` #${r.subsection}`;
|
|
46684
|
+
}
|
|
46685
|
+
parts.push(`${i + 1}. **${title}**`);
|
|
46686
|
+
if (r.breadcrumb.length > 0) {
|
|
46687
|
+
parts.push(` Path: ${r.breadcrumb.join(" > ")}`);
|
|
46688
|
+
}
|
|
46689
|
+
let displayUrl = r.url;
|
|
46690
|
+
if (r.anchorId) {
|
|
46691
|
+
displayUrl += `#${r.anchorId}`;
|
|
46692
|
+
}
|
|
46693
|
+
parts.push(` URL: ${displayUrl}`);
|
|
46694
|
+
if (r.excerpt) {
|
|
46695
|
+
parts.push(` ${r.excerpt}`);
|
|
46696
|
+
}
|
|
46697
|
+
return parts.join("\n");
|
|
46698
|
+
}).join("\n\n");
|
|
46699
|
+
return `Found ${results.length} results for "${query}":
|
|
46700
|
+
|
|
46701
|
+
${formatted}
|
|
46702
|
+
|
|
46703
|
+
Use action='view' with the URL to read the full document.`;
|
|
46704
|
+
} catch (e) {
|
|
46705
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
46706
|
+
}
|
|
46707
|
+
}
|
|
46708
|
+
async function executeView(url3, page) {
|
|
46709
|
+
if (!url3) {
|
|
46710
|
+
return "Error: 'url' is required when action is 'view'.";
|
|
46711
|
+
}
|
|
46712
|
+
try {
|
|
46713
|
+
log("[harmony-doc view]", { url: url3, page });
|
|
46714
|
+
const objectId = objectIdFromUrl(url3);
|
|
46715
|
+
if (!objectId) {
|
|
46716
|
+
return `Error: Cannot extract document ID from URL: ${url3}`;
|
|
46717
|
+
}
|
|
46718
|
+
let cached3 = getCached(objectId);
|
|
46719
|
+
let doc;
|
|
46720
|
+
let markdown;
|
|
46721
|
+
let breadcrumb;
|
|
46722
|
+
if (cached3) {
|
|
46723
|
+
doc = cached3.doc;
|
|
46724
|
+
markdown = cached3.markdown;
|
|
46725
|
+
breadcrumb = cached3.breadcrumb;
|
|
46726
|
+
log("[harmony-doc view] cache hit", { objectId, page });
|
|
46727
|
+
} else {
|
|
46728
|
+
doc = await fetchDocument(objectId);
|
|
46729
|
+
if (!doc.html) {
|
|
46730
|
+
log("[harmony-doc view] API returned empty HTML", { objectId, title: doc.title });
|
|
46731
|
+
return `Error: Document content is empty for ${url3} (objectId: ${objectId}). The API returned no HTML content \u2014 the document may not be available through this API.`;
|
|
46732
|
+
}
|
|
46733
|
+
markdown = htmlToMarkdown(doc.html);
|
|
46734
|
+
log("[harmony-doc view] conversion result", {
|
|
46735
|
+
objectId,
|
|
46736
|
+
htmlLength: doc.html.length,
|
|
46737
|
+
markdownLength: markdown.length,
|
|
46738
|
+
markdownPreview: markdown.substring(0, 300)
|
|
46739
|
+
});
|
|
46740
|
+
if (!markdown.trim()) {
|
|
46741
|
+
log("[harmony-doc view] markdown conversion produced empty output", {
|
|
46742
|
+
objectId,
|
|
46743
|
+
htmlLength: doc.html.length
|
|
46744
|
+
});
|
|
46745
|
+
return `Error: Document HTML was non-empty (${doc.html.length} chars) but converted to empty markdown for ${url3}. This may indicate the HTML structure is not supported.`;
|
|
46746
|
+
}
|
|
46747
|
+
breadcrumb = [];
|
|
46748
|
+
if (doc.catalogName) {
|
|
46749
|
+
try {
|
|
46750
|
+
breadcrumb = await resolveBreadcrumb("cn", doc.catalogName, objectId);
|
|
46751
|
+
} catch (e) {
|
|
46752
|
+
log("[harmony-doc view] breadcrumb resolution failed", { objectId, error: String(e) });
|
|
46753
|
+
}
|
|
46754
|
+
}
|
|
46755
|
+
setCache(objectId, doc, markdown, breadcrumb);
|
|
46756
|
+
log("[harmony-doc view] cache miss, fetched from network", { objectId });
|
|
46757
|
+
}
|
|
46758
|
+
const MAX_LENGTH = 3e4;
|
|
46759
|
+
const totalPages = Math.max(1, Math.ceil(markdown.length / MAX_LENGTH));
|
|
46760
|
+
const currentPage = Math.min(page ?? 1, totalPages);
|
|
46761
|
+
const start = (currentPage - 1) * MAX_LENGTH;
|
|
46762
|
+
const end = Math.min(currentPage * MAX_LENGTH, markdown.length);
|
|
46763
|
+
const chunk = markdown.substring(start, end);
|
|
46764
|
+
let output = `# ${doc.title}
|
|
46765
|
+
|
|
46766
|
+
Source: ${url3}
|
|
46445
46767
|
`;
|
|
46446
|
-
|
|
46447
|
-
|
|
46768
|
+
if (breadcrumb.length > 0) {
|
|
46769
|
+
output += `Path: ${breadcrumb.join(" > ")}
|
|
46448
46770
|
`;
|
|
46449
|
-
|
|
46450
|
-
|
|
46451
|
-
|
|
46771
|
+
}
|
|
46772
|
+
const updateTime = formatUpdateTime(doc.displayUpdateTime);
|
|
46773
|
+
if (updateTime) {
|
|
46774
|
+
output += `Updated: ${updateTime}
|
|
46452
46775
|
`;
|
|
46453
|
-
|
|
46454
|
-
|
|
46455
|
-
|
|
46456
|
-
|
|
46457
|
-
|
|
46776
|
+
}
|
|
46777
|
+
if (totalPages > 1) {
|
|
46778
|
+
output += `Page ${currentPage} of ${totalPages}
|
|
46779
|
+
`;
|
|
46780
|
+
}
|
|
46781
|
+
output += "\n---\n\n";
|
|
46782
|
+
output += chunk;
|
|
46783
|
+
if (currentPage < totalPages) {
|
|
46784
|
+
output += `
|
|
46458
46785
|
|
|
46459
46786
|
---
|
|
46460
|
-
[Document continues \u2014 call
|
|
46461
|
-
|
|
46462
|
-
|
|
46463
|
-
|
|
46464
|
-
|
|
46465
|
-
|
|
46466
|
-
|
|
46467
|
-
|
|
46787
|
+
[Document continues \u2014 call with action='view' and page=${currentPage + 1} to read more]`;
|
|
46788
|
+
}
|
|
46789
|
+
log("[harmony-doc view] RETURN", {
|
|
46790
|
+
objectId,
|
|
46791
|
+
outputLength: output.length,
|
|
46792
|
+
chunkLength: chunk.length,
|
|
46793
|
+
markdownLength: markdown.length,
|
|
46794
|
+
outputEnd: output.substring(Math.max(0, output.length - 200))
|
|
46795
|
+
});
|
|
46796
|
+
return output;
|
|
46797
|
+
} catch (e) {
|
|
46798
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
46799
|
+
}
|
|
46468
46800
|
}
|
|
46469
46801
|
|
|
46470
46802
|
// src/tools/builtin.ts
|
|
@@ -46472,8 +46804,7 @@ function createBuiltinTools(managers) {
|
|
|
46472
46804
|
return {
|
|
46473
46805
|
createHmTemplate: createHmTemplateTool(managers),
|
|
46474
46806
|
skillSearch: skillSearchTool(managers),
|
|
46475
|
-
"harmony-doc
|
|
46476
|
-
"harmony-doc-view": docViewTool(managers)
|
|
46807
|
+
"harmony-doc": harmonyDocTool(managers)
|
|
46477
46808
|
};
|
|
46478
46809
|
}
|
|
46479
46810
|
|
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.
|
|
4
|
+
"version": "0.0.11",
|
|
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",
|