opencode-dynamic-skills 1.0.1 → 1.0.2
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 +75 -5
- package/dist/lib/SkillLinks.d.ts +7 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17839,6 +17839,18 @@ function createResourceCandidates(type, relativePath) {
|
|
|
17839
17839
|
function normalizeSkillIdentifier(identifier) {
|
|
17840
17840
|
return identifier.trim().toLowerCase().replace(/[/-]/g, "_");
|
|
17841
17841
|
}
|
|
17842
|
+
function resolveFileWithinSkillRoot(skill, relativePath) {
|
|
17843
|
+
const normalizedPath = normalizeSkillResourcePath(relativePath);
|
|
17844
|
+
const absolutePath = path.resolve(skill.fullPath, normalizedPath);
|
|
17845
|
+
const relativeFromSkillRoot = path.relative(skill.fullPath, absolutePath);
|
|
17846
|
+
if (relativeFromSkillRoot.startsWith("..") || path.isAbsolute(relativeFromSkillRoot) || !doesPathExist(absolutePath)) {
|
|
17847
|
+
return null;
|
|
17848
|
+
}
|
|
17849
|
+
return {
|
|
17850
|
+
absolutePath,
|
|
17851
|
+
mimeType: detectMimeType(absolutePath)
|
|
17852
|
+
};
|
|
17853
|
+
}
|
|
17842
17854
|
function createSkillResourceResolver(provider) {
|
|
17843
17855
|
return async (args) => {
|
|
17844
17856
|
const skillIdentifier = normalizeSkillIdentifier(args.skill_name);
|
|
@@ -17849,18 +17861,19 @@ function createSkillResourceResolver(provider) {
|
|
|
17849
17861
|
const resourceMap = getSkillResources(skill);
|
|
17850
17862
|
const candidatePaths = createResourceCandidates(args.type, args.relative_path);
|
|
17851
17863
|
const resourceEntry = candidatePaths.map((candidatePath) => resourceMap.get(candidatePath)).find((candidate) => candidate !== undefined);
|
|
17852
|
-
|
|
17864
|
+
const resolvedResource = resourceEntry ?? candidatePaths.map((candidatePath) => resolveFileWithinSkillRoot(skill, candidatePath)).find((candidate) => candidate !== null);
|
|
17865
|
+
if (!resolvedResource) {
|
|
17853
17866
|
throw new Error(`Resource not found: Skill "${args.skill_name}" does not have a resource at path "${args.relative_path}"`);
|
|
17854
17867
|
}
|
|
17855
17868
|
try {
|
|
17856
|
-
const content = await readSkillFile(
|
|
17869
|
+
const content = await readSkillFile(resolvedResource.absolutePath);
|
|
17857
17870
|
return {
|
|
17858
|
-
absolute_path:
|
|
17871
|
+
absolute_path: resolvedResource.absolutePath,
|
|
17859
17872
|
content,
|
|
17860
|
-
mimeType:
|
|
17873
|
+
mimeType: resolvedResource.mimeType
|
|
17861
17874
|
};
|
|
17862
17875
|
} catch (error45) {
|
|
17863
|
-
throw new Error(`Failed to read resource at ${
|
|
17876
|
+
throw new Error(`Failed to read resource at ${resolvedResource.absolutePath}: ${error45 instanceof Error ? error45.message : String(error45)}`);
|
|
17864
17877
|
}
|
|
17865
17878
|
};
|
|
17866
17879
|
}
|
|
@@ -23207,11 +23220,57 @@ async function rewriteSlashCommandText(args) {
|
|
|
23207
23220
|
});
|
|
23208
23221
|
}
|
|
23209
23222
|
|
|
23223
|
+
// src/lib/SkillLinks.ts
|
|
23224
|
+
import path2 from "path";
|
|
23225
|
+
var SCHEME_PATTERN = /^[a-z][a-z0-9+.-]*:/i;
|
|
23226
|
+
function normalizeLinkedSkillPath(target) {
|
|
23227
|
+
const trimmedTarget = target.trim();
|
|
23228
|
+
if (trimmedTarget.length === 0 || trimmedTarget.startsWith("#") || trimmedTarget.startsWith("/") || SCHEME_PATTERN.test(trimmedTarget)) {
|
|
23229
|
+
return null;
|
|
23230
|
+
}
|
|
23231
|
+
const [pathWithoutFragment] = trimmedTarget.split("#", 1);
|
|
23232
|
+
const [pathWithoutQuery] = pathWithoutFragment.split("?", 1);
|
|
23233
|
+
const normalizedPath = path2.posix.normalize(pathWithoutQuery.replace(/\\/g, "/")).replace(/^\.\//, "");
|
|
23234
|
+
if (normalizedPath.length === 0 || normalizedPath === "." || normalizedPath.startsWith("../") || normalizedPath.includes("/../")) {
|
|
23235
|
+
return null;
|
|
23236
|
+
}
|
|
23237
|
+
return normalizedPath;
|
|
23238
|
+
}
|
|
23239
|
+
function extractSkillLinks(content) {
|
|
23240
|
+
const links = new Map;
|
|
23241
|
+
for (const match of content.matchAll(/\[([^\]]+)\]\(([^)]+)\)/g)) {
|
|
23242
|
+
const label = match[1]?.trim();
|
|
23243
|
+
const originalPath = match[2]?.trim();
|
|
23244
|
+
if (!label || !originalPath) {
|
|
23245
|
+
continue;
|
|
23246
|
+
}
|
|
23247
|
+
const resourcePath = normalizeLinkedSkillPath(originalPath);
|
|
23248
|
+
if (!resourcePath) {
|
|
23249
|
+
continue;
|
|
23250
|
+
}
|
|
23251
|
+
links.set(`${label}:${resourcePath}`, {
|
|
23252
|
+
label,
|
|
23253
|
+
originalPath,
|
|
23254
|
+
resourcePath
|
|
23255
|
+
});
|
|
23256
|
+
}
|
|
23257
|
+
return Array.from(links.values());
|
|
23258
|
+
}
|
|
23259
|
+
|
|
23210
23260
|
// src/lib/renderers/JsonPromptRenderer.ts
|
|
23211
23261
|
var createJsonPromptRenderer = () => {
|
|
23212
23262
|
const renderer = {
|
|
23213
23263
|
format: "json",
|
|
23214
23264
|
render(args) {
|
|
23265
|
+
if (args.type === "Skill") {
|
|
23266
|
+
return JSON.stringify({
|
|
23267
|
+
Skill: {
|
|
23268
|
+
...args.data,
|
|
23269
|
+
linkedResources: extractSkillLinks(args.data.content),
|
|
23270
|
+
skillRootInstruction: "Resolve linked files relative to the skill root and use skill_resource with the exact root-relative path."
|
|
23271
|
+
}
|
|
23272
|
+
}, null, 2);
|
|
23273
|
+
}
|
|
23215
23274
|
return JSON.stringify({ [args.type]: args.data }, null, 2);
|
|
23216
23275
|
}
|
|
23217
23276
|
};
|
|
@@ -23269,6 +23328,8 @@ var createXmlPromptRenderer = () => {
|
|
|
23269
23328
|
const prepareSkill = (skill) => {
|
|
23270
23329
|
return {
|
|
23271
23330
|
...skill,
|
|
23331
|
+
linkedResources: extractSkillLinks(skill.content),
|
|
23332
|
+
skillRootInstruction: "Resolve linked files relative to the skill root and use skill_resource with the exact root-relative path.",
|
|
23272
23333
|
references: resourceMapToArray(skill.references),
|
|
23273
23334
|
scripts: resourceMapToArray(skill.scripts),
|
|
23274
23335
|
assets: resourceMapToArray(skill.assets)
|
|
@@ -23490,6 +23551,7 @@ var createMdPromptRenderer = () => {
|
|
|
23490
23551
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
23491
23552
|
};
|
|
23492
23553
|
const renderSkill = (skill) => {
|
|
23554
|
+
const linkedResources = extractSkillLinks(skill.content);
|
|
23493
23555
|
return dedent_default`
|
|
23494
23556
|
# ${skill.name}
|
|
23495
23557
|
|
|
@@ -23497,6 +23559,14 @@ var createMdPromptRenderer = () => {
|
|
|
23497
23559
|
> ${skill.fullPath}
|
|
23498
23560
|
|
|
23499
23561
|
Relative file references in this skill resolve from the skill root directory.
|
|
23562
|
+
Always use skill_resource with the exact root-relative path for linked files.
|
|
23563
|
+
|
|
23564
|
+
${linkedResources.length > 0 ? dedent_default`
|
|
23565
|
+
## Linked files detected in skill content
|
|
23566
|
+
|
|
23567
|
+
${linkedResources.map((link) => `- [${link.label}](${link.originalPath}) \u2192 use skill_resource with ${link.resourcePath}`).join(`
|
|
23568
|
+
`)}
|
|
23569
|
+
` : ""}
|
|
23500
23570
|
|
|
23501
23571
|
${skill.content}
|
|
23502
23572
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-dynamic-skills",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "OpenCode plugin for dynamic skill loading, slash skill commands, and skill-root-relative resources",
|
|
5
5
|
"homepage": "https://github.com/Wu-H-Y/opencode-dynamic-skills#readme",
|
|
6
6
|
"bugs": {
|