@gobing-ai/superskill 0.1.7 → 0.1.8
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 +509 -163
- package/package.json +1 -1
- package/rubrics/command.yaml +12 -12
- package/rubrics/hook.yaml +28 -24
- package/rubrics/magent.yaml +7 -6
package/dist/index.js
CHANGED
|
@@ -9317,7 +9317,7 @@ function hashContent(filePath) {
|
|
|
9317
9317
|
var init_hash = () => {};
|
|
9318
9318
|
|
|
9319
9319
|
// ../../packages/core/src/content/identity.ts
|
|
9320
|
-
import { existsSync } from "fs";
|
|
9320
|
+
import { existsSync, statSync } from "fs";
|
|
9321
9321
|
import { basename, dirname, extname, join } from "path";
|
|
9322
9322
|
import { cwd } from "process";
|
|
9323
9323
|
function resolveContentName(path) {
|
|
@@ -9333,10 +9333,21 @@ function resolveContentName(path) {
|
|
|
9333
9333
|
}
|
|
9334
9334
|
function resolveContentPath(type, name, opts) {
|
|
9335
9335
|
if (name.includes("/") || name.includes("\\")) {
|
|
9336
|
-
if (existsSync(name))
|
|
9337
|
-
|
|
9336
|
+
if (existsSync(name)) {
|
|
9337
|
+
const st = statSync(name);
|
|
9338
|
+
if (st.isDirectory()) {
|
|
9339
|
+
const skillMd = join(name, "SKILL.md");
|
|
9340
|
+
if (existsSync(skillMd))
|
|
9341
|
+
return skillMd;
|
|
9342
|
+
} else {
|
|
9343
|
+
return name;
|
|
9344
|
+
}
|
|
9345
|
+
}
|
|
9338
9346
|
}
|
|
9339
9347
|
const base = opts?.baseDir ?? cwd();
|
|
9348
|
+
const asIs = join(base, name);
|
|
9349
|
+
if (existsSync(asIs) && statSync(asIs).isFile())
|
|
9350
|
+
return asIs;
|
|
9340
9351
|
const direct = join(base, `${name}.md`);
|
|
9341
9352
|
if (existsSync(direct))
|
|
9342
9353
|
return direct;
|
|
@@ -9710,7 +9721,7 @@ var init_adapt_subagent = __esm(() => {
|
|
|
9710
9721
|
});
|
|
9711
9722
|
|
|
9712
9723
|
// ../../packages/core/src/mapper.ts
|
|
9713
|
-
import { existsSync as existsSync3, mkdirSync, readdirSync, readFileSync as readFileSync2, statSync, writeFileSync } from "fs";
|
|
9724
|
+
import { existsSync as existsSync3, mkdirSync, readdirSync, readFileSync as readFileSync2, statSync as statSync2, writeFileSync } from "fs";
|
|
9714
9725
|
import { join as join3 } from "path";
|
|
9715
9726
|
function mapPluginToRulesync(pluginPath, pluginName, outputDir) {
|
|
9716
9727
|
assertSafePathSegment(pluginName, "plugin name");
|
|
@@ -9833,7 +9844,7 @@ function copyAndRewriteDirectory(source, destination, pluginName) {
|
|
|
9833
9844
|
for (const entry of readdirSync(source)) {
|
|
9834
9845
|
const srcPath = join3(source, entry);
|
|
9835
9846
|
const destPath = join3(destination, entry);
|
|
9836
|
-
if (
|
|
9847
|
+
if (statSync2(srcPath).isDirectory()) {
|
|
9837
9848
|
copyAndRewriteDirectory(srcPath, destPath, pluginName);
|
|
9838
9849
|
} else {
|
|
9839
9850
|
const content = readFileSync2(srcPath, "utf-8");
|
|
@@ -13814,7 +13825,7 @@ var init_zod = __esm(() => {
|
|
|
13814
13825
|
});
|
|
13815
13826
|
|
|
13816
13827
|
// ../../packages/core/src/marketplace.ts
|
|
13817
|
-
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
13828
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
|
|
13818
13829
|
import { join as join4, resolve } from "path";
|
|
13819
13830
|
function resolvePlugin(marketplacePath, pluginName) {
|
|
13820
13831
|
let manifestPath = null;
|
|
@@ -13861,8 +13872,19 @@ function resolvePlugin(marketplacePath, pluginName) {
|
|
|
13861
13872
|
const marketplaceRoot = resolve(manifestPath, "..", "..");
|
|
13862
13873
|
const pluginRootBase = manifest.metadata?.pluginRoot ?? "";
|
|
13863
13874
|
const pluginRoot = resolve(marketplaceRoot, pluginRootBase, source);
|
|
13864
|
-
|
|
13865
|
-
|
|
13875
|
+
let dirents;
|
|
13876
|
+
try {
|
|
13877
|
+
dirents = readdirSync2(pluginRoot);
|
|
13878
|
+
} catch {
|
|
13879
|
+
throw new Error(`Plugin root not found: ${pluginRoot}`);
|
|
13880
|
+
}
|
|
13881
|
+
const hasSkills = dirents.includes("skills");
|
|
13882
|
+
const hasCommands = dirents.includes("commands");
|
|
13883
|
+
const hasAgents = dirents.includes("agents");
|
|
13884
|
+
const hasHooksDir = dirents.includes("hooks");
|
|
13885
|
+
const hasHooksFile = dirents.includes("hooks.json");
|
|
13886
|
+
if (!(hasSkills || hasCommands || hasAgents || hasHooksDir || hasHooksFile)) {
|
|
13887
|
+
throw new Error(`No recognizable plugin content (skills/, commands/, agents/, hooks/, hooks.json) in: ${pluginRoot}`);
|
|
13866
13888
|
}
|
|
13867
13889
|
return { pluginRoot, marketplaceRoot, source };
|
|
13868
13890
|
}
|
|
@@ -14013,7 +14035,7 @@ var init_migrate = __esm(() => {
|
|
|
14013
14035
|
});
|
|
14014
14036
|
|
|
14015
14037
|
// ../../packages/core/src/operations/package.ts
|
|
14016
|
-
import { cpSync, existsSync as existsSync6, mkdirSync as mkdirSync3, statSync as
|
|
14038
|
+
import { cpSync, existsSync as existsSync6, mkdirSync as mkdirSync3, statSync as statSync3 } from "fs";
|
|
14017
14039
|
import { basename as basename2, dirname as dirname3, join as join5 } from "path";
|
|
14018
14040
|
import { cwd as cwd3 } from "process";
|
|
14019
14041
|
function resolveSkillDir(name) {
|
|
@@ -14021,7 +14043,7 @@ function resolveSkillDir(name) {
|
|
|
14021
14043
|
if (!skillPath) {
|
|
14022
14044
|
throw Object.assign(new Error(`Skill not found: ${name}`), { code: "ENOENT" });
|
|
14023
14045
|
}
|
|
14024
|
-
const dir =
|
|
14046
|
+
const dir = statSync3(skillPath).isDirectory() ? skillPath : dirname3(skillPath);
|
|
14025
14047
|
return { dir, name: basename2(dir) };
|
|
14026
14048
|
}
|
|
14027
14049
|
function copyDirIfExists(src, dest) {
|
|
@@ -14122,10 +14144,10 @@ var init_types2 = __esm(() => {
|
|
|
14122
14144
|
VAGUE_KEYWORDS = ["maybe", "perhaps", "might", "could be", "probably"];
|
|
14123
14145
|
REQUIRED_FIELDS = {
|
|
14124
14146
|
skill: ["name", "description"],
|
|
14125
|
-
command: ["
|
|
14147
|
+
command: ["description"],
|
|
14126
14148
|
agent: ["name", "description", "model", "tools"],
|
|
14127
|
-
hook: [
|
|
14128
|
-
magent: [
|
|
14149
|
+
hook: [],
|
|
14150
|
+
magent: []
|
|
14129
14151
|
};
|
|
14130
14152
|
});
|
|
14131
14153
|
|
|
@@ -14224,89 +14246,172 @@ var init_heuristics = __esm(() => {
|
|
|
14224
14246
|
});
|
|
14225
14247
|
|
|
14226
14248
|
// ../../packages/core/src/quality/hook.ts
|
|
14227
|
-
function
|
|
14228
|
-
|
|
14229
|
-
|
|
14230
|
-
|
|
14231
|
-
|
|
14232
|
-
|
|
14233
|
-
|
|
14234
|
-
|
|
14235
|
-
|
|
14236
|
-
|
|
14237
|
-
|
|
14238
|
-
|
|
14249
|
+
function parseHookEntries(raw) {
|
|
14250
|
+
if (typeof raw !== "object" || raw === null)
|
|
14251
|
+
return null;
|
|
14252
|
+
const data = raw;
|
|
14253
|
+
const hooks = data.hooks;
|
|
14254
|
+
if (typeof hooks !== "object" || hooks === null)
|
|
14255
|
+
return null;
|
|
14256
|
+
const entries = [];
|
|
14257
|
+
for (const [event, matcherBlocks] of Object.entries(hooks)) {
|
|
14258
|
+
if (!Array.isArray(matcherBlocks))
|
|
14259
|
+
continue;
|
|
14260
|
+
for (const block of matcherBlocks) {
|
|
14261
|
+
if (typeof block !== "object" || block === null)
|
|
14262
|
+
continue;
|
|
14263
|
+
const subHooks = block.hooks;
|
|
14264
|
+
if (!Array.isArray(subHooks))
|
|
14265
|
+
continue;
|
|
14266
|
+
for (const h of subHooks) {
|
|
14267
|
+
if (typeof h !== "object" || h === null)
|
|
14268
|
+
continue;
|
|
14269
|
+
entries.push({
|
|
14270
|
+
event,
|
|
14271
|
+
matcher: typeof block.matcher === "string" ? block.matcher : "",
|
|
14272
|
+
command: typeof h.command === "string" ? h.command : "",
|
|
14273
|
+
type: typeof h.type === "string" ? h.type : "",
|
|
14274
|
+
timeout: typeof h.timeout === "number" ? h.timeout : undefined
|
|
14275
|
+
});
|
|
14276
|
+
}
|
|
14239
14277
|
}
|
|
14240
|
-
} else {
|
|
14241
|
-
base = 0;
|
|
14242
|
-
note = "Missing event field";
|
|
14243
|
-
}
|
|
14244
|
-
if (data.enabled === true) {
|
|
14245
|
-
base = clamp(base + 0.1);
|
|
14246
14278
|
}
|
|
14247
|
-
return
|
|
14248
|
-
}
|
|
14249
|
-
function scoreEventCoverage(data, body) {
|
|
14250
|
-
const eventName = typeof data.event === "string" ? data.event.trim() : "";
|
|
14251
|
-
const mentionsEventName = eventName.length > 0 && body.toLowerCase().includes(eventName.toLowerCase()) ? 1 : 0;
|
|
14252
|
-
const density = keywordDensity(body, ["event", "intercept", "trigger", "when", "condition", "match"]);
|
|
14253
|
-
const score = 0.5 * mentionsEventName + 0.5 * density;
|
|
14254
|
-
const note = score >= 0.5 ? "Event coverage described" : "Minimal event description";
|
|
14255
|
-
return { score: clamp(score), note };
|
|
14256
|
-
}
|
|
14257
|
-
function scoreSafety(body) {
|
|
14258
|
-
const density = keywordDensity(body, [
|
|
14259
|
-
"safety",
|
|
14260
|
-
"secure",
|
|
14261
|
-
"gated",
|
|
14262
|
-
"approval",
|
|
14263
|
-
"explicit",
|
|
14264
|
-
"dangerous",
|
|
14265
|
-
"destructive",
|
|
14266
|
-
"block"
|
|
14267
|
-
]);
|
|
14268
|
-
const note = density >= 0.2 ? "Includes safety considerations" : "No safety gates described";
|
|
14269
|
-
return { score: clamp(density), note };
|
|
14279
|
+
return entries.length > 0 ? entries : null;
|
|
14270
14280
|
}
|
|
14271
|
-
function
|
|
14272
|
-
|
|
14273
|
-
|
|
14274
|
-
|
|
14275
|
-
|
|
14276
|
-
if (hasSpecificGlob) {
|
|
14277
|
-
score = 0.9;
|
|
14278
|
-
note = "Specific match patterns";
|
|
14279
|
-
} else if (patternDensity > 0) {
|
|
14280
|
-
score = 0.3;
|
|
14281
|
-
note = "Broad/unspecific patterns";
|
|
14282
|
-
} else {
|
|
14283
|
-
score = 0;
|
|
14284
|
-
note = "No match patterns";
|
|
14281
|
+
function scoreCorrectness(entries) {
|
|
14282
|
+
let valid = 0;
|
|
14283
|
+
for (const e of entries) {
|
|
14284
|
+
if (e.type === "command" && e.command.length > 0 && e.matcher.length > 0)
|
|
14285
|
+
valid++;
|
|
14285
14286
|
}
|
|
14287
|
+
const score = entries.length > 0 ? clamp(valid / entries.length) : 0;
|
|
14288
|
+
const note = `${valid}/${entries.length} entries valid`;
|
|
14289
|
+
return { score, note };
|
|
14290
|
+
}
|
|
14291
|
+
function scoreEventCoverage(entries) {
|
|
14292
|
+
const events = new Set(entries.map((e) => e.event));
|
|
14293
|
+
const covered = KNOWN_HOOK_EVENTS.filter((e) => events.has(e)).length;
|
|
14294
|
+
const score = clamp(Math.min(events.size / 3, 1));
|
|
14295
|
+
const note = `${covered} of ${KNOWN_HOOK_EVENTS.length} known events covered (${events.size} total)`;
|
|
14286
14296
|
return { score, note };
|
|
14287
14297
|
}
|
|
14298
|
+
function scoreSafety(entries) {
|
|
14299
|
+
const dangerousPatterns = [
|
|
14300
|
+
{ re: /rm\s+-rf/i, label: "rm -rf" },
|
|
14301
|
+
{ re: /\b(?:curl|wget|fetch)\b.*\|\s*(?:ba|z)?sh\b/i, label: "download pipe to shell" },
|
|
14302
|
+
{ re: /--no-verify/i, label: "--no-verify bypass" },
|
|
14303
|
+
{ re: /\beval\b/i, label: "eval" },
|
|
14304
|
+
{ re: /sudo\b/i, label: "sudo" },
|
|
14305
|
+
{ re: /chmod\s+777/i, label: "chmod 777" },
|
|
14306
|
+
{ re: /\$\([^)]*\)/, label: "unquoted command substitution" },
|
|
14307
|
+
{ re: /`[^`]+`/, label: "backtick execution" }
|
|
14308
|
+
];
|
|
14309
|
+
const findings = [];
|
|
14310
|
+
let dangerous = 0;
|
|
14311
|
+
for (const e of entries) {
|
|
14312
|
+
for (const { re, label } of dangerousPatterns) {
|
|
14313
|
+
if (re.test(e.command)) {
|
|
14314
|
+
dangerous++;
|
|
14315
|
+
findings.push(`[${e.event}] ${label}: ${e.command.slice(0, 60)}`);
|
|
14316
|
+
}
|
|
14317
|
+
}
|
|
14318
|
+
}
|
|
14319
|
+
const base = entries.length > 0 ? 1 - dangerous / (entries.length * 2) : 0;
|
|
14320
|
+
const score = clamp(Math.max(base, 0.1));
|
|
14321
|
+
const note = dangerous === 0 ? "No dangerous command patterns found" : `${dangerous} dangerous pattern(s) found`;
|
|
14322
|
+
const recs = dangerous > 0 ? ["Replace dangerous patterns with safe alternatives; add explicit approval gates"] : undefined;
|
|
14323
|
+
return { score, note, findings: findings.length > 0 ? findings : undefined, recommendations: recs };
|
|
14324
|
+
}
|
|
14325
|
+
function scorePatternMatchQuality(entries) {
|
|
14326
|
+
let specificMatchers = 0;
|
|
14327
|
+
let hasTimeout = 0;
|
|
14328
|
+
let portable = 0;
|
|
14329
|
+
for (const e of entries) {
|
|
14330
|
+
if (e.matcher !== "*" && e.matcher.length > 0)
|
|
14331
|
+
specificMatchers++;
|
|
14332
|
+
if (e.timeout !== undefined && e.timeout > 0)
|
|
14333
|
+
hasTimeout++;
|
|
14334
|
+
if (/\$\{CLAUDE_PLUGIN_ROOT\}/.test(e.command) || !/^\//.test(e.command.trim()))
|
|
14335
|
+
portable++;
|
|
14336
|
+
}
|
|
14337
|
+
const n = entries.length || 1;
|
|
14338
|
+
const matcherScore = specificMatchers / n;
|
|
14339
|
+
const timeoutScore = hasTimeout / n;
|
|
14340
|
+
const portableScore = portable / n;
|
|
14341
|
+
const score = clamp(0.3 * matcherScore + 0.4 * timeoutScore + 0.3 * portableScore);
|
|
14342
|
+
const noteParts = [];
|
|
14343
|
+
if (specificMatchers < n)
|
|
14344
|
+
noteParts.push(`${n - specificMatchers} broad matcher(s)`);
|
|
14345
|
+
if (hasTimeout < n)
|
|
14346
|
+
noteParts.push(`${n - hasTimeout} missing timeout`);
|
|
14347
|
+
if (portable < n)
|
|
14348
|
+
noteParts.push(`${n - portable} non-portable path(s)`);
|
|
14349
|
+
const findings = noteParts.length > 0 ? noteParts.map((p) => p) : undefined;
|
|
14350
|
+
const recs = noteParts.length > 0 ? [
|
|
14351
|
+
"Use specific matchers instead of *; add timeout to every hook; use CLAUDE_PLUGIN_ROOT env var for paths"
|
|
14352
|
+
] : undefined;
|
|
14353
|
+
return {
|
|
14354
|
+
score,
|
|
14355
|
+
note: noteParts.length > 0 ? noteParts.join("; ") : "All entries specific, timed, and portable",
|
|
14356
|
+
findings,
|
|
14357
|
+
recommendations: recs
|
|
14358
|
+
};
|
|
14359
|
+
}
|
|
14288
14360
|
function evaluateHook(content, target) {
|
|
14289
|
-
const
|
|
14290
|
-
|
|
14291
|
-
|
|
14292
|
-
|
|
14293
|
-
|
|
14294
|
-
|
|
14295
|
-
const errNote = parseErrorNote(content, "Frontmatter parse error");
|
|
14361
|
+
const isJson = target.endsWith(".json") || /^\s*\{/.test(content);
|
|
14362
|
+
if (isJson) {
|
|
14363
|
+
let parsed;
|
|
14364
|
+
try {
|
|
14365
|
+
parsed = JSON.parse(content);
|
|
14366
|
+
} catch {
|
|
14296
14367
|
return {
|
|
14297
|
-
|
|
14298
|
-
|
|
14299
|
-
|
|
14300
|
-
|
|
14368
|
+
type: "hook",
|
|
14369
|
+
target,
|
|
14370
|
+
content: "",
|
|
14371
|
+
aggregate: 0,
|
|
14372
|
+
dimensions: {
|
|
14373
|
+
correctness: { score: 0, note: "Invalid JSON: parse error" },
|
|
14374
|
+
"event-coverage": { score: 0, note: "Invalid JSON: parse error" },
|
|
14375
|
+
safety: { score: 0, note: "Invalid JSON: parse error" },
|
|
14376
|
+
"pattern-match-quality": { score: 0, note: "Invalid JSON: parse error" }
|
|
14377
|
+
}
|
|
14301
14378
|
};
|
|
14302
14379
|
}
|
|
14380
|
+
const entries = parseHookEntries(parsed);
|
|
14381
|
+
if (!entries || entries.length === 0) {
|
|
14382
|
+
return {
|
|
14383
|
+
type: "hook",
|
|
14384
|
+
target,
|
|
14385
|
+
content: "",
|
|
14386
|
+
aggregate: 0,
|
|
14387
|
+
dimensions: {
|
|
14388
|
+
correctness: { score: 0, note: "No hook entries found in JSON" },
|
|
14389
|
+
"event-coverage": { score: 0, note: "No hook entries found in JSON" },
|
|
14390
|
+
safety: { score: 0, note: "No hook entries found in JSON" },
|
|
14391
|
+
"pattern-match-quality": { score: 0, note: "No hook entries found in JSON" }
|
|
14392
|
+
}
|
|
14393
|
+
};
|
|
14394
|
+
}
|
|
14395
|
+
const dimensions2 = {
|
|
14396
|
+
correctness: scoreCorrectness(entries),
|
|
14397
|
+
"event-coverage": scoreEventCoverage(entries),
|
|
14398
|
+
safety: scoreSafety(entries),
|
|
14399
|
+
"pattern-match-quality": scorePatternMatchQuality(entries)
|
|
14400
|
+
};
|
|
14303
14401
|
return {
|
|
14304
|
-
|
|
14305
|
-
|
|
14306
|
-
|
|
14307
|
-
|
|
14402
|
+
type: "hook",
|
|
14403
|
+
target,
|
|
14404
|
+
content: "",
|
|
14405
|
+
aggregate: computeAggregate(dimensions2),
|
|
14406
|
+
dimensions: dimensions2
|
|
14308
14407
|
};
|
|
14309
|
-
}
|
|
14408
|
+
}
|
|
14409
|
+
const dimensions = {
|
|
14410
|
+
correctness: { score: 0, note: "Markdown hook definitions not supported; use hooks.json" },
|
|
14411
|
+
"event-coverage": { score: 0, note: "Markdown hook definitions not supported; use hooks.json" },
|
|
14412
|
+
safety: { score: 0, note: "Markdown hook definitions not supported; use hooks.json" },
|
|
14413
|
+
"pattern-match-quality": { score: 0, note: "Markdown hook definitions not supported; use hooks.json" }
|
|
14414
|
+
};
|
|
14310
14415
|
return {
|
|
14311
14416
|
type: "hook",
|
|
14312
14417
|
target,
|
|
@@ -14333,8 +14438,34 @@ var init_hook = __esm(() => {
|
|
|
14333
14438
|
});
|
|
14334
14439
|
|
|
14335
14440
|
// ../../packages/core/src/operations/validate.ts
|
|
14336
|
-
import { existsSync as existsSync8, statSync as
|
|
14337
|
-
import { dirname as dirname4 } from "path";
|
|
14441
|
+
import { existsSync as existsSync8, statSync as statSync4 } from "fs";
|
|
14442
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
14443
|
+
function checkBodyLinks(body, baseDir) {
|
|
14444
|
+
const findings = [];
|
|
14445
|
+
const linkRe = /\[([^\]]*)\]\(([^)]+)\)/g;
|
|
14446
|
+
for (const match of body.matchAll(linkRe)) {
|
|
14447
|
+
const linkText = match[1];
|
|
14448
|
+
const target = match[2];
|
|
14449
|
+
if (!target)
|
|
14450
|
+
continue;
|
|
14451
|
+
if (/^#/.test(target))
|
|
14452
|
+
continue;
|
|
14453
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(target))
|
|
14454
|
+
continue;
|
|
14455
|
+
const filePart = target.split(/[#?]/)[0];
|
|
14456
|
+
if (!filePart)
|
|
14457
|
+
continue;
|
|
14458
|
+
const resolved = join7(baseDir, filePart);
|
|
14459
|
+
if (!existsSync8(resolved)) {
|
|
14460
|
+
findings.push({
|
|
14461
|
+
severity: "warning",
|
|
14462
|
+
field: "_links",
|
|
14463
|
+
message: `Broken body link: [${linkText}](${target}) \u2192 target not found: ${resolved}`
|
|
14464
|
+
});
|
|
14465
|
+
}
|
|
14466
|
+
}
|
|
14467
|
+
return findings;
|
|
14468
|
+
}
|
|
14338
14469
|
async function validate(type, nameOrPath, opts) {
|
|
14339
14470
|
const resolvedPath = resolveContentPath(type, nameOrPath);
|
|
14340
14471
|
if (!resolvedPath) {
|
|
@@ -14345,7 +14476,7 @@ async function validate(type, nameOrPath, opts) {
|
|
|
14345
14476
|
}
|
|
14346
14477
|
let stat;
|
|
14347
14478
|
try {
|
|
14348
|
-
stat =
|
|
14479
|
+
stat = statSync4(resolvedPath);
|
|
14349
14480
|
} catch {
|
|
14350
14481
|
return sentinelResult(`File not found: ${resolvedPath}`);
|
|
14351
14482
|
}
|
|
@@ -14365,10 +14496,17 @@ async function validate(type, nameOrPath, opts) {
|
|
|
14365
14496
|
return sentinelResult(`Cannot read file: ${resolvedPath}`);
|
|
14366
14497
|
}
|
|
14367
14498
|
const baseDir = dirname4(resolvedPath);
|
|
14368
|
-
|
|
14499
|
+
const result = _validateContent(type, content, {
|
|
14369
14500
|
...opts,
|
|
14370
14501
|
referenceChecker: (refType, refName) => resolveContentPath(refType, refName, { baseDir }) !== null
|
|
14371
14502
|
});
|
|
14503
|
+
const bodyStart = content.indexOf(`
|
|
14504
|
+
---
|
|
14505
|
+
`);
|
|
14506
|
+
const body = bodyStart >= 0 ? content.slice(bodyStart + 5) : content;
|
|
14507
|
+
const linkFindings = checkBodyLinks(body, baseDir);
|
|
14508
|
+
result.findings.push(...linkFindings);
|
|
14509
|
+
return result;
|
|
14372
14510
|
}
|
|
14373
14511
|
function _validateContent(type, content, opts) {
|
|
14374
14512
|
const findings = [];
|
|
@@ -32208,7 +32346,7 @@ var init_config = __esm(() => {
|
|
|
32208
32346
|
});
|
|
32209
32347
|
|
|
32210
32348
|
// ../../node_modules/.bun/@gobing-ai+ts-runtime@0.3.21/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
|
|
32211
|
-
import { appendFileSync, cpSync as cpSync2, createWriteStream, existsSync as existsSync9, mkdirSync as mkdirSync5, readdirSync as
|
|
32349
|
+
import { appendFileSync, cpSync as cpSync2, createWriteStream, existsSync as existsSync9, mkdirSync as mkdirSync5, readdirSync as readdirSync3, readFileSync as readFileSync6, renameSync, rmSync as rmSync2, statSync as statSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
32212
32350
|
import { dirname as dirname5, resolve as resolvePath } from "path";
|
|
32213
32351
|
function createNodeFileSystem(root) {
|
|
32214
32352
|
const projectRoot = root ?? findProjectRoot(process.cwd());
|
|
@@ -32228,7 +32366,7 @@ function createNodeFileSystem(root) {
|
|
|
32228
32366
|
ensureDir: (path) => {
|
|
32229
32367
|
mkdirSync5(path, { recursive: true });
|
|
32230
32368
|
},
|
|
32231
|
-
readDir: (path) =>
|
|
32369
|
+
readDir: (path) => readdirSync3(path),
|
|
32232
32370
|
deleteFile: (path) => {
|
|
32233
32371
|
rmSync2(path, { recursive: true, force: true });
|
|
32234
32372
|
},
|
|
@@ -32244,7 +32382,7 @@ function createNodeFileSystem(root) {
|
|
|
32244
32382
|
},
|
|
32245
32383
|
stat: (path) => {
|
|
32246
32384
|
try {
|
|
32247
|
-
const s =
|
|
32385
|
+
const s = statSync5(path);
|
|
32248
32386
|
return {
|
|
32249
32387
|
isFile: () => s.isFile(),
|
|
32250
32388
|
isDirectory: () => s.isDirectory(),
|
|
@@ -34872,7 +35010,7 @@ var init_encoding_option = __esm(() => {
|
|
|
34872
35010
|
});
|
|
34873
35011
|
|
|
34874
35012
|
// ../../node_modules/.bun/execa@9.6.1/node_modules/execa/lib/arguments/cwd.js
|
|
34875
|
-
import { statSync as
|
|
35013
|
+
import { statSync as statSync6 } from "fs";
|
|
34876
35014
|
import path4 from "path";
|
|
34877
35015
|
import process6 from "process";
|
|
34878
35016
|
var normalizeCwd = (cwd5 = getDefaultCwd()) => {
|
|
@@ -34892,7 +35030,7 @@ ${error51.message}`;
|
|
|
34892
35030
|
}
|
|
34893
35031
|
let cwdStat;
|
|
34894
35032
|
try {
|
|
34895
|
-
cwdStat =
|
|
35033
|
+
cwdStat = statSync6(cwd5);
|
|
34896
35034
|
} catch (error51) {
|
|
34897
35035
|
return `The "cwd" option is invalid: ${cwd5}.
|
|
34898
35036
|
${error51.message}
|
|
@@ -40376,8 +40514,14 @@ function scoreCompleteness(data) {
|
|
|
40376
40514
|
const required2 = REQUIRED_FIELDS.agent;
|
|
40377
40515
|
const score = scorePresence(fieldsPresent, required2);
|
|
40378
40516
|
const missing = required2.filter((f) => !fieldsPresent.includes(f));
|
|
40379
|
-
const
|
|
40380
|
-
|
|
40517
|
+
const findings = missing.length > 0 ? [`Missing required fields: ${missing.join(", ")}`] : undefined;
|
|
40518
|
+
const recs = missing.length > 0 ? ["Add the missing frontmatter fields"] : undefined;
|
|
40519
|
+
return {
|
|
40520
|
+
score: clamp(score),
|
|
40521
|
+
note: missing.length > 0 ? `Missing: ${missing.join(", ")}` : "All required fields present",
|
|
40522
|
+
findings,
|
|
40523
|
+
recommendations: recs
|
|
40524
|
+
};
|
|
40381
40525
|
}
|
|
40382
40526
|
function scoreRoleClarity(body) {
|
|
40383
40527
|
const patterns = [/you are/i, /role/i, /specialist/i, /persona/i];
|
|
@@ -40404,7 +40548,9 @@ function scoreRoleClarity(body) {
|
|
|
40404
40548
|
}
|
|
40405
40549
|
const score = specificity ? clamp(base2 + 0.2) : base2;
|
|
40406
40550
|
const note = matchCount >= 2 ? "Clear role defined" : matchCount === 1 ? "Role definition vague/generic" : "No role definition found";
|
|
40407
|
-
|
|
40551
|
+
const findings = matchCount < 2 ? ["Role definition is vague, generic, or absent"] : undefined;
|
|
40552
|
+
const recs = matchCount === 0 ? ['Add a role statement (e.g. "You are a ... specialist")'] : matchCount === 1 ? ["Add more role signals (specialist, persona, role scope)"] : undefined;
|
|
40553
|
+
return { score, note, findings, recommendations: recs };
|
|
40408
40554
|
}
|
|
40409
40555
|
function scoreToolSelection(data) {
|
|
40410
40556
|
const tools = data.tools;
|
|
@@ -40417,7 +40563,9 @@ function scoreToolSelection(data) {
|
|
|
40417
40563
|
} else {
|
|
40418
40564
|
score = 0.1;
|
|
40419
40565
|
}
|
|
40420
|
-
|
|
40566
|
+
const findings = count2 < 3 ? [`Only ${count2} tool(s) selected \u2014 3+ recommended`] : undefined;
|
|
40567
|
+
const recs = count2 === 0 ? ["Add at least 3 tools to the agent frontmatter"] : count2 < 3 ? ["Add more tools to cover the agent workflow"] : undefined;
|
|
40568
|
+
return { score, note: `${count2} tools selected`, findings, recommendations: recs };
|
|
40421
40569
|
}
|
|
40422
40570
|
function scoreSkillLinkage(body) {
|
|
40423
40571
|
const patterns = [/skill:/i, /skills:/i, /skill/i];
|
|
@@ -40437,7 +40585,13 @@ function scoreSkillLinkage(body) {
|
|
|
40437
40585
|
score = 0;
|
|
40438
40586
|
note = "No skill references";
|
|
40439
40587
|
}
|
|
40440
|
-
|
|
40588
|
+
let findings;
|
|
40589
|
+
let recs;
|
|
40590
|
+
if (score < 1) {
|
|
40591
|
+
findings = ["Skill linkage is weak or missing"];
|
|
40592
|
+
recs = score === 0.5 ? ["Use structured skill references (skill: or skills:) in the agent body"] : ["Add skill references to the agent body for delegated workflows"];
|
|
40593
|
+
}
|
|
40594
|
+
return { score, note, findings, recommendations: recs };
|
|
40441
40595
|
}
|
|
40442
40596
|
function scoreModelFit(data) {
|
|
40443
40597
|
const model = data.model;
|
|
@@ -40460,7 +40614,16 @@ function scoreModelFit(data) {
|
|
|
40460
40614
|
score = 0;
|
|
40461
40615
|
note = "Model: missing";
|
|
40462
40616
|
}
|
|
40463
|
-
|
|
40617
|
+
let findings;
|
|
40618
|
+
let recs;
|
|
40619
|
+
if (score < 0.5) {
|
|
40620
|
+
findings = ["Model field is missing or unrecognized"];
|
|
40621
|
+
recs = ["Specify a valid model (e.g. inherit, claude-sonnet-4, claude-opus-4)"];
|
|
40622
|
+
} else if (score === 0.5) {
|
|
40623
|
+
findings = ["Model name appears ambiguous"];
|
|
40624
|
+
recs = ["Use a recognized model alias (inherit, sonnet, opus, haiku) or well-formed name (claude-sonnet-4)"];
|
|
40625
|
+
}
|
|
40626
|
+
return { score, note, findings, recommendations: recs };
|
|
40464
40627
|
}
|
|
40465
40628
|
function evaluateAgent(content, target) {
|
|
40466
40629
|
const data = parseFrontmatterSafe(content) ?? {};
|
|
@@ -40489,53 +40652,99 @@ var init_agent = __esm(() => {
|
|
|
40489
40652
|
function scoreCompleteness2(data) {
|
|
40490
40653
|
const fieldsPresent = Object.keys(data);
|
|
40491
40654
|
const base2 = scorePresence(fieldsPresent, REQUIRED_FIELDS.command);
|
|
40492
|
-
const
|
|
40493
|
-
const
|
|
40655
|
+
const hasArgHint = typeof data["argument-hint"] === "string" && data["argument-hint"].length > 0;
|
|
40656
|
+
const hasAllowedTools = Array.isArray(data["allowed-tools"]) && data["allowed-tools"].length > 0;
|
|
40657
|
+
const optBonus = (hasArgHint ? 0.1 : 0) + (hasAllowedTools ? 0.1 : 0);
|
|
40658
|
+
const score = clamp(Math.min(base2 + optBonus, 1));
|
|
40494
40659
|
const missing = [];
|
|
40495
40660
|
for (const f of REQUIRED_FIELDS.command) {
|
|
40496
40661
|
if (!(f in data))
|
|
40497
40662
|
missing.push(f);
|
|
40498
40663
|
}
|
|
40499
40664
|
const note = missing.length > 0 ? `Missing fields: ${missing.join(", ")}` : "All required fields present";
|
|
40500
|
-
|
|
40665
|
+
const findings = missing.length > 0 ? [`Missing required fields: ${missing.join(", ")}`] : undefined;
|
|
40666
|
+
const recs = missing.includes("description") ? ["Add a description field to the command frontmatter"] : undefined;
|
|
40667
|
+
return { score, note, findings, recommendations: recs };
|
|
40501
40668
|
}
|
|
40502
40669
|
function scoreClarity(body) {
|
|
40503
40670
|
return scoreClarityFromDensities(body);
|
|
40504
40671
|
}
|
|
40505
40672
|
function scoreArgumentHints(data) {
|
|
40506
|
-
const
|
|
40507
|
-
|
|
40508
|
-
|
|
40673
|
+
const hasKey = "argument-hint" in data;
|
|
40674
|
+
const argHint = data["argument-hint"];
|
|
40675
|
+
if (!hasKey) {
|
|
40676
|
+
return { score: 1, note: "No argument-hint (command takes no parameters)" };
|
|
40509
40677
|
}
|
|
40510
|
-
if (
|
|
40511
|
-
return {
|
|
40512
|
-
|
|
40513
|
-
|
|
40514
|
-
|
|
40515
|
-
|
|
40516
|
-
|
|
40517
|
-
|
|
40678
|
+
if (typeof argHint !== "string" || argHint.trim().length === 0) {
|
|
40679
|
+
return {
|
|
40680
|
+
score: 0.4,
|
|
40681
|
+
note: "Argument-hint declared but empty",
|
|
40682
|
+
findings: ["Argument-hint is declared but empty"],
|
|
40683
|
+
recommendations: [
|
|
40684
|
+
"Add a descriptive argument-hint string with placeholder syntax, or remove the empty field"
|
|
40685
|
+
]
|
|
40686
|
+
};
|
|
40518
40687
|
}
|
|
40519
|
-
const
|
|
40520
|
-
|
|
40521
|
-
|
|
40522
|
-
|
|
40688
|
+
const hint = argHint.trim();
|
|
40689
|
+
const hasPositional = /<[a-z][^>]*>/i.test(hint);
|
|
40690
|
+
const hasFlags = /\[?--[a-z][^\s\]]*\]?/i.test(hint);
|
|
40691
|
+
let score;
|
|
40692
|
+
let note;
|
|
40693
|
+
if (hasPositional && hasFlags) {
|
|
40694
|
+
score = 1;
|
|
40695
|
+
note = "Rich argument-hint with positional args and flags";
|
|
40696
|
+
} else if (hasPositional || hasFlags) {
|
|
40697
|
+
score = 0.75;
|
|
40698
|
+
note = "Argument-hint present but could be more descriptive";
|
|
40699
|
+
} else {
|
|
40700
|
+
score = 0.4;
|
|
40701
|
+
note = "Argument-hint is vague or placeholder-only";
|
|
40702
|
+
}
|
|
40703
|
+
let findings;
|
|
40704
|
+
let recs;
|
|
40705
|
+
if (score < 1 && score > 0.5) {
|
|
40706
|
+
findings = ["Argument-hint could be more descriptive"];
|
|
40707
|
+
recs = ["Add both positional args (<name>) and flags ([--flag <value>]) to the argument-hint"];
|
|
40708
|
+
} else if (score <= 0.5) {
|
|
40709
|
+
findings = ["Argument-hint is vague or missing for a parameterized command"];
|
|
40710
|
+
recs = ["Add a descriptive argument-hint string with placeholder syntax"];
|
|
40711
|
+
}
|
|
40712
|
+
return { score, note, findings, recommendations: recs };
|
|
40713
|
+
}
|
|
40714
|
+
function scoreToolReferences(body, data) {
|
|
40715
|
+
const allowedTools = data["allowed-tools"];
|
|
40716
|
+
const toolCount = Array.isArray(allowedTools) ? allowedTools.length : 0;
|
|
40523
40717
|
const structuredCount = (body.match(/\btools?:/gi) ?? []).length;
|
|
40524
40718
|
const backtickCount = (body.match(/`[a-z][a-z0-9_-]*`/g) ?? []).length;
|
|
40525
|
-
const
|
|
40719
|
+
const bodyWeighted = structuredCount + Math.min(backtickCount, 1);
|
|
40526
40720
|
let score;
|
|
40527
40721
|
let note;
|
|
40528
|
-
if (
|
|
40722
|
+
if (toolCount >= 5) {
|
|
40529
40723
|
score = 1;
|
|
40530
|
-
note =
|
|
40531
|
-
} else if (
|
|
40532
|
-
score = 0.
|
|
40724
|
+
note = `${toolCount} allowed-tools declared`;
|
|
40725
|
+
} else if (toolCount >= 3) {
|
|
40726
|
+
score = 0.9;
|
|
40727
|
+
note = `${toolCount} allowed-tools declared`;
|
|
40728
|
+
} else if (toolCount >= 1 || structuredCount >= 1 || bodyWeighted >= 2) {
|
|
40729
|
+
score = 0.7;
|
|
40730
|
+
note = toolCount > 0 ? `${toolCount} allowed-tools declared` : "Uses tool references";
|
|
40731
|
+
} else if (bodyWeighted === 1) {
|
|
40732
|
+
score = 0.4;
|
|
40533
40733
|
note = "Limited tool references";
|
|
40534
40734
|
} else {
|
|
40535
40735
|
score = 0.1;
|
|
40536
40736
|
note = "No tool references found";
|
|
40537
40737
|
}
|
|
40538
|
-
|
|
40738
|
+
let findings;
|
|
40739
|
+
let recs;
|
|
40740
|
+
if (score < 0.5) {
|
|
40741
|
+
findings = ["No tool references found"];
|
|
40742
|
+
recs = ["Add allowed-tools to frontmatter or reference tools in the command body"];
|
|
40743
|
+
} else if (score < 0.8) {
|
|
40744
|
+
findings = ["Limited tool references"];
|
|
40745
|
+
recs = ["Declare all required tools in the allowed-tools frontmatter array"];
|
|
40746
|
+
}
|
|
40747
|
+
return { score, note, findings, recommendations: recs };
|
|
40539
40748
|
}
|
|
40540
40749
|
function scoreSlashSyntax(body, target) {
|
|
40541
40750
|
const slashPattern = /\/[a-z][a-z-]*/g;
|
|
@@ -40562,7 +40771,7 @@ function evaluateCommand(content, target) {
|
|
|
40562
40771
|
completeness: scoreCompleteness2(data),
|
|
40563
40772
|
clarity: scoreClarity(body),
|
|
40564
40773
|
"argument-hints": scoreArgumentHints(data),
|
|
40565
|
-
"tool-references": scoreToolReferences(body),
|
|
40774
|
+
"tool-references": scoreToolReferences(body, data),
|
|
40566
40775
|
"slash-syntax": scoreSlashSyntax(body, target)
|
|
40567
40776
|
};
|
|
40568
40777
|
return {
|
|
@@ -40581,16 +40790,21 @@ var init_command3 = __esm(() => {
|
|
|
40581
40790
|
// ../../packages/core/src/quality/magent.ts
|
|
40582
40791
|
function scoreCompleteness3(body) {
|
|
40583
40792
|
let found = 0;
|
|
40584
|
-
for (const re of MAGENT_SECTIONS) {
|
|
40793
|
+
for (const { re } of MAGENT_SECTIONS) {
|
|
40585
40794
|
if (re.test(body))
|
|
40586
40795
|
found++;
|
|
40587
40796
|
}
|
|
40797
|
+
const score = clamp(found / MAGENT_SECTIONS.length);
|
|
40798
|
+
const findings = found < MAGENT_SECTIONS.length / 2 ? ["Config is missing key governance sections"] : undefined;
|
|
40799
|
+
const recs = found < MAGENT_SECTIONS.length ? ["Add more governance sections (commands, verification, conventions, safety, docs)"] : undefined;
|
|
40588
40800
|
return {
|
|
40589
|
-
score
|
|
40590
|
-
note: `${found}/${MAGENT_SECTIONS.length} sections present
|
|
40801
|
+
score,
|
|
40802
|
+
note: `${found}/${MAGENT_SECTIONS.length} governance sections present`,
|
|
40803
|
+
findings,
|
|
40804
|
+
recommendations: recs
|
|
40591
40805
|
};
|
|
40592
40806
|
}
|
|
40593
|
-
function scorePlatformCoverage(data) {
|
|
40807
|
+
function scorePlatformCoverage(data, body) {
|
|
40594
40808
|
const raw = data.platforms;
|
|
40595
40809
|
let platforms = [];
|
|
40596
40810
|
if (Array.isArray(raw)) {
|
|
@@ -40598,8 +40812,29 @@ function scorePlatformCoverage(data) {
|
|
|
40598
40812
|
} else if (typeof raw === "string") {
|
|
40599
40813
|
platforms = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
40600
40814
|
}
|
|
40815
|
+
if (platforms.length === 0) {
|
|
40816
|
+
const detected = [];
|
|
40817
|
+
const platformPatterns = [
|
|
40818
|
+
[/claude.?code/i, "claude-code"],
|
|
40819
|
+
[/codex/i, "codex"],
|
|
40820
|
+
[/gemini/i, "gemini"],
|
|
40821
|
+
[/cursor/i, "cursor"],
|
|
40822
|
+
[/windsurf/i, "windsurf"],
|
|
40823
|
+
[/opencode/i, "opencode"],
|
|
40824
|
+
[/openclaw/i, "openclaw"],
|
|
40825
|
+
[/antigravity/i, "antigravity"],
|
|
40826
|
+
[/pi\b/i, "pi"]
|
|
40827
|
+
];
|
|
40828
|
+
for (const [re, name] of platformPatterns) {
|
|
40829
|
+
if (re.test(body))
|
|
40830
|
+
detected.push(name);
|
|
40831
|
+
}
|
|
40832
|
+
platforms = detected;
|
|
40833
|
+
}
|
|
40601
40834
|
const score = clamp(Math.min(platforms.length / 5, 1));
|
|
40602
|
-
|
|
40835
|
+
const findings = platforms.length === 0 ? ["No platforms declared or detected"] : platforms.length < 3 ? ["Limited platform coverage"] : undefined;
|
|
40836
|
+
const recs = platforms.length < 3 ? ["Declare supported platforms in frontmatter (platforms:) or mention them in prose"] : undefined;
|
|
40837
|
+
return { score, note: `${platforms.length} platforms covered`, findings, recommendations: recs };
|
|
40603
40838
|
}
|
|
40604
40839
|
function scoreConciseness(body) {
|
|
40605
40840
|
return {
|
|
@@ -40627,16 +40862,19 @@ function scoreSafety2(body) {
|
|
|
40627
40862
|
if (lower.includes(kw.toLowerCase()))
|
|
40628
40863
|
count2++;
|
|
40629
40864
|
}
|
|
40630
|
-
|
|
40865
|
+
const findings = count2 < 3 ? ["Limited safety markers in config"] : undefined;
|
|
40866
|
+
const recs = count2 < 3 ? ["Add [CRITICAL] markers, safety rules, NEVER directives, and security validation"] : undefined;
|
|
40867
|
+
return { score: density, note: `${count2} safety markers found`, findings, recommendations: recs };
|
|
40631
40868
|
}
|
|
40632
40869
|
function evaluateMagent(content, target) {
|
|
40633
|
-
const fmResult = parseFrontmatterSafe(content);
|
|
40634
|
-
const data = fmResult ?? {};
|
|
40635
|
-
const fmNote = fmResult === null ? parseErrorNote(content, "unknown parse error") : null;
|
|
40636
40870
|
const body = extractBody(content);
|
|
40871
|
+
const hasFrontmatter = /^---\s*$/m.test(content);
|
|
40872
|
+
const fmResult = hasFrontmatter ? parseFrontmatterSafe(content) : undefined;
|
|
40873
|
+
const data = fmResult ?? {};
|
|
40874
|
+
const fmNote = hasFrontmatter && fmResult === null ? parseErrorNote(content, "Frontmatter parse error") : null;
|
|
40637
40875
|
const dimensions = {
|
|
40638
40876
|
completeness: scoreCompleteness3(body),
|
|
40639
|
-
"platform-coverage": scorePlatformCoverage(data),
|
|
40877
|
+
"platform-coverage": scorePlatformCoverage(data, body),
|
|
40640
40878
|
conciseness: scoreConciseness(body),
|
|
40641
40879
|
"tone-consistency": scoreToneConsistency(body),
|
|
40642
40880
|
safety: scoreSafety2(body)
|
|
@@ -40659,7 +40897,14 @@ var MAGENT_SECTIONS;
|
|
|
40659
40897
|
var init_magent = __esm(() => {
|
|
40660
40898
|
init_heuristics();
|
|
40661
40899
|
init_types2();
|
|
40662
|
-
MAGENT_SECTIONS = [
|
|
40900
|
+
MAGENT_SECTIONS = [
|
|
40901
|
+
{ re: /^## .*[Pp]roject|^## .*[Ss]tack/m, label: "project" },
|
|
40902
|
+
{ re: /^## .*[Cc]ommand|^## .*[Tt]ool/m, label: "commands" },
|
|
40903
|
+
{ re: /^## .*[Vv]erif|^## .*[Tt]est|^## .*[Gg]ate/m, label: "verification" },
|
|
40904
|
+
{ re: /^## .*[Cc]onvention|^## .*[Ss]tyle|^## .*[Bb]oundar/m, label: "conventions" },
|
|
40905
|
+
{ re: /^## .*[Ss]afety|^## .*[Ss]ecurity|^## .*[Cc]ritical/m, label: "safety" },
|
|
40906
|
+
{ re: /^## .*[Dd]oc|^## .*[Rr]eference|^## .*[Rr]outing/m, label: "docs" }
|
|
40907
|
+
];
|
|
40663
40908
|
});
|
|
40664
40909
|
|
|
40665
40910
|
// ../../packages/core/src/quality/skill.ts
|
|
@@ -40694,7 +40939,17 @@ function scoreCompleteness4(content, data, body) {
|
|
|
40694
40939
|
const keySet = new Set(presentKeys);
|
|
40695
40940
|
const missing = required2.filter((f) => !keySet.has(f));
|
|
40696
40941
|
const note = missing.length > 0 ? `Missing fields: ${missing.join(", ")}` : "All required fields present";
|
|
40697
|
-
|
|
40942
|
+
const findings = [];
|
|
40943
|
+
const recommendations = [];
|
|
40944
|
+
if (missing.length > 0) {
|
|
40945
|
+
findings.push(`Missing required frontmatter: ${missing.join(", ")}`);
|
|
40946
|
+
recommendations.push(`Add \`${missing.join("`, `")}\` to YAML frontmatter`);
|
|
40947
|
+
}
|
|
40948
|
+
if (structure < 1) {
|
|
40949
|
+
findings.push("Body lacks section headings (# / ## / ###). Structure aids navigation.");
|
|
40950
|
+
recommendations.push("Organize content with markdown headings for progressive disclosure");
|
|
40951
|
+
}
|
|
40952
|
+
return { score, note, findings, recommendations };
|
|
40698
40953
|
}
|
|
40699
40954
|
function scoreClarity2(body) {
|
|
40700
40955
|
return scoreClarityFromDensities(body);
|
|
@@ -40709,7 +40964,16 @@ function scoreTriggerAccuracy(body) {
|
|
|
40709
40964
|
} else {
|
|
40710
40965
|
score = clamp(1 - (count2 - 10) / 10);
|
|
40711
40966
|
}
|
|
40712
|
-
|
|
40967
|
+
const findings = [];
|
|
40968
|
+
const recommendations = [];
|
|
40969
|
+
if (count2 < 3) {
|
|
40970
|
+
findings.push(`Only ${count2} trigger phrase(s) found; aim for 3\u201310 for reliable skill activation.`);
|
|
40971
|
+
recommendations.push("Add 1\u20132 more When-to-Use scenarios or trigger phrase patterns to the description.");
|
|
40972
|
+
} else if (count2 > 10) {
|
|
40973
|
+
findings.push(`${count2} trigger phrases may cause overlap with adjacent skills.`);
|
|
40974
|
+
recommendations.push("Consolidate overlapping triggers or narrow the activation scope.");
|
|
40975
|
+
}
|
|
40976
|
+
return { score, note: `${count2} trigger phrases found`, findings, recommendations };
|
|
40713
40977
|
}
|
|
40714
40978
|
function scoreAntiHallucination(body) {
|
|
40715
40979
|
const density = keywordDensity(body, [
|
|
@@ -40723,10 +40987,16 @@ function scoreAntiHallucination(body) {
|
|
|
40723
40987
|
"evidence"
|
|
40724
40988
|
]);
|
|
40725
40989
|
const note = density > 0 ? "Includes verification language" : "Missing verification instructions";
|
|
40726
|
-
|
|
40990
|
+
const findings = [];
|
|
40991
|
+
const recommendations = [];
|
|
40992
|
+
if (density < 0.3) {
|
|
40993
|
+
findings.push("Verification/citation language sparse or absent. Skill may invite fabrication.");
|
|
40994
|
+
recommendations.push('Add explicit "verify with source", "cross-check against docs", or "cite the reference" instructions.');
|
|
40995
|
+
}
|
|
40996
|
+
return { score: density, note, findings, recommendations };
|
|
40727
40997
|
}
|
|
40728
40998
|
function scoreConciseness2(body) {
|
|
40729
|
-
const score = scoreLength(body, 500,
|
|
40999
|
+
const score = scoreLength(body, 500, 15000);
|
|
40730
41000
|
return { score, note: `Body length: ${body.length} chars` };
|
|
40731
41001
|
}
|
|
40732
41002
|
function countTriggerPhrases(body) {
|
|
@@ -40791,7 +41061,7 @@ var init_evaluate = __esm(() => {
|
|
|
40791
41061
|
// ../../packages/core/src/quality/rubric.ts
|
|
40792
41062
|
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
40793
41063
|
import { homedir as homedir3 } from "os";
|
|
40794
|
-
import { join as
|
|
41064
|
+
import { join as join8 } from "path";
|
|
40795
41065
|
function resolveRubricContent(type, opts) {
|
|
40796
41066
|
if (opts?.path) {
|
|
40797
41067
|
if (!existsSync10(opts.path)) {
|
|
@@ -40800,15 +41070,15 @@ function resolveRubricContent(type, opts) {
|
|
|
40800
41070
|
return readFileSync9(opts.path, "utf-8");
|
|
40801
41071
|
}
|
|
40802
41072
|
const homeDir = process.env.HOME ?? homedir3();
|
|
40803
|
-
const userPath =
|
|
41073
|
+
const userPath = join8(homeDir, ".superskill", "rubrics", `${type}.yaml`);
|
|
40804
41074
|
if (existsSync10(userPath)) {
|
|
40805
41075
|
return readFileSync9(userPath, "utf-8");
|
|
40806
41076
|
}
|
|
40807
|
-
const devPath =
|
|
41077
|
+
const devPath = join8(import.meta.dir, "..", "rubrics", `${type}.yaml`);
|
|
40808
41078
|
if (existsSync10(devPath)) {
|
|
40809
41079
|
return readFileSync9(devPath, "utf-8");
|
|
40810
41080
|
}
|
|
40811
|
-
const prodPath =
|
|
41081
|
+
const prodPath = join8(import.meta.dir, "..", "..", "rubrics", `${type}.yaml`);
|
|
40812
41082
|
if (existsSync10(prodPath)) {
|
|
40813
41083
|
return readFileSync9(prodPath, "utf-8");
|
|
40814
41084
|
}
|
|
@@ -46639,11 +46909,11 @@ var require_out = __commonJS((exports) => {
|
|
|
46639
46909
|
async.read(path7, getSettings(optionsOrSettingsOrCallback), callback);
|
|
46640
46910
|
}
|
|
46641
46911
|
exports.stat = stat;
|
|
46642
|
-
function
|
|
46912
|
+
function statSync7(path7, optionsOrSettings) {
|
|
46643
46913
|
const settings = getSettings(optionsOrSettings);
|
|
46644
46914
|
return sync.read(path7, settings);
|
|
46645
46915
|
}
|
|
46646
|
-
exports.statSync =
|
|
46916
|
+
exports.statSync = statSync7;
|
|
46647
46917
|
function getSettings(settingsOrOptions = {}) {
|
|
46648
46918
|
if (settingsOrOptions instanceof settings_1.default) {
|
|
46649
46919
|
return settingsOrOptions;
|
|
@@ -57813,7 +58083,7 @@ import { join as join52 } from "path";
|
|
|
57813
58083
|
import path10, { relative as relative2, resolve as resolve4 } from "path";
|
|
57814
58084
|
import { basename as basename4, join as join9 } from "path";
|
|
57815
58085
|
import { join as join72 } from "path";
|
|
57816
|
-
import { join as
|
|
58086
|
+
import { join as join82 } from "path";
|
|
57817
58087
|
import { join as join10 } from "path";
|
|
57818
58088
|
import { basename as basename22, join as join11 } from "path";
|
|
57819
58089
|
import { join as join13 } from "path";
|
|
@@ -57886,7 +58156,7 @@ import { join as join79 } from "path";
|
|
|
57886
58156
|
import { join as join81 } from "path";
|
|
57887
58157
|
import { join as join80 } from "path";
|
|
57888
58158
|
import { join as join84 } from "path";
|
|
57889
|
-
import { join as
|
|
58159
|
+
import { join as join822 } from "path";
|
|
57890
58160
|
import { join as join83 } from "path";
|
|
57891
58161
|
import { join as join85 } from "path";
|
|
57892
58162
|
import { join as join86 } from "path";
|
|
@@ -63432,7 +63702,7 @@ var init_chunk_QKQNZ6C3 = __esm(() => {
|
|
|
63432
63702
|
constructor({ frontmatter, body, ...rest2 }) {
|
|
63433
63703
|
const parseResult = RulesyncCommandFrontmatterSchema.safeParse(frontmatter);
|
|
63434
63704
|
if (!parseResult.success && rest2.validate) {
|
|
63435
|
-
throw new Error(`Invalid frontmatter in ${
|
|
63705
|
+
throw new Error(`Invalid frontmatter in ${join82(rest2.outputRoot ?? process.cwd(), rest2.relativeDirPath, rest2.relativeFilePath)}: ${formatError2(parseResult.error)}`);
|
|
63436
63706
|
}
|
|
63437
63707
|
const parsedFrontmatter = parseResult.success ? { ...frontmatter, ...parseResult.data } : { ...frontmatter, targets: frontmatter.targets ?? ["*"] };
|
|
63438
63708
|
super({
|
|
@@ -63473,7 +63743,7 @@ var init_chunk_QKQNZ6C3 = __esm(() => {
|
|
|
63473
63743
|
} else {
|
|
63474
63744
|
return {
|
|
63475
63745
|
success: false,
|
|
63476
|
-
error: new Error(`Invalid frontmatter in ${
|
|
63746
|
+
error: new Error(`Invalid frontmatter in ${join82(this.relativeDirPath, this.relativeFilePath)}: ${formatError2(result.error)}`)
|
|
63477
63747
|
};
|
|
63478
63748
|
}
|
|
63479
63749
|
}
|
|
@@ -63481,7 +63751,7 @@ var init_chunk_QKQNZ6C3 = __esm(() => {
|
|
|
63481
63751
|
outputRoot = process.cwd(),
|
|
63482
63752
|
relativeFilePath
|
|
63483
63753
|
}) {
|
|
63484
|
-
const filePath =
|
|
63754
|
+
const filePath = join82(outputRoot, _RulesyncCommand.getSettablePaths().relativeDirPath, relativeFilePath);
|
|
63485
63755
|
const fileContent = await readFileContent(filePath);
|
|
63486
63756
|
const { frontmatter, body: content } = parseFrontmatter2(fileContent, filePath);
|
|
63487
63757
|
const result = RulesyncCommandFrontmatterSchema.safeParse(frontmatter);
|
|
@@ -69803,9 +70073,9 @@ prompt = ""`;
|
|
|
69803
70073
|
return ignoreProcessorToolTargets;
|
|
69804
70074
|
}
|
|
69805
70075
|
};
|
|
69806
|
-
AMP_GLOBAL_DIR =
|
|
69807
|
-
AMP_SKILLS_PROJECT_DIR =
|
|
69808
|
-
AMP_SKILLS_GLOBAL_DIR =
|
|
70076
|
+
AMP_GLOBAL_DIR = join822(".config", "amp");
|
|
70077
|
+
AMP_SKILLS_PROJECT_DIR = join822(AMP_AGENTS_DIR, "skills");
|
|
70078
|
+
AMP_SKILLS_GLOBAL_DIR = join822(".config", "agents", "skills");
|
|
69809
70079
|
EnvVarNameSchema = exports_external3.string().check(refine2((value) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(value), "envVars entries must be valid environment variable names"));
|
|
69810
70080
|
McpServerSchema = exports_external3.looseObject({
|
|
69811
70081
|
type: exports_external3.optional(exports_external3.enum(["local", "stdio", "sse", "http", "ws", "streamable-http"])),
|
|
@@ -94660,11 +94930,39 @@ function deserializeEvaluation(row) {
|
|
|
94660
94930
|
}
|
|
94661
94931
|
|
|
94662
94932
|
// src/operations/evaluate.ts
|
|
94933
|
+
async function showHistory(type, contentName, opts) {
|
|
94934
|
+
const adapter = opts.adapter ?? await openStore();
|
|
94935
|
+
const dao = new EvaluationDao(adapter);
|
|
94936
|
+
const rows = await dao.getEvaluations(type, contentName);
|
|
94937
|
+
if (rows.length === 0)
|
|
94938
|
+
return;
|
|
94939
|
+
const cols = { date: 19, agg: 9, verdict: 7 };
|
|
94940
|
+
const lines = [];
|
|
94941
|
+
lines.push(`Evaluation history for ${contentName} (${rows.length} entries):`);
|
|
94942
|
+
lines.push("");
|
|
94943
|
+
const header = `${"Date".padEnd(cols.date)} ${"Aggregate".padEnd(cols.agg)} ${"Verdict".padEnd(cols.verdict)} Scorer`;
|
|
94944
|
+
lines.push(header);
|
|
94945
|
+
lines.push("-".repeat(header.length));
|
|
94946
|
+
for (const row of rows) {
|
|
94947
|
+
const date9 = new Date(row.created_at).toISOString().slice(0, 19).replace("T", " ");
|
|
94948
|
+
const agg = row.aggregate.toFixed(2).padEnd(cols.agg);
|
|
94949
|
+
const verdict = (row.aggregate >= 0.7 ? "PASS" : "FAIL").padEnd(cols.verdict);
|
|
94950
|
+
const scorer = row.scorer ?? "heuristic";
|
|
94951
|
+
const rv = row.rubric_version ? ` v${row.rubric_version}` : "";
|
|
94952
|
+
lines.push(`${date9.padEnd(cols.date)} ${agg} ${verdict} ${scorer}${rv}`);
|
|
94953
|
+
}
|
|
94954
|
+
echo(lines.join(`
|
|
94955
|
+
`));
|
|
94956
|
+
}
|
|
94663
94957
|
async function evaluate3(type, nameOrPath, opts) {
|
|
94664
94958
|
const resolvedPath = resolveContentPath(type, nameOrPath);
|
|
94665
94959
|
if (!resolvedPath) {
|
|
94666
94960
|
throw Object.assign(new Error(`File not found: ${nameOrPath}`), { code: 2 });
|
|
94667
94961
|
}
|
|
94962
|
+
if (opts?.history) {
|
|
94963
|
+
await showHistory(type, resolveContentName(resolvedPath), opts);
|
|
94964
|
+
return null;
|
|
94965
|
+
}
|
|
94668
94966
|
let content;
|
|
94669
94967
|
try {
|
|
94670
94968
|
content = await Bun.file(resolvedPath).text();
|
|
@@ -94680,6 +94978,7 @@ async function evaluate3(type, nameOrPath, opts) {
|
|
|
94680
94978
|
}
|
|
94681
94979
|
const report = evaluate(type, content, resolvedTarget);
|
|
94682
94980
|
report.content = resolveContentName(resolvedPath);
|
|
94981
|
+
applyRubricWeightingAndVerdict(type, report, opts);
|
|
94683
94982
|
if (opts?.save) {
|
|
94684
94983
|
await persistEvaluation(type, resolvedPath, resolvedTarget, report, opts);
|
|
94685
94984
|
}
|
|
@@ -94695,11 +94994,30 @@ function computeWeightedAggregate(scores, rubric2) {
|
|
|
94695
94994
|
}
|
|
94696
94995
|
return sum2;
|
|
94697
94996
|
}
|
|
94997
|
+
function applyRubricWeightingAndVerdict(type, report, opts) {
|
|
94998
|
+
try {
|
|
94999
|
+
const rubric2 = loadRubric(type, opts?.rubric ? { path: opts.rubric } : undefined);
|
|
95000
|
+
report.aggregate = computeWeightedAggregate(report.dimensions, rubric2);
|
|
95001
|
+
} catch {}
|
|
95002
|
+
const agg = report.aggregate;
|
|
95003
|
+
report.verdict = agg >= 0.7 ? "PASS" : "FAIL";
|
|
95004
|
+
if (agg >= 0.9)
|
|
95005
|
+
report.grade = "A";
|
|
95006
|
+
else if (agg >= 0.75)
|
|
95007
|
+
report.grade = "B";
|
|
95008
|
+
else if (agg >= 0.6)
|
|
95009
|
+
report.grade = "C";
|
|
95010
|
+
else if (agg >= 0.45)
|
|
95011
|
+
report.grade = "D";
|
|
95012
|
+
else
|
|
95013
|
+
report.grade = "F";
|
|
95014
|
+
}
|
|
94698
95015
|
function emitEnvelope(type, resolvedPath, content, resolvedTarget, opts) {
|
|
94699
95016
|
const rubric2 = loadRubric(type, { path: opts.rubric });
|
|
94700
95017
|
const contentName = resolveContentName(resolvedPath);
|
|
94701
95018
|
const baseline = evaluate(type, content, resolvedTarget);
|
|
94702
95019
|
baseline.content = contentName;
|
|
95020
|
+
applyRubricWeightingAndVerdict(type, baseline, opts);
|
|
94703
95021
|
const envelope = {
|
|
94704
95022
|
type,
|
|
94705
95023
|
content_name: contentName,
|
|
@@ -94811,6 +95129,33 @@ function formatEvaluationReport(report, json3) {
|
|
|
94811
95129
|
}
|
|
94812
95130
|
lines.push(` ${"\u2500".repeat(maxNameLen)}${"\u2500".repeat(30)}`);
|
|
94813
95131
|
lines.push(` ${"AGGREGATE".padEnd(maxNameLen)} ${report.aggregate.toFixed(2)}`);
|
|
95132
|
+
if (report.verdict || report.grade) {
|
|
95133
|
+
lines.push("");
|
|
95134
|
+
lines.push(` Verdict: ${report.verdict ?? ""} Grade: ${report.grade ?? ""}`);
|
|
95135
|
+
}
|
|
95136
|
+
const findings = [];
|
|
95137
|
+
const recommendations = [];
|
|
95138
|
+
for (const name of dimNames) {
|
|
95139
|
+
const dim2 = report.dimensions[name];
|
|
95140
|
+
if (!dim2)
|
|
95141
|
+
continue;
|
|
95142
|
+
if (dim2.findings)
|
|
95143
|
+
findings.push(...dim2.findings.map((f) => `[${name}] ${f}`));
|
|
95144
|
+
if (dim2.recommendations)
|
|
95145
|
+
recommendations.push(...dim2.recommendations.map((r) => `[${name}] ${r}`));
|
|
95146
|
+
}
|
|
95147
|
+
if (findings.length > 0) {
|
|
95148
|
+
lines.push("");
|
|
95149
|
+
lines.push("Findings:");
|
|
95150
|
+
for (const f of findings)
|
|
95151
|
+
lines.push(` \u2022 ${f}`);
|
|
95152
|
+
}
|
|
95153
|
+
if (recommendations.length > 0) {
|
|
95154
|
+
lines.push("");
|
|
95155
|
+
lines.push("Recommendations:");
|
|
95156
|
+
for (const r of recommendations)
|
|
95157
|
+
lines.push(` \u2192 ${r}`);
|
|
95158
|
+
}
|
|
94814
95159
|
return lines.join(`
|
|
94815
95160
|
`);
|
|
94816
95161
|
}
|
|
@@ -96004,10 +96349,10 @@ import {
|
|
|
96004
96349
|
copyFileSync as copyFileSync2,
|
|
96005
96350
|
existsSync as existsSync14,
|
|
96006
96351
|
mkdirSync as mkdirSync9,
|
|
96007
|
-
readdirSync as
|
|
96352
|
+
readdirSync as readdirSync4,
|
|
96008
96353
|
readFileSync as readFileSync13,
|
|
96009
96354
|
rmSync as rmSync4,
|
|
96010
|
-
statSync as
|
|
96355
|
+
statSync as statSync7,
|
|
96011
96356
|
writeFileSync as writeFileSync8
|
|
96012
96357
|
} from "fs";
|
|
96013
96358
|
import { homedir as homedir5 } from "os";
|
|
@@ -96267,7 +96612,7 @@ async function executeInstall(plugin, targets2, options2, dependencies = {}) {
|
|
|
96267
96612
|
if (existsSync14(agentsDir) && !options2.dryRun) {
|
|
96268
96613
|
const piAgentsDir = join223(outputRoot, ".pi", "agent", "agents");
|
|
96269
96614
|
mkdirSync9(piAgentsDir, { recursive: true });
|
|
96270
|
-
for (const entry of
|
|
96615
|
+
for (const entry of readdirSync4(agentsDir)) {
|
|
96271
96616
|
if (!entry.endsWith(".md"))
|
|
96272
96617
|
continue;
|
|
96273
96618
|
const agentName = entry.replace(/\.md$/, "");
|
|
@@ -96332,7 +96677,7 @@ function resolvePluginRoot(plugin, marketplacePath) {
|
|
|
96332
96677
|
return { pluginRoot: resolved.pluginRoot, marketplaceRoot: manifestRoot, marketplaceName };
|
|
96333
96678
|
}
|
|
96334
96679
|
const fallback = join223("plugins", plugin);
|
|
96335
|
-
if (existsSync14(
|
|
96680
|
+
if (existsSync14(fallback) && readdirSync4(fallback).some((d) => ["skills", "commands", "agents", "hooks", "hooks.json"].includes(d)))
|
|
96336
96681
|
return { pluginRoot: fallback };
|
|
96337
96682
|
const available = listResolvablePlugins(marketplacePath);
|
|
96338
96683
|
const msg = available.length > 0 ? `Available: ${available.join(", ")}` : "No marketplace manifest found and no plugins/<name>/ directory.";
|
|
@@ -96369,9 +96714,9 @@ function transformRulesyncMarkdown(root, target, pluginName) {
|
|
|
96369
96714
|
function transformMarkdownDirectory(dir, target, pluginName) {
|
|
96370
96715
|
if (!existsSync14(dir))
|
|
96371
96716
|
return;
|
|
96372
|
-
for (const entry of
|
|
96717
|
+
for (const entry of readdirSync4(dir)) {
|
|
96373
96718
|
const path11 = join223(dir, entry);
|
|
96374
|
-
const stats =
|
|
96719
|
+
const stats = statSync7(path11);
|
|
96375
96720
|
if (stats.isDirectory()) {
|
|
96376
96721
|
transformMarkdownDirectory(path11, target, pluginName);
|
|
96377
96722
|
continue;
|
|
@@ -96388,12 +96733,12 @@ function copyDirectory(source, destination, options2 = {}) {
|
|
|
96388
96733
|
if (!existsSync14(source))
|
|
96389
96734
|
return;
|
|
96390
96735
|
mkdirSync9(destination, { recursive: true });
|
|
96391
|
-
for (const entry of
|
|
96736
|
+
for (const entry of readdirSync4(source)) {
|
|
96392
96737
|
if (options2.skipDirectoryNames?.has(entry))
|
|
96393
96738
|
continue;
|
|
96394
96739
|
const sourcePath = join223(source, entry);
|
|
96395
96740
|
const destinationPath = join223(destination, entry);
|
|
96396
|
-
if (
|
|
96741
|
+
if (statSync7(sourcePath).isDirectory()) {
|
|
96397
96742
|
copyDirectory(sourcePath, destinationPath, options2);
|
|
96398
96743
|
} else {
|
|
96399
96744
|
copyFileSync2(sourcePath, destinationPath);
|
|
@@ -96695,10 +97040,11 @@ async function skillEvaluate(opts) {
|
|
|
96695
97040
|
const report = await evaluate3("skill", opts.nameOrPath, {
|
|
96696
97041
|
target,
|
|
96697
97042
|
save: opts.save,
|
|
97043
|
+
history: opts.history,
|
|
96698
97044
|
...opts.rubric ? { rubric: opts.rubric } : {},
|
|
96699
97045
|
...opts.ingest ? { ingest: opts.ingest } : {}
|
|
96700
97046
|
});
|
|
96701
|
-
if (report) {
|
|
97047
|
+
if (report && !opts.history) {
|
|
96702
97048
|
const output2 = formatEvaluationReport(report, opts.json);
|
|
96703
97049
|
echo(`${output2}`);
|
|
96704
97050
|
}
|
|
@@ -96770,7 +97116,7 @@ function registerSkill(program2) {
|
|
|
96770
97116
|
const cmd = program2.command("skill").description("Manage skill definitions");
|
|
96771
97117
|
addScaffoldOptions(cmd.command("scaffold <name>").description("Create a new skill from template")).action(handleSkillScaffold);
|
|
96772
97118
|
addStrictOption(addTargetOption(addJsonOption(cmd.command("validate <nameOrPath>").description("Validate a skill file")))).action(handleSkillValidate);
|
|
96773
|
-
addEvaluateOptions(addSaveOption(addTargetOption(addJsonOption(cmd.command("evaluate <nameOrPath>").description("Evaluate skill quality (use --rubric --json for envelope, --ingest --save to persist scores)"))))).action(handleSkillEvaluate);
|
|
97119
|
+
addEvaluateOptions(addSaveOption(addTargetOption(addJsonOption(cmd.command("evaluate <nameOrPath>").description("Evaluate skill quality (use --rubric --json for envelope, --ingest --save to persist scores)").option("--history", "Show prior evaluation rows from the store"))))).action(handleSkillEvaluate);
|
|
96774
97120
|
addSaveOption(addTargetOption(addAutoOption(cmd.command("refine <nameOrPath>").description("Evaluate and auto-fix a skill")))).action(handleSkillRefine);
|
|
96775
97121
|
addEvolveOptions(cmd.command("evolve <name>").description("Longitudinal improvement from evaluation history")).action(handleSkillEvolve);
|
|
96776
97122
|
cmd.command("package <name>").description("Package a skill for distribution").option("-o, --output <dir>", "Output directory (default: cwd)").option("--include-companions", "Include companion configs (metadata.openclaw, agents/)").action(handleSkillPackage);
|
package/package.json
CHANGED
package/rubrics/command.yaml
CHANGED
|
@@ -4,13 +4,13 @@ dimensions:
|
|
|
4
4
|
- name: completeness
|
|
5
5
|
weight: 0.25
|
|
6
6
|
criterion: >
|
|
7
|
-
Does the command
|
|
8
|
-
|
|
9
|
-
complete command
|
|
7
|
+
Does the command frontmatter include the required description field
|
|
8
|
+
and optional-but-expected fields (argument-hint string, allowed-tools array)?
|
|
9
|
+
A complete command declares its purpose and signals what tools and arguments
|
|
10
|
+
it uses. Penalize missing description; reward argument-hint and allowed-tools.
|
|
10
11
|
anchors:
|
|
11
|
-
excellent: "
|
|
12
|
-
poor: "
|
|
13
|
-
|
|
12
|
+
excellent: "Description present; argument-hint and allowed-tools declared."
|
|
13
|
+
poor: "Missing description; no argument-hint or allowed-tools signals."
|
|
14
14
|
- name: clarity
|
|
15
15
|
weight: 0.25
|
|
16
16
|
criterion: >
|
|
@@ -24,13 +24,13 @@ dimensions:
|
|
|
24
24
|
- name: argument-hints
|
|
25
25
|
weight: 0.20
|
|
26
26
|
criterion: >
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
Is the argument-hint string clear, descriptive, and consistent with
|
|
28
|
+
Claude Code conventions? A rich hint names positional args (<agent-name>)
|
|
29
|
+
and flags ([--description <text>]). Penalize placeholder-only hints or
|
|
30
|
+
missing hints on commands that take parameters.
|
|
30
31
|
anchors:
|
|
31
|
-
excellent: "
|
|
32
|
-
poor: "
|
|
33
|
-
|
|
32
|
+
excellent: "Argument-hint string names positional args and flags with descriptions."
|
|
33
|
+
poor: "Argument-hint is empty, placeholder-only, or missing on a parameterized command."
|
|
34
34
|
- name: tool-references
|
|
35
35
|
weight: 0.15
|
|
36
36
|
criterion: >
|
package/rubrics/hook.yaml
CHANGED
|
@@ -2,41 +2,45 @@ version: 1
|
|
|
2
2
|
type: hook
|
|
3
3
|
dimensions:
|
|
4
4
|
- name: correctness
|
|
5
|
-
weight: 0.
|
|
5
|
+
weight: 0.25
|
|
6
6
|
criterion: >
|
|
7
|
-
Does
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Does each hook entry have a valid command, type="command", and a non-empty
|
|
8
|
+
matcher? Penalize entries with missing or empty commands, unrecognized types,
|
|
9
|
+
or blank matchers.
|
|
10
10
|
anchors:
|
|
11
|
-
excellent: "
|
|
12
|
-
poor: "
|
|
11
|
+
excellent: "Every entry has type=command, a non-empty command, and a valid matcher."
|
|
12
|
+
poor: "Entries missing commands, have invalid types, or have blank matchers."
|
|
13
13
|
|
|
14
14
|
- name: event-coverage
|
|
15
|
-
weight: 0.
|
|
15
|
+
weight: 0.20
|
|
16
16
|
criterion: >
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
How many lifecycle events are hooked? The canonical event set includes
|
|
18
|
+
PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd,
|
|
19
|
+
UserPromptSubmit, PreCompact, and Notification. Broader coverage of
|
|
20
|
+
appropriate events is better; penalize coverage that misses safety-critical
|
|
21
|
+
events (e.g. no PreToolUse gate).
|
|
20
22
|
anchors:
|
|
21
|
-
excellent: "
|
|
22
|
-
poor: "
|
|
23
|
+
excellent: "Covers multiple lifecycle events including PreToolUse and Stop; breadth matches the hook's purpose."
|
|
24
|
+
poor: "Covers a single event or misses safety-critical lifecycle events."
|
|
23
25
|
|
|
24
26
|
- name: safety
|
|
25
|
-
weight: 0.
|
|
27
|
+
weight: 0.35
|
|
26
28
|
criterion: >
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
Do the hook commands avoid dangerous patterns? Scan for rm -rf, curl pipe
|
|
30
|
+
to shell, --no-verify bypasses, eval, sudo, chmod 777, unquoted command
|
|
31
|
+
substitution, and backtick execution. Hooks run arbitrary shell — safety
|
|
32
|
+
is the highest-value dimension.
|
|
30
33
|
anchors:
|
|
31
|
-
excellent: "No
|
|
32
|
-
poor: "
|
|
34
|
+
excellent: "No dangerous patterns; all commands use safe, portable constructs."
|
|
35
|
+
poor: "Commands contain dangerous patterns (rm -rf, curl|sh, --no-verify) with no guard."
|
|
33
36
|
|
|
34
37
|
- name: pattern-match-quality
|
|
35
|
-
weight: 0.
|
|
38
|
+
weight: 0.20
|
|
36
39
|
criterion: >
|
|
37
|
-
Are
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
Are matchers specific (not just *), do entries have timeouts, and are
|
|
41
|
+
paths portable (using ${CLAUDE_PLUGIN_ROOT} rather than absolute paths)?
|
|
42
|
+
Penalize broad * matchers on destructive events, missing timeouts, and
|
|
43
|
+
hardcoded user-specific paths.
|
|
40
44
|
anchors:
|
|
41
|
-
excellent: "
|
|
42
|
-
poor: "
|
|
45
|
+
excellent: "Specific matchers, timeouts on every entry, portable paths throughout."
|
|
46
|
+
poor: "Broad * matchers, missing timeouts, absolute paths tied to a specific user/machine."
|
package/rubrics/magent.yaml
CHANGED
|
@@ -4,13 +4,14 @@ dimensions:
|
|
|
4
4
|
- name: completeness
|
|
5
5
|
weight: 0.25
|
|
6
6
|
criterion: >
|
|
7
|
-
Does the main-agent config cover
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Does the main-agent config cover key governance areas: project/stack,
|
|
8
|
+
commands/tools, verification/testing, conventions/style, safety/security,
|
|
9
|
+
and documentation/references? Configs may be frontmatter-optional
|
|
10
|
+
(AGENTS.md/CLAUDE.md are plain markdown by design). Penalize missing
|
|
11
|
+
governance sections regardless of frontmatter presence.
|
|
10
12
|
anchors:
|
|
11
|
-
excellent: "
|
|
12
|
-
poor: "
|
|
13
|
-
|
|
13
|
+
excellent: "All governance areas covered; project, commands, verification, conventions, safety, docs."
|
|
14
|
+
poor: "Few governance sections; config is mostly unstructured or bare."
|
|
14
15
|
- name: platform-coverage
|
|
15
16
|
weight: 0.25
|
|
16
17
|
criterion: >
|