@harmonyos-arkts/opencode-plugin 0.0.12 → 0.0.14
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/dist/index.js +207 -114
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5052,32 +5052,32 @@ var require_URL = __commonJS({
|
|
|
5052
5052
|
else
|
|
5053
5053
|
return basepath.substring(0, lastslash + 1) + refpath;
|
|
5054
5054
|
}
|
|
5055
|
-
function remove_dot_segments(
|
|
5056
|
-
if (!
|
|
5055
|
+
function remove_dot_segments(path9) {
|
|
5056
|
+
if (!path9) return path9;
|
|
5057
5057
|
var output = "";
|
|
5058
|
-
while (
|
|
5059
|
-
if (
|
|
5060
|
-
|
|
5058
|
+
while (path9.length > 0) {
|
|
5059
|
+
if (path9 === "." || path9 === "..") {
|
|
5060
|
+
path9 = "";
|
|
5061
5061
|
break;
|
|
5062
5062
|
}
|
|
5063
|
-
var twochars =
|
|
5064
|
-
var threechars =
|
|
5065
|
-
var fourchars =
|
|
5063
|
+
var twochars = path9.substring(0, 2);
|
|
5064
|
+
var threechars = path9.substring(0, 3);
|
|
5065
|
+
var fourchars = path9.substring(0, 4);
|
|
5066
5066
|
if (threechars === "../") {
|
|
5067
|
-
|
|
5067
|
+
path9 = path9.substring(3);
|
|
5068
5068
|
} else if (twochars === "./") {
|
|
5069
|
-
|
|
5069
|
+
path9 = path9.substring(2);
|
|
5070
5070
|
} else if (threechars === "/./") {
|
|
5071
|
-
|
|
5072
|
-
} else if (twochars === "/." &&
|
|
5073
|
-
|
|
5074
|
-
} else if (fourchars === "/../" || threechars === "/.." &&
|
|
5075
|
-
|
|
5071
|
+
path9 = "/" + path9.substring(3);
|
|
5072
|
+
} else if (twochars === "/." && path9.length === 2) {
|
|
5073
|
+
path9 = "/";
|
|
5074
|
+
} else if (fourchars === "/../" || threechars === "/.." && path9.length === 3) {
|
|
5075
|
+
path9 = "/" + path9.substring(4);
|
|
5076
5076
|
output = output.replace(/\/?[^\/]*$/, "");
|
|
5077
5077
|
} else {
|
|
5078
|
-
var segment =
|
|
5078
|
+
var segment = path9.match(/(\/?([^\/]*))/)[0];
|
|
5079
5079
|
output += segment;
|
|
5080
|
-
|
|
5080
|
+
path9 = path9.substring(segment.length);
|
|
5081
5081
|
}
|
|
5082
5082
|
}
|
|
5083
5083
|
return output;
|
|
@@ -18304,10 +18304,10 @@ function mergeDefs(...defs) {
|
|
|
18304
18304
|
function cloneDef(schema) {
|
|
18305
18305
|
return mergeDefs(schema._zod.def);
|
|
18306
18306
|
}
|
|
18307
|
-
function getElementAtPath(obj,
|
|
18308
|
-
if (!
|
|
18307
|
+
function getElementAtPath(obj, path9) {
|
|
18308
|
+
if (!path9)
|
|
18309
18309
|
return obj;
|
|
18310
|
-
return
|
|
18310
|
+
return path9.reduce((acc, key) => acc?.[key], obj);
|
|
18311
18311
|
}
|
|
18312
18312
|
function promiseAllObject(promisesObj) {
|
|
18313
18313
|
const keys = Object.keys(promisesObj);
|
|
@@ -18690,11 +18690,11 @@ function aborted(x, startIndex = 0) {
|
|
|
18690
18690
|
}
|
|
18691
18691
|
return false;
|
|
18692
18692
|
}
|
|
18693
|
-
function prefixIssues(
|
|
18693
|
+
function prefixIssues(path9, issues) {
|
|
18694
18694
|
return issues.map((iss) => {
|
|
18695
18695
|
var _a2;
|
|
18696
18696
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
18697
|
-
iss.path.unshift(
|
|
18697
|
+
iss.path.unshift(path9);
|
|
18698
18698
|
return iss;
|
|
18699
18699
|
});
|
|
18700
18700
|
}
|
|
@@ -18877,7 +18877,7 @@ function formatError(error92, mapper = (issue3) => issue3.message) {
|
|
|
18877
18877
|
}
|
|
18878
18878
|
function treeifyError(error92, mapper = (issue3) => issue3.message) {
|
|
18879
18879
|
const result = { errors: [] };
|
|
18880
|
-
const processError = (error93,
|
|
18880
|
+
const processError = (error93, path9 = []) => {
|
|
18881
18881
|
var _a2, _b;
|
|
18882
18882
|
for (const issue3 of error93.issues) {
|
|
18883
18883
|
if (issue3.code === "invalid_union" && issue3.errors.length) {
|
|
@@ -18887,7 +18887,7 @@ function treeifyError(error92, mapper = (issue3) => issue3.message) {
|
|
|
18887
18887
|
} else if (issue3.code === "invalid_element") {
|
|
18888
18888
|
processError({ issues: issue3.issues }, issue3.path);
|
|
18889
18889
|
} else {
|
|
18890
|
-
const fullpath = [...
|
|
18890
|
+
const fullpath = [...path9, ...issue3.path];
|
|
18891
18891
|
if (fullpath.length === 0) {
|
|
18892
18892
|
result.errors.push(mapper(issue3));
|
|
18893
18893
|
continue;
|
|
@@ -18919,8 +18919,8 @@ function treeifyError(error92, mapper = (issue3) => issue3.message) {
|
|
|
18919
18919
|
}
|
|
18920
18920
|
function toDotPath(_path) {
|
|
18921
18921
|
const segs = [];
|
|
18922
|
-
const
|
|
18923
|
-
for (const seg of
|
|
18922
|
+
const path9 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
18923
|
+
for (const seg of path9) {
|
|
18924
18924
|
if (typeof seg === "number")
|
|
18925
18925
|
segs.push(`[${seg}]`);
|
|
18926
18926
|
else if (typeof seg === "symbol")
|
|
@@ -30897,13 +30897,13 @@ function resolveRef(ref, ctx) {
|
|
|
30897
30897
|
if (!ref.startsWith("#")) {
|
|
30898
30898
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
30899
30899
|
}
|
|
30900
|
-
const
|
|
30901
|
-
if (
|
|
30900
|
+
const path9 = ref.slice(1).split("/").filter(Boolean);
|
|
30901
|
+
if (path9.length === 0) {
|
|
30902
30902
|
return ctx.rootSchema;
|
|
30903
30903
|
}
|
|
30904
30904
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
30905
|
-
if (
|
|
30906
|
-
const key =
|
|
30905
|
+
if (path9[0] === defsKey) {
|
|
30906
|
+
const key = path9[1];
|
|
30907
30907
|
if (!key || !ctx.defs[key]) {
|
|
30908
30908
|
throw new Error(`Reference not found: ${ref}`);
|
|
30909
30909
|
}
|
|
@@ -31452,31 +31452,28 @@ import path2 from "path";
|
|
|
31452
31452
|
import fs from "fs/promises";
|
|
31453
31453
|
import os2 from "os";
|
|
31454
31454
|
var paths = envPaths("hm-plugin");
|
|
31455
|
-
var
|
|
31456
|
-
|
|
31457
|
-
|
|
31458
|
-
|
|
31459
|
-
|
|
31460
|
-
|
|
31461
|
-
|
|
31462
|
-
|
|
31463
|
-
|
|
31464
|
-
|
|
31465
|
-
|
|
31466
|
-
|
|
31467
|
-
state: paths.data
|
|
31468
|
-
};
|
|
31469
|
-
})(HMGlobal || (HMGlobal = {}));
|
|
31455
|
+
var Path = {
|
|
31456
|
+
// Allow override via OPENCODE_TEST_HOME for test isolation
|
|
31457
|
+
get home() {
|
|
31458
|
+
return process.env.OPENCODE_TEST_HOME || os2.homedir();
|
|
31459
|
+
},
|
|
31460
|
+
data: paths.data,
|
|
31461
|
+
bin: path2.join(paths.data, "bin"),
|
|
31462
|
+
log: paths.log,
|
|
31463
|
+
cache: paths.cache,
|
|
31464
|
+
config: paths.config,
|
|
31465
|
+
state: paths.data
|
|
31466
|
+
};
|
|
31470
31467
|
await Promise.all([
|
|
31471
|
-
fs.mkdir(
|
|
31472
|
-
fs.mkdir(
|
|
31473
|
-
fs.mkdir(
|
|
31474
|
-
fs.mkdir(
|
|
31475
|
-
fs.mkdir(
|
|
31468
|
+
fs.mkdir(Path.data, { recursive: true }),
|
|
31469
|
+
fs.mkdir(Path.config, { recursive: true }),
|
|
31470
|
+
fs.mkdir(Path.state, { recursive: true }),
|
|
31471
|
+
fs.mkdir(Path.log, { recursive: true }),
|
|
31472
|
+
fs.mkdir(Path.bin, { recursive: true })
|
|
31476
31473
|
]);
|
|
31477
31474
|
|
|
31478
31475
|
// src/shared/log.ts
|
|
31479
|
-
var logFile = path3.join(
|
|
31476
|
+
var logFile = path3.join(Path.log, "plugin.log");
|
|
31480
31477
|
var logDir = path3.dirname(logFile);
|
|
31481
31478
|
var MAX_LOG_SIZE_BYTES = 5 * 1024 * 1024;
|
|
31482
31479
|
var MAX_LOG_AGE_DAYS = 7;
|
|
@@ -31568,10 +31565,11 @@ cleanupRotatedLogs();
|
|
|
31568
31565
|
|
|
31569
31566
|
// src/agents/prompt.ts
|
|
31570
31567
|
var HM_DESIGN = `
|
|
31571
|
-
###
|
|
31568
|
+
### Design Phase
|
|
31572
31569
|
- Use the \`harmonyos-prd-design\` skill to create prd desgin documents
|
|
31573
31570
|
- Reference design patterns and best practices from the skill documentation
|
|
31574
31571
|
- Design documents should be stored in the project's \`.harmonyos/\` directory
|
|
31572
|
+
- After UX renders the prototype, write it as \`prototype.html\`, then call \`html_preview\` with \`filePath\` set to that file's local path so the frontend can open it reliably
|
|
31575
31573
|
`;
|
|
31576
31574
|
var HM_DEVELOP = `
|
|
31577
31575
|
### Development Phase
|
|
@@ -31733,33 +31731,29 @@ function buildPlanPrompt(type) {
|
|
|
31733
31731
|
}
|
|
31734
31732
|
}
|
|
31735
31733
|
function buildAllAgentPrompt() {
|
|
31736
|
-
return
|
|
31734
|
+
return `- **New project (greenfield)**: Design \u2192 Development \u2192 Build. Do not skip the design phase, as it is critical for planning and architectural decisions.
|
|
31735
|
+
- **Complex tasks**: List a plan \u2192 Discuss plan details with the user if needed \u2192 Development \u2192 Build.
|
|
31736
|
+
- **Simple tasks**: Development \u2192 Build directly.
|
|
31737
|
+
|
|
31738
|
+
Each phase has its own set of best practices and workflows to ensure efficient and successful project completion.
|
|
31739
|
+
|
|
31737
31740
|
${HM_DESIGN}
|
|
31738
31741
|
${HM_DEVELOP}
|
|
31739
31742
|
${HM_BUILD}
|
|
31740
|
-
|
|
31741
|
-
Follow these workflows:
|
|
31742
|
-
- **New project (greenfield)**: PRD Design \u2192 Development \u2192 Build.
|
|
31743
|
-
- **Complex tasks**: List a plan \u2192 Discuss plan details with the user if needed \u2192 Development \u2192 Build.
|
|
31744
|
-
- **Simple tasks**: Development \u2192 Build directly.
|
|
31745
31743
|
`;
|
|
31746
31744
|
}
|
|
31747
31745
|
function buildHmAgentPrompt(type) {
|
|
31748
31746
|
return `You are HarmonyOS Dev Assistant(HDACode), an expert coding agent specialized in HarmonyOS development.
|
|
31749
|
-
|
|
31750
31747
|
You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
|
|
31751
|
-
|
|
31752
31748
|
IMPORTANT: You must NEVER generate or guess URLs for the user unless you are confident that the URLs are for helping the user with programming. You may use URLs provided by the user in their messages or local files.
|
|
31753
|
-
|
|
31754
31749
|
When the user directly asks about HDACode (eg. "can HDACode do...", "does HDACode have..."), or asks in second person (eg. "are you able...", "can you do..."), or asks how to use a specific HDACode feature (eg. implement a hook, write a slash command, or install an MCP server), use the WebFetch tool to gather information to answer the question from HDACode docs.
|
|
31755
|
-
|
|
31756
31750
|
**IMPORTANT**: All HarmonyOS development tasks must follow these workflows:
|
|
31757
31751
|
|
|
31758
31752
|
${buildPlanPrompt(type)}
|
|
31759
31753
|
|
|
31760
31754
|
# Tone and style
|
|
31761
31755
|
- Only use emojis if the user explicitly requests it. Avoid using emojis in all communication unless asked.
|
|
31762
|
-
-
|
|
31756
|
+
- You MUST reply in Chinese (\u7B80\u4F53\u4E2D\u6587) at all times; code, technical identifiers, and file paths are excluded from this rule.
|
|
31763
31757
|
- Your output will be displayed on a command line interface. You can use GitHub-flavored markdown for formatting, and will be rendered in a monospace font using the CommonMark specification.
|
|
31764
31758
|
- Keep your responses brief and direct. Lead with the answer or action, not the reasoning. Skip preamble and postamble (e.g., "The answer is...", "Based on the information provided..."). Only elaborate when the user asks for detail.
|
|
31765
31759
|
- Output text to communicate with the user; all text you output outside of tool use is displayed to the user. Only use tools to complete tasks. Never use tools like Bash or code comments as means to communicate with the user during the session.
|
|
@@ -31796,24 +31790,6 @@ The first item has been fixed, let me mark the first todo as completed, and move
|
|
|
31796
31790
|
</example>
|
|
31797
31791
|
In the above example, the assistant completes all the tasks, including the 10 error fixes and running the build and fixing all errors.
|
|
31798
31792
|
|
|
31799
|
-
<example>
|
|
31800
|
-
user: Help me write a new feature that allows users to track their usage metrics and export them to various formats
|
|
31801
|
-
assistant: I'll help you implement a usage metrics tracking and export feature. Let me first use the TodoWrite tool to plan this task.
|
|
31802
|
-
Adding the following todos to the todo list:
|
|
31803
|
-
1. Research existing metrics tracking in the codebase
|
|
31804
|
-
2. Design the metrics collection system
|
|
31805
|
-
3. Implement core metrics tracking functionality
|
|
31806
|
-
4. Create export functionality for different formats
|
|
31807
|
-
|
|
31808
|
-
Let me start by researching the existing codebase to understand what metrics we might already be tracking and how we can build on that.
|
|
31809
|
-
|
|
31810
|
-
I'm going to search for any existing metrics or telemetry code in the project.
|
|
31811
|
-
|
|
31812
|
-
I've found some existing telemetry code. Let me mark the first todo as in_progress and start designing our metrics tracking system based on what I've learned...
|
|
31813
|
-
|
|
31814
|
-
[Assistant continues implementing the feature step by step, marking todos as in_progress and completed as they go]
|
|
31815
|
-
</example>
|
|
31816
|
-
|
|
31817
31793
|
|
|
31818
31794
|
# Doing tasks
|
|
31819
31795
|
The user will primarily request you perform software engineering tasks. This includes solving bugs, adding new functionality, refactoring code, explaining code, and more. For these tasks the following steps are recommended:
|
|
@@ -32938,10 +32914,10 @@ function mergeDefs2(...defs) {
|
|
|
32938
32914
|
function cloneDef2(schema) {
|
|
32939
32915
|
return mergeDefs2(schema._zod.def);
|
|
32940
32916
|
}
|
|
32941
|
-
function getElementAtPath2(obj,
|
|
32942
|
-
if (!
|
|
32917
|
+
function getElementAtPath2(obj, path9) {
|
|
32918
|
+
if (!path9)
|
|
32943
32919
|
return obj;
|
|
32944
|
-
return
|
|
32920
|
+
return path9.reduce((acc, key) => acc?.[key], obj);
|
|
32945
32921
|
}
|
|
32946
32922
|
function promiseAllObject2(promisesObj) {
|
|
32947
32923
|
const keys = Object.keys(promisesObj);
|
|
@@ -33302,11 +33278,11 @@ function aborted2(x, startIndex = 0) {
|
|
|
33302
33278
|
}
|
|
33303
33279
|
return false;
|
|
33304
33280
|
}
|
|
33305
|
-
function prefixIssues2(
|
|
33281
|
+
function prefixIssues2(path9, issues) {
|
|
33306
33282
|
return issues.map((iss) => {
|
|
33307
33283
|
var _a2;
|
|
33308
33284
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
33309
|
-
iss.path.unshift(
|
|
33285
|
+
iss.path.unshift(path9);
|
|
33310
33286
|
return iss;
|
|
33311
33287
|
});
|
|
33312
33288
|
}
|
|
@@ -33474,7 +33450,7 @@ function treeifyError2(error92, _mapper) {
|
|
|
33474
33450
|
return issue3.message;
|
|
33475
33451
|
};
|
|
33476
33452
|
const result = { errors: [] };
|
|
33477
|
-
const processError = (error93,
|
|
33453
|
+
const processError = (error93, path9 = []) => {
|
|
33478
33454
|
var _a2, _b;
|
|
33479
33455
|
for (const issue3 of error93.issues) {
|
|
33480
33456
|
if (issue3.code === "invalid_union" && issue3.errors.length) {
|
|
@@ -33484,7 +33460,7 @@ function treeifyError2(error92, _mapper) {
|
|
|
33484
33460
|
} else if (issue3.code === "invalid_element") {
|
|
33485
33461
|
processError({ issues: issue3.issues }, issue3.path);
|
|
33486
33462
|
} else {
|
|
33487
|
-
const fullpath = [...
|
|
33463
|
+
const fullpath = [...path9, ...issue3.path];
|
|
33488
33464
|
if (fullpath.length === 0) {
|
|
33489
33465
|
result.errors.push(mapper(issue3));
|
|
33490
33466
|
continue;
|
|
@@ -33516,8 +33492,8 @@ function treeifyError2(error92, _mapper) {
|
|
|
33516
33492
|
}
|
|
33517
33493
|
function toDotPath2(_path) {
|
|
33518
33494
|
const segs = [];
|
|
33519
|
-
const
|
|
33520
|
-
for (const seg of
|
|
33495
|
+
const path9 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
33496
|
+
for (const seg of path9) {
|
|
33521
33497
|
if (typeof seg === "number")
|
|
33522
33498
|
segs.push(`[${seg}]`);
|
|
33523
33499
|
else if (typeof seg === "symbol")
|
|
@@ -44630,7 +44606,7 @@ function tool(input) {
|
|
|
44630
44606
|
tool.schema = external_exports2;
|
|
44631
44607
|
|
|
44632
44608
|
// src/tools/create-template/download-template.ts
|
|
44633
|
-
import { cpSync,
|
|
44609
|
+
import { cpSync, existsSync } from "fs";
|
|
44634
44610
|
import path6 from "path";
|
|
44635
44611
|
import { fileURLToPath } from "url";
|
|
44636
44612
|
|
|
@@ -44720,7 +44696,9 @@ async function downloadTemplate(mode, filePath) {
|
|
|
44720
44696
|
}
|
|
44721
44697
|
const targetDir = path6.resolve(filePath);
|
|
44722
44698
|
const templateDir = path6.join(__dirname, "templates", templateType);
|
|
44723
|
-
|
|
44699
|
+
if (!existsSync(targetDir)) {
|
|
44700
|
+
throw new Error(`Directory does not exist: ${targetDir}. Please create it first.`);
|
|
44701
|
+
}
|
|
44724
44702
|
cpSync(templateDir, targetDir, { recursive: true });
|
|
44725
44703
|
const sdkInfo = getSdkVersion();
|
|
44726
44704
|
if (sdkInfo) {
|
|
@@ -44835,15 +44813,15 @@ async function updateEtsCache(sessionManager, sessionID, directory) {
|
|
|
44835
44813
|
var CHECK_ETS_TEMPLATE_MODES = /* @__PURE__ */ new Set(["atomic", "application"]);
|
|
44836
44814
|
function createHmTemplateTool(managers) {
|
|
44837
44815
|
return tool({
|
|
44838
|
-
description: "Create an empty HarmonyOS project template. Supports three modes: 'application' (full app), 'module' (sub-module), 'atomic' (atomic service).
|
|
44816
|
+
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.",
|
|
44839
44817
|
args: {
|
|
44840
|
-
filePath: tool.schema.string("Absolute path of the target directory where the template will be created"),
|
|
44818
|
+
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."),
|
|
44841
44819
|
mode: tool.schema.string("Template type: 'application' (\u5E94\u7528), 'module' (\u6A21\u5757), or 'atomic' (\u5143\u670D\u52A1)")
|
|
44842
44820
|
},
|
|
44843
44821
|
execute: async (args, context) => {
|
|
44844
44822
|
try {
|
|
44845
44823
|
if (CHECK_ETS_TEMPLATE_MODES.has(args.mode)) {
|
|
44846
|
-
const fileCount = await checkEtsExists(
|
|
44824
|
+
const fileCount = await checkEtsExists(args.filePath);
|
|
44847
44825
|
if (fileCount > 0) {
|
|
44848
44826
|
log("[createHmTemplate] skipped, ets files exist", { fileCount });
|
|
44849
44827
|
return `Skipped: Current directory already contains ${fileCount} .ets file(s). Template download is not needed.`;
|
|
@@ -44860,7 +44838,7 @@ function createHmTemplateTool(managers) {
|
|
|
44860
44838
|
}
|
|
44861
44839
|
|
|
44862
44840
|
// src/tools/skill-search/search-skill.ts
|
|
44863
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
44841
|
+
import { readFileSync, existsSync as existsSync2 } from "node:fs";
|
|
44864
44842
|
import { join as join4 } from "node:path";
|
|
44865
44843
|
|
|
44866
44844
|
// src/tools/skill-search/tokenizer.ts
|
|
@@ -44895,7 +44873,10 @@ function tokenize(text) {
|
|
|
44895
44873
|
var BM25Retriever = class {
|
|
44896
44874
|
k1;
|
|
44897
44875
|
b;
|
|
44898
|
-
|
|
44876
|
+
_documents = [];
|
|
44877
|
+
get documents() {
|
|
44878
|
+
return this._documents;
|
|
44879
|
+
}
|
|
44899
44880
|
tokenizedDocs = [];
|
|
44900
44881
|
df = /* @__PURE__ */ new Map();
|
|
44901
44882
|
tfMaps = [];
|
|
@@ -44905,7 +44886,7 @@ var BM25Retriever = class {
|
|
|
44905
44886
|
this.b = opts?.b ?? 0.75;
|
|
44906
44887
|
}
|
|
44907
44888
|
addDocuments(docs, tokenized) {
|
|
44908
|
-
this.
|
|
44889
|
+
this._documents = [...this._documents, ...docs];
|
|
44909
44890
|
this.tokenizedDocs = [...this.tokenizedDocs, ...tokenized];
|
|
44910
44891
|
const totalLen = this.tokenizedDocs.reduce(
|
|
44911
44892
|
(sum, doc) => sum + doc.length,
|
|
@@ -44990,12 +44971,12 @@ var HybridRetriever = class {
|
|
|
44990
44971
|
this.keyword.addDocuments(docs, tokenized);
|
|
44991
44972
|
}
|
|
44992
44973
|
search(query, topK = 5) {
|
|
44993
|
-
if (this.bm25
|
|
44974
|
+
if (this.bm25.documents.length === 0) return [];
|
|
44994
44975
|
const poolSize = topK * 3;
|
|
44995
44976
|
const bm25Results = this.bm25.search(query, poolSize);
|
|
44996
44977
|
const keywordResults = this.keyword.search(query, poolSize);
|
|
44997
44978
|
const rrfK = 60;
|
|
44998
|
-
const documents = this.bm25
|
|
44979
|
+
const documents = this.bm25.documents;
|
|
44999
44980
|
const scores = /* @__PURE__ */ new Map();
|
|
45000
44981
|
for (let rank = 0; rank < bm25Results.length; rank++) {
|
|
45001
44982
|
const idx = bm25Results[rank].index;
|
|
@@ -45226,7 +45207,7 @@ async function loadAndCache(resolvedPath) {
|
|
|
45226
45207
|
log("start load sdk cache", sdk_path);
|
|
45227
45208
|
if (sdk_path !== void 0) {
|
|
45228
45209
|
const sdkIndexPath = join4(import.meta.dirname, "sdk-index.json");
|
|
45229
|
-
if (!
|
|
45210
|
+
if (!existsSync2(sdkIndexPath)) {
|
|
45230
45211
|
log("sdk path is not exist, need to build");
|
|
45231
45212
|
await buildSDKIndex(sdk_path);
|
|
45232
45213
|
}
|
|
@@ -45253,7 +45234,7 @@ async function loadAndCache(resolvedPath) {
|
|
|
45253
45234
|
function loadSdkIndex() {
|
|
45254
45235
|
if (cachedSdkRetriever) return true;
|
|
45255
45236
|
const sdkIndexPath = join4(import.meta.dirname, "sdk-index.json");
|
|
45256
|
-
if (!
|
|
45237
|
+
if (!existsSync2(sdkIndexPath)) return false;
|
|
45257
45238
|
try {
|
|
45258
45239
|
const raw = readFileSync(sdkIndexPath, "utf-8");
|
|
45259
45240
|
const entries = JSON.parse(raw);
|
|
@@ -45285,7 +45266,8 @@ async function searchSkill(skill_path, query, topK = 3) {
|
|
|
45285
45266
|
preFiltered.push({
|
|
45286
45267
|
ets_file_path: "",
|
|
45287
45268
|
experience_file_path: join4(prefix, entry.path),
|
|
45288
|
-
sdk_file_path: ""
|
|
45269
|
+
sdk_file_path: "",
|
|
45270
|
+
score: 0
|
|
45289
45271
|
});
|
|
45290
45272
|
query = (query.slice(0, idx) + query.slice(idx + category.length)).trim();
|
|
45291
45273
|
}
|
|
@@ -45297,7 +45279,8 @@ async function searchSkill(skill_path, query, topK = 3) {
|
|
|
45297
45279
|
const sdkResults = loadSdkIndex() ? cachedSdkRetriever.search(query, topK).filter((r) => r.score >= MIN_SCORE).map((r) => ({
|
|
45298
45280
|
ets_file_path: "",
|
|
45299
45281
|
experience_file_path: "",
|
|
45300
|
-
sdk_file_path: cachedSdkEntries[r.index].path
|
|
45282
|
+
sdk_file_path: cachedSdkEntries[r.index].path,
|
|
45283
|
+
score: r.score
|
|
45301
45284
|
})) : [];
|
|
45302
45285
|
const searchResults = cachedRetriever.search(query, topK);
|
|
45303
45286
|
const filtered = searchResults.filter((r) => r.score >= MIN_SCORE);
|
|
@@ -45313,6 +45296,9 @@ function aggregateByExperience(results) {
|
|
|
45313
45296
|
if (r.ets_file_path) {
|
|
45314
45297
|
existing.ets_file_path = existing.ets_file_path ? existing.ets_file_path + "\n" + r.ets_file_path : r.ets_file_path;
|
|
45315
45298
|
}
|
|
45299
|
+
if (r.score > existing.score) {
|
|
45300
|
+
existing.score = r.score;
|
|
45301
|
+
}
|
|
45316
45302
|
} else {
|
|
45317
45303
|
map3.set(key, { ...r });
|
|
45318
45304
|
}
|
|
@@ -45328,7 +45314,8 @@ function convert_search_result(searchResults, prefix) {
|
|
|
45328
45314
|
return {
|
|
45329
45315
|
ets_file_path: join4(prefix, entry.path),
|
|
45330
45316
|
experience_file_path: experience?.path ? join4(prefix, experience.path) : "",
|
|
45331
|
-
sdk_file_path: ""
|
|
45317
|
+
sdk_file_path: "",
|
|
45318
|
+
score: result.score
|
|
45332
45319
|
};
|
|
45333
45320
|
});
|
|
45334
45321
|
}
|
|
@@ -45341,22 +45328,22 @@ var HEADERS = {
|
|
|
45341
45328
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
|
|
45342
45329
|
};
|
|
45343
45330
|
var REQUEST_TIMEOUT_MS = 1e4;
|
|
45344
|
-
async function post(
|
|
45331
|
+
async function post(path9, body) {
|
|
45345
45332
|
const controller = new AbortController();
|
|
45346
45333
|
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
45347
45334
|
try {
|
|
45348
|
-
const res = await fetch(BASE_URL +
|
|
45335
|
+
const res = await fetch(BASE_URL + path9, {
|
|
45349
45336
|
method: "POST",
|
|
45350
45337
|
headers: HEADERS,
|
|
45351
45338
|
body: JSON.stringify(body),
|
|
45352
45339
|
signal: controller.signal
|
|
45353
45340
|
});
|
|
45354
45341
|
if (!res.ok) {
|
|
45355
|
-
throw new Error(`API request failed: ${res.status} ${res.statusText} for ${
|
|
45342
|
+
throw new Error(`API request failed: ${res.status} ${res.statusText} for ${path9}`);
|
|
45356
45343
|
}
|
|
45357
45344
|
const data = await res.json();
|
|
45358
45345
|
if (data.code && data.code !== 0 && data.code !== "0") {
|
|
45359
|
-
throw new Error(`API error [${data.code}]: ${data.message} for ${
|
|
45346
|
+
throw new Error(`API error [${data.code}]: ${data.message} for ${path9}`);
|
|
45360
45347
|
}
|
|
45361
45348
|
return data;
|
|
45362
45349
|
} finally {
|
|
@@ -45563,6 +45550,7 @@ ${localResults.message}`);
|
|
|
45563
45550
|
} else {
|
|
45564
45551
|
const localInfo = localResults.map((r) => {
|
|
45565
45552
|
const lines = [];
|
|
45553
|
+
lines.push(`score ${r.score.toFixed(4)}`);
|
|
45566
45554
|
if (r.experience_file_path) lines.push(`- experience path: ${r.experience_file_path}`);
|
|
45567
45555
|
if (r.ets_file_path) lines.push(`- sample code path: ${r.ets_file_path}`);
|
|
45568
45556
|
if (r.sdk_file_path) lines.push(`- sdk info path: ${r.sdk_file_path}`);
|
|
@@ -45613,6 +45601,82 @@ Use harmony-doc-view with a URL above to read any official document in full.`
|
|
|
45613
45601
|
});
|
|
45614
45602
|
}
|
|
45615
45603
|
|
|
45604
|
+
// src/tools/html-preview/html-preview-tool.ts
|
|
45605
|
+
import { access } from "fs/promises";
|
|
45606
|
+
import path7 from "path";
|
|
45607
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
45608
|
+
var PROTOTYPE_HTML_FILENAME = "prototype.html";
|
|
45609
|
+
function toFileUrl(filePath) {
|
|
45610
|
+
return pathToFileURL(path7.resolve(filePath)).href;
|
|
45611
|
+
}
|
|
45612
|
+
async function fileExists(filePath) {
|
|
45613
|
+
try {
|
|
45614
|
+
await access(filePath);
|
|
45615
|
+
return true;
|
|
45616
|
+
} catch {
|
|
45617
|
+
return false;
|
|
45618
|
+
}
|
|
45619
|
+
}
|
|
45620
|
+
function resolvePrototypePath(directory, input) {
|
|
45621
|
+
const trimmed = input.trim();
|
|
45622
|
+
if (!trimmed) {
|
|
45623
|
+
throw new Error("filePath is required");
|
|
45624
|
+
}
|
|
45625
|
+
if (trimmed.startsWith("file://")) {
|
|
45626
|
+
return fileURLToPath2(trimmed);
|
|
45627
|
+
}
|
|
45628
|
+
return path7.isAbsolute(trimmed) ? trimmed : path7.resolve(directory, trimmed);
|
|
45629
|
+
}
|
|
45630
|
+
async function resolvePrototypePreview(directory, filePathInput) {
|
|
45631
|
+
const filePath = resolvePrototypePath(directory, filePathInput);
|
|
45632
|
+
if (!await fileExists(filePath)) {
|
|
45633
|
+
throw new Error(
|
|
45634
|
+
`Prototype HTML not found: ${filePath}. Pass the exact local path where ${PROTOTYPE_HTML_FILENAME} was written.`
|
|
45635
|
+
);
|
|
45636
|
+
}
|
|
45637
|
+
return { url: toFileUrl(filePath), filePath };
|
|
45638
|
+
}
|
|
45639
|
+
function htmlPreviewTool(_managers) {
|
|
45640
|
+
return tool({
|
|
45641
|
+
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.",
|
|
45642
|
+
args: {
|
|
45643
|
+
filePath: tool.schema.string(
|
|
45644
|
+
"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."
|
|
45645
|
+
),
|
|
45646
|
+
title: tool.schema.optional(
|
|
45647
|
+
tool.schema.string("Optional title shown in the preview panel")
|
|
45648
|
+
)
|
|
45649
|
+
},
|
|
45650
|
+
execute: async (args, context) => {
|
|
45651
|
+
try {
|
|
45652
|
+
log("[html_preview]", { filePath: args.filePath, title: args.title });
|
|
45653
|
+
const { url: url3, filePath } = await resolvePrototypePreview(context.directory, args.filePath);
|
|
45654
|
+
const title = args.title?.trim() || "Prototype Preview";
|
|
45655
|
+
const metadata = {
|
|
45656
|
+
url: url3,
|
|
45657
|
+
filePath,
|
|
45658
|
+
fileName: path7.basename(filePath),
|
|
45659
|
+
title
|
|
45660
|
+
};
|
|
45661
|
+
context.metadata({
|
|
45662
|
+
title,
|
|
45663
|
+
metadata: {
|
|
45664
|
+
htmlPreview: metadata
|
|
45665
|
+
}
|
|
45666
|
+
});
|
|
45667
|
+
return [
|
|
45668
|
+
"Prototype preview sent to frontend.",
|
|
45669
|
+
`Title: ${title}`,
|
|
45670
|
+
`Local path: ${filePath}`,
|
|
45671
|
+
`Preview URL: ${url3}`
|
|
45672
|
+
].join("\n");
|
|
45673
|
+
} catch (e) {
|
|
45674
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
45675
|
+
}
|
|
45676
|
+
}
|
|
45677
|
+
});
|
|
45678
|
+
}
|
|
45679
|
+
|
|
45616
45680
|
// node_modules/turndown/lib/turndown.es.js
|
|
45617
45681
|
function extend3(destination) {
|
|
45618
45682
|
for (var i = 1; i < arguments.length; i++) {
|
|
@@ -46786,6 +46850,7 @@ function createBuiltinTools(managers) {
|
|
|
46786
46850
|
return {
|
|
46787
46851
|
createHmTemplate: createHmTemplateTool(managers),
|
|
46788
46852
|
skillSearch: skillSearchTool(managers),
|
|
46853
|
+
html_preview: htmlPreviewTool(managers),
|
|
46789
46854
|
"harmony-doc-view": harmonyDocViewTool(managers)
|
|
46790
46855
|
};
|
|
46791
46856
|
}
|
|
@@ -46910,11 +46975,11 @@ Diff.prototype = {
|
|
|
46910
46975
|
}
|
|
46911
46976
|
}
|
|
46912
46977
|
},
|
|
46913
|
-
addToPath: function addToPath(
|
|
46914
|
-
var last =
|
|
46978
|
+
addToPath: function addToPath(path9, added, removed, oldPosInc, options) {
|
|
46979
|
+
var last = path9.lastComponent;
|
|
46915
46980
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
46916
46981
|
return {
|
|
46917
|
-
oldPos:
|
|
46982
|
+
oldPos: path9.oldPos + oldPosInc,
|
|
46918
46983
|
lastComponent: {
|
|
46919
46984
|
count: last.count + 1,
|
|
46920
46985
|
added,
|
|
@@ -46924,7 +46989,7 @@ Diff.prototype = {
|
|
|
46924
46989
|
};
|
|
46925
46990
|
} else {
|
|
46926
46991
|
return {
|
|
46927
|
-
oldPos:
|
|
46992
|
+
oldPos: path9.oldPos + oldPosInc,
|
|
46928
46993
|
lastComponent: {
|
|
46929
46994
|
count: 1,
|
|
46930
46995
|
added,
|
|
@@ -47361,6 +47426,9 @@ arrayDiff.join = arrayDiff.removeEmpty = function(value) {
|
|
|
47361
47426
|
return value;
|
|
47362
47427
|
};
|
|
47363
47428
|
|
|
47429
|
+
// src/hooks/tool-hooks.ts
|
|
47430
|
+
import path8 from "path";
|
|
47431
|
+
|
|
47364
47432
|
// src/compress/compile-hvigorw.ts
|
|
47365
47433
|
function processHvigorwCompileInfo(input, output) {
|
|
47366
47434
|
if (input.tool !== "bash") return;
|
|
@@ -47402,6 +47470,7 @@ function createToolHooks(sessionManager, projectDir) {
|
|
|
47402
47470
|
"tool.execute.after": async (input, output) => {
|
|
47403
47471
|
processHvigorwCompileInfo(input, output);
|
|
47404
47472
|
injectAiCodeChange(input, output);
|
|
47473
|
+
await injectHtmlPreview(input, output, projectDir);
|
|
47405
47474
|
await updateEtsCount(sessionManager, projectDir, input);
|
|
47406
47475
|
}
|
|
47407
47476
|
};
|
|
@@ -47427,6 +47496,30 @@ async function updateEtsCount(sessionManager, projectDir, input) {
|
|
|
47427
47496
|
}
|
|
47428
47497
|
}
|
|
47429
47498
|
}
|
|
47499
|
+
async function injectHtmlPreview(input, output, projectDir) {
|
|
47500
|
+
if (input.tool !== "html_preview") return;
|
|
47501
|
+
try {
|
|
47502
|
+
const existing = output.metadata.htmlPreview;
|
|
47503
|
+
if (existing?.url) {
|
|
47504
|
+
output.title = existing.title?.trim() || "Prototype Preview";
|
|
47505
|
+
return;
|
|
47506
|
+
}
|
|
47507
|
+
const filePathArg = input.args?.filePath;
|
|
47508
|
+
if (!filePathArg) return;
|
|
47509
|
+
const title = input.args?.title?.trim() || "Prototype Preview";
|
|
47510
|
+
const { url: url3, filePath } = await resolvePrototypePreview(projectDir, filePathArg);
|
|
47511
|
+
output.title = title;
|
|
47512
|
+
output.metadata.htmlPreview = {
|
|
47513
|
+
url: url3,
|
|
47514
|
+
filePath,
|
|
47515
|
+
fileName: path8.basename(filePath),
|
|
47516
|
+
title
|
|
47517
|
+
};
|
|
47518
|
+
log("htmlPreview injected", { url: url3, title });
|
|
47519
|
+
} catch (e) {
|
|
47520
|
+
log("htmlPreview after hook error", { tool: input.tool, error: String(e) });
|
|
47521
|
+
}
|
|
47522
|
+
}
|
|
47430
47523
|
function injectAiCodeChange(input, output) {
|
|
47431
47524
|
try {
|
|
47432
47525
|
if (input.tool === "write") {
|
|
@@ -47571,7 +47664,7 @@ function createHooks(args) {
|
|
|
47571
47664
|
["tool.execute.before", () => toolHooks["tool.execute.before"]],
|
|
47572
47665
|
["tool.execute.after", () => toolHooks["tool.execute.after"]],
|
|
47573
47666
|
["command.execute.before", () => commandHooks["command.execute.before"]],
|
|
47574
|
-
["event", () => sessionHooks
|
|
47667
|
+
["event", () => sessionHooks.event],
|
|
47575
47668
|
["chat.params", () => createChatParamsHandler(config3)],
|
|
47576
47669
|
["chat.message", () => createChatMessageHandler(ctx, managers)]
|
|
47577
47670
|
];
|
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.14",
|
|
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",
|