@gobing-ai/superskill 0.2.9 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.js +359 -44
- package/package.json +1 -1
- package/rubrics/agent.yaml +15 -4
- package/rubrics/command.yaml +11 -4
- package/rubrics/hook.yaml +5 -0
- package/rubrics/magent.yaml +11 -4
- package/rubrics/skill.yaml +39 -15
- package/templates/skill/default.md +2 -0
- package/templates/skill/pattern.md +2 -0
- package/templates/skill/reference.md +2 -0
- package/templates/skill/technique.md +2 -0
package/README.md
CHANGED
|
@@ -25,8 +25,8 @@ Requires [Bun](https://bun.sh/) ≥ 1.3.14. See [Installation guide](docs/help/i
|
|
|
25
25
|
## Quick start
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
# Distribute a plugin to every supported target
|
|
29
|
-
superskill install
|
|
28
|
+
# Distribute a plugin cc in current repo to every supported target
|
|
29
|
+
superskill install cc --targets all
|
|
30
30
|
|
|
31
31
|
# Author a skill: scaffold → validate → evaluate → refine
|
|
32
32
|
superskill skill scaffold my-skill --description "Deploy a Cloudflare Worker"
|
package/dist/index.js
CHANGED
|
@@ -14269,6 +14269,25 @@ function mergeFrontmatterList(content, key, items) {
|
|
|
14269
14269
|
}
|
|
14270
14270
|
return content.slice(0, fmStart) + newFmBody + content.slice(closePos);
|
|
14271
14271
|
}
|
|
14272
|
+
function mergeFrontmatterScalar(content, key, value) {
|
|
14273
|
+
const fenceRe = /^---$/gm;
|
|
14274
|
+
const matches = [...content.matchAll(fenceRe)];
|
|
14275
|
+
if (matches.length < 2)
|
|
14276
|
+
return content;
|
|
14277
|
+
const openPos = matches[0]?.index ?? 0;
|
|
14278
|
+
const closePos = matches[1]?.index ?? 0;
|
|
14279
|
+
const fmStart = openPos + 3;
|
|
14280
|
+
const fmBody = content.slice(fmStart, closePos);
|
|
14281
|
+
const pattern = new RegExp(`^${key}:.*$`, "m");
|
|
14282
|
+
let newFmBody;
|
|
14283
|
+
if (pattern.test(fmBody)) {
|
|
14284
|
+
newFmBody = fmBody.replace(pattern, `${key}: ${value}`);
|
|
14285
|
+
} else {
|
|
14286
|
+
newFmBody = `${fmBody}${key}: ${value}
|
|
14287
|
+
`;
|
|
14288
|
+
}
|
|
14289
|
+
return content.slice(0, fmStart) + newFmBody + content.slice(closePos);
|
|
14290
|
+
}
|
|
14272
14291
|
function resolveTemplate(type, tier) {
|
|
14273
14292
|
const homeDir = process.env.HOME ?? homedir3();
|
|
14274
14293
|
const tierName = tier?.trim();
|
|
@@ -14315,6 +14334,14 @@ async function scaffold(type, name, opts = {}) {
|
|
|
14315
14334
|
});
|
|
14316
14335
|
const toolField = type === "agent" ? "tools" : "allowed-tools";
|
|
14317
14336
|
content = mergeFrontmatterList(content, toolField, parseList(opts.tools));
|
|
14337
|
+
if (type === "skill" && opts.invocationMode === "user") {
|
|
14338
|
+
content = mergeFrontmatterScalar(content, "disable-model-invocation", "true");
|
|
14339
|
+
if (!opts.description) {
|
|
14340
|
+
content = content.replace(/^description: *$/m, "description: # One-line, human-facing: what this does, no trigger phrasing (user picks this skill directly)");
|
|
14341
|
+
}
|
|
14342
|
+
} else if (type === "skill" && !opts.description) {
|
|
14343
|
+
content = content.replace(/^description: *$/m, 'description: # Trigger-rich: front-load the identity phrase, one branch per genuine "use when" case');
|
|
14344
|
+
}
|
|
14318
14345
|
const outDir = opts.output ?? cwd4();
|
|
14319
14346
|
mkdirSync4(outDir, { recursive: true });
|
|
14320
14347
|
const filePath = type === "skill" ? join6(outDir, name, "SKILL.md") : join6(outDir, `${name}.md`);
|
|
@@ -14434,6 +14461,89 @@ function hasPattern(text, patterns) {
|
|
|
14434
14461
|
}
|
|
14435
14462
|
return clamp(matched / patterns.length);
|
|
14436
14463
|
}
|
|
14464
|
+
function scoreDescriptionBudget(description, min = 20, max = 500) {
|
|
14465
|
+
return scoreLength(description, min, max);
|
|
14466
|
+
}
|
|
14467
|
+
function noOpDensity(text) {
|
|
14468
|
+
const lower = text.toLowerCase();
|
|
14469
|
+
const noOpHits = NO_OP_PHRASES.filter((p) => lower.includes(p)).length;
|
|
14470
|
+
const imperativeHits = IMPERATIVE_KEYWORDS.filter((k) => lower.includes(k)).length;
|
|
14471
|
+
const denominator = noOpHits + imperativeHits;
|
|
14472
|
+
if (denominator === 0)
|
|
14473
|
+
return 0;
|
|
14474
|
+
return clamp(noOpHits / denominator);
|
|
14475
|
+
}
|
|
14476
|
+
function shingles(text, size) {
|
|
14477
|
+
const words = text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter(Boolean);
|
|
14478
|
+
if (words.length < size)
|
|
14479
|
+
return [];
|
|
14480
|
+
const result = [];
|
|
14481
|
+
for (let i = 0;i <= words.length - size; i++) {
|
|
14482
|
+
result.push(words.slice(i, i + size).join(" "));
|
|
14483
|
+
}
|
|
14484
|
+
return result;
|
|
14485
|
+
}
|
|
14486
|
+
function duplicationRatio(text, other, size = 8) {
|
|
14487
|
+
const own = shingles(text, size);
|
|
14488
|
+
if (own.length === 0)
|
|
14489
|
+
return 0;
|
|
14490
|
+
if (other !== undefined) {
|
|
14491
|
+
const otherSet = new Set(shingles(other, size));
|
|
14492
|
+
if (otherSet.size === 0)
|
|
14493
|
+
return 0;
|
|
14494
|
+
const dup = own.filter((s) => otherSet.has(s)).length;
|
|
14495
|
+
return clamp(dup / own.length);
|
|
14496
|
+
}
|
|
14497
|
+
const counts = new Map;
|
|
14498
|
+
for (const s of own)
|
|
14499
|
+
counts.set(s, (counts.get(s) ?? 0) + 1);
|
|
14500
|
+
const repeated = own.filter((s) => (counts.get(s) ?? 0) > 1).length;
|
|
14501
|
+
return clamp(repeated / own.length);
|
|
14502
|
+
}
|
|
14503
|
+
function countTriggerBranches(phrases) {
|
|
14504
|
+
const wordSets = phrases.map((p) => new Set(p.toLowerCase().split(/\s+/).filter(Boolean)));
|
|
14505
|
+
const branchOf = new Array(phrases.length).fill(-1);
|
|
14506
|
+
let nextBranch = 0;
|
|
14507
|
+
for (let i = 0;i < wordSets.length; i++) {
|
|
14508
|
+
if (branchOf[i] !== -1)
|
|
14509
|
+
continue;
|
|
14510
|
+
branchOf[i] = nextBranch;
|
|
14511
|
+
for (let j = i + 1;j < wordSets.length; j++) {
|
|
14512
|
+
if (branchOf[j] !== -1)
|
|
14513
|
+
continue;
|
|
14514
|
+
if (jaccard(wordSets[i] ?? new Set, wordSets[j] ?? new Set) >= 0.5) {
|
|
14515
|
+
branchOf[j] = branchOf[i] ?? nextBranch;
|
|
14516
|
+
}
|
|
14517
|
+
}
|
|
14518
|
+
nextBranch++;
|
|
14519
|
+
}
|
|
14520
|
+
return nextBranch;
|
|
14521
|
+
}
|
|
14522
|
+
function jaccard(a, b) {
|
|
14523
|
+
if (a.size === 0 && b.size === 0)
|
|
14524
|
+
return 1;
|
|
14525
|
+
let intersection = 0;
|
|
14526
|
+
for (const w of a)
|
|
14527
|
+
if (b.has(w))
|
|
14528
|
+
intersection++;
|
|
14529
|
+
const union = a.size + b.size - intersection;
|
|
14530
|
+
return union === 0 ? 0 : intersection / union;
|
|
14531
|
+
}
|
|
14532
|
+
function completionCheckability(body) {
|
|
14533
|
+
const stepLines = body.split(`
|
|
14534
|
+
`).filter((l) => /^\s*\d+[.)]\s/.test(l) || /^\s*-\s*\[[ x]\]/i.test(l));
|
|
14535
|
+
if (stepLines.length === 0)
|
|
14536
|
+
return 1;
|
|
14537
|
+
const lower = body.toLowerCase();
|
|
14538
|
+
const vagueHits = VAGUE_COMPLETION_BOUNDS.filter((v) => lower.includes(v)).length;
|
|
14539
|
+
const penalty = clamp(vagueHits / Math.max(3, VAGUE_COMPLETION_BOUNDS.length));
|
|
14540
|
+
return clamp(1 - penalty);
|
|
14541
|
+
}
|
|
14542
|
+
function progressiveDisclosureShape(body, budget = 8000) {
|
|
14543
|
+
if (body.length <= budget)
|
|
14544
|
+
return true;
|
|
14545
|
+
return /references\//i.test(body) || /##\s*(see also|additional resources)/i.test(body);
|
|
14546
|
+
}
|
|
14437
14547
|
function extractBody(content) {
|
|
14438
14548
|
if (!content.startsWith(`---
|
|
14439
14549
|
`))
|
|
@@ -14443,15 +14553,52 @@ function extractBody(content) {
|
|
|
14443
14553
|
return content.slice(4);
|
|
14444
14554
|
return content.slice(closerMatch.index + 4 + 4);
|
|
14445
14555
|
}
|
|
14556
|
+
function descriptionTriggerRichness(description) {
|
|
14557
|
+
if (!description)
|
|
14558
|
+
return 0;
|
|
14559
|
+
const branchDelimiters = (description.match(/[,;]|\bor\b/gi) ?? []).length;
|
|
14560
|
+
const dispatchCues = (description.match(/\b(use (?:this|it) when|whenever|triggers? on|use when)\b/gi) ?? []).length;
|
|
14561
|
+
const lengthSignal = clamp(description.length / 300);
|
|
14562
|
+
const branchSignal = clamp(branchDelimiters / 3);
|
|
14563
|
+
const cueSignal = clamp(dispatchCues);
|
|
14564
|
+
return clamp(branchSignal * 0.5 + cueSignal * 0.3 + lengthSignal * 0.2);
|
|
14565
|
+
}
|
|
14446
14566
|
function clamp(n) {
|
|
14447
14567
|
return Math.max(0, Math.min(1, n));
|
|
14448
14568
|
}
|
|
14449
14569
|
function escapeRegex(s) {
|
|
14450
14570
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14451
14571
|
}
|
|
14572
|
+
var NO_OP_PHRASES, VAGUE_COMPLETION_BOUNDS;
|
|
14452
14573
|
var init_heuristics = __esm(() => {
|
|
14453
14574
|
init_frontmatter();
|
|
14454
14575
|
init_types2();
|
|
14576
|
+
NO_OP_PHRASES = [
|
|
14577
|
+
"be helpful",
|
|
14578
|
+
"be concise",
|
|
14579
|
+
"be accurate",
|
|
14580
|
+
"do your best",
|
|
14581
|
+
"think carefully",
|
|
14582
|
+
"think step by step",
|
|
14583
|
+
"try your best",
|
|
14584
|
+
"work hard",
|
|
14585
|
+
"pay attention",
|
|
14586
|
+
"be thorough",
|
|
14587
|
+
"take your time",
|
|
14588
|
+
"be careful",
|
|
14589
|
+
"stay focused",
|
|
14590
|
+
"use good judgment"
|
|
14591
|
+
];
|
|
14592
|
+
VAGUE_COMPLETION_BOUNDS = [
|
|
14593
|
+
"understanding reached",
|
|
14594
|
+
"as needed",
|
|
14595
|
+
"as appropriate",
|
|
14596
|
+
"when ready",
|
|
14597
|
+
"until satisfied",
|
|
14598
|
+
"good enough",
|
|
14599
|
+
"as necessary",
|
|
14600
|
+
"when done"
|
|
14601
|
+
];
|
|
14455
14602
|
});
|
|
14456
14603
|
|
|
14457
14604
|
// ../../packages/core/src/quality/hook.ts
|
|
@@ -14776,6 +14923,9 @@ function _validateContent(type, content, opts) {
|
|
|
14776
14923
|
findings.push(...checkLinkValidity(type, data, opts?.referenceChecker));
|
|
14777
14924
|
if (opts?.strict) {
|
|
14778
14925
|
findings.push(...strictChecks(type, data, body));
|
|
14926
|
+
if (type === "skill") {
|
|
14927
|
+
findings.push(...checkInvocationModeMismatch(data));
|
|
14928
|
+
}
|
|
14779
14929
|
}
|
|
14780
14930
|
const valid = !findings.some((f) => f.severity === "error");
|
|
14781
14931
|
return { valid, findings };
|
|
@@ -14894,6 +15044,28 @@ function checkLinkValidity(type, data, referenceChecker) {
|
|
|
14894
15044
|
}
|
|
14895
15045
|
return findings;
|
|
14896
15046
|
}
|
|
15047
|
+
function checkInvocationModeMismatch(data) {
|
|
15048
|
+
const findings = [];
|
|
15049
|
+
const description = typeof data.description === "string" ? data.description : "";
|
|
15050
|
+
if (!description)
|
|
15051
|
+
return findings;
|
|
15052
|
+
const userInvoked = data["disable-model-invocation"] === true;
|
|
15053
|
+
const richness = descriptionTriggerRichness(description);
|
|
15054
|
+
if (userInvoked && richness >= TRIGGER_RICH_THRESHOLD) {
|
|
15055
|
+
findings.push({
|
|
15056
|
+
severity: "warning",
|
|
15057
|
+
field: "invocation-mode",
|
|
15058
|
+
message: "disable-model-invocation is true (user-invoked) but the description reads " + "trigger-rich (branch/dispatch phrasing). A user-invoked skill cannot be fired " + "by other skills or commands \u2014 rewrite the description as a one-line, " + "human-facing summary instead."
|
|
15059
|
+
});
|
|
15060
|
+
} else if (!userInvoked && richness <= ONE_LINE_THRESHOLD) {
|
|
15061
|
+
findings.push({
|
|
15062
|
+
severity: "warning",
|
|
15063
|
+
field: "invocation-mode",
|
|
15064
|
+
message: "This skill is model-invoked (disable-model-invocation is absent/false) but the " + "description reads like a bare one-liner with no trigger phrasing. The dispatching " + 'orchestrator needs distinct "use when" branches to select this skill reliably.'
|
|
15065
|
+
});
|
|
15066
|
+
}
|
|
15067
|
+
return findings;
|
|
15068
|
+
}
|
|
14897
15069
|
function strictChecks(type, data, body) {
|
|
14898
15070
|
const findings = [];
|
|
14899
15071
|
const desc = data.description;
|
|
@@ -14959,10 +15131,11 @@ function sentinelResult(message) {
|
|
|
14959
15131
|
findings: [{ severity: "error", field: "_file", message }]
|
|
14960
15132
|
};
|
|
14961
15133
|
}
|
|
14962
|
-
var MODEL_ALIASES, FIELD_TYPES, DEPRECATED_FIELDS, KNOWN_OPTIONAL;
|
|
15134
|
+
var MODEL_ALIASES, FIELD_TYPES, DEPRECATED_FIELDS, KNOWN_OPTIONAL, TRIGGER_RICH_THRESHOLD = 0.4, ONE_LINE_THRESHOLD = 0.15;
|
|
14963
15135
|
var init_validate = __esm(() => {
|
|
14964
15136
|
init_frontmatter();
|
|
14965
15137
|
init_identity();
|
|
15138
|
+
init_heuristics();
|
|
14966
15139
|
init_hook();
|
|
14967
15140
|
init_types2();
|
|
14968
15141
|
MODEL_ALIASES = ["inherit", "sonnet", "opus", "haiku"];
|
|
@@ -14970,7 +15143,8 @@ var init_validate = __esm(() => {
|
|
|
14970
15143
|
skill: {
|
|
14971
15144
|
name: { type: "string" },
|
|
14972
15145
|
description: { type: "string" },
|
|
14973
|
-
"allowed-tools": { type: "array" }
|
|
15146
|
+
"allowed-tools": { type: "array" },
|
|
15147
|
+
"disable-model-invocation": { type: "boolean" }
|
|
14974
15148
|
},
|
|
14975
15149
|
command: {
|
|
14976
15150
|
name: { type: "string" },
|
|
@@ -40886,18 +41060,39 @@ var init_slash_command2 = __esm(() => {
|
|
|
40886
41060
|
});
|
|
40887
41061
|
|
|
40888
41062
|
// ../../packages/core/src/quality/agent.ts
|
|
40889
|
-
function scoreCompleteness(data) {
|
|
41063
|
+
function scoreCompleteness(data, body) {
|
|
40890
41064
|
const fieldsPresent = Object.keys(data);
|
|
40891
41065
|
const required2 = REQUIRED_FIELDS.agent;
|
|
40892
|
-
const
|
|
41066
|
+
const presence = scorePresence(fieldsPresent, required2);
|
|
40893
41067
|
const missing = required2.filter((f) => !fieldsPresent.includes(f));
|
|
40894
|
-
const
|
|
40895
|
-
const
|
|
41068
|
+
const description = typeof data.description === "string" ? data.description : "";
|
|
41069
|
+
const budgetScore = description ? scoreDescriptionBudget(description, 20, 2000) : 1;
|
|
41070
|
+
const noOp = noOpDensity(description);
|
|
41071
|
+
const descDup = description ? duplicationRatio(description, body, 12) : 0;
|
|
41072
|
+
const score = clamp(presence * budgetScore * (1 - noOp) * (1 - descDup));
|
|
41073
|
+
const findings = [];
|
|
41074
|
+
const recs = [];
|
|
41075
|
+
if (missing.length > 0) {
|
|
41076
|
+
findings.push(`Missing required fields: ${missing.join(", ")}`);
|
|
41077
|
+
recs.push("Add the missing frontmatter fields");
|
|
41078
|
+
}
|
|
41079
|
+
if (budgetScore < 1) {
|
|
41080
|
+
findings.push(`Description is ${description.length} chars, outside the 20\u20132000 char budget.`);
|
|
41081
|
+
recs.push("Tighten the description to the agent\u2019s role and dispatch trigger.");
|
|
41082
|
+
}
|
|
41083
|
+
if (noOp > 0.2) {
|
|
41084
|
+
findings.push("Description contains default-behavior phrases that add no signal (no-op candidates).");
|
|
41085
|
+
recs.push("Delete no-op phrasing from the description rather than trimming it.");
|
|
41086
|
+
}
|
|
41087
|
+
if (descDup > 0.3) {
|
|
41088
|
+
findings.push("Description restates body text near-verbatim (duplication).");
|
|
41089
|
+
recs.push("State the agent\u2019s role once in the description; do not repeat it in the body.");
|
|
41090
|
+
}
|
|
40896
41091
|
return {
|
|
40897
|
-
score
|
|
41092
|
+
score,
|
|
40898
41093
|
note: missing.length > 0 ? `Missing: ${missing.join(", ")}` : "All required fields present",
|
|
40899
|
-
findings,
|
|
40900
|
-
recommendations: recs
|
|
41094
|
+
findings: findings.length > 0 ? findings : undefined,
|
|
41095
|
+
recommendations: recs.length > 0 ? recs : undefined
|
|
40901
41096
|
};
|
|
40902
41097
|
}
|
|
40903
41098
|
function scoreRoleClarity(body) {
|
|
@@ -41006,7 +41201,7 @@ function evaluateAgent(content, target) {
|
|
|
41006
41201
|
const data = parseFrontmatterSafe(content) ?? {};
|
|
41007
41202
|
const body = extractBody(content);
|
|
41008
41203
|
const dimensions = {
|
|
41009
|
-
completeness: scoreCompleteness(data),
|
|
41204
|
+
completeness: scoreCompleteness(data, body),
|
|
41010
41205
|
"role-clarity": scoreRoleClarity(body),
|
|
41011
41206
|
"tool-selection": scoreToolSelection(data),
|
|
41012
41207
|
"skill-linkage": scoreSkillLinkage(body),
|
|
@@ -41043,8 +41238,27 @@ function scoreCompleteness2(data) {
|
|
|
41043
41238
|
const recs = missing.includes("description") ? ["Add a description field to the command frontmatter"] : undefined;
|
|
41044
41239
|
return { score, note, findings, recommendations: recs };
|
|
41045
41240
|
}
|
|
41046
|
-
function scoreClarity(body) {
|
|
41047
|
-
|
|
41241
|
+
function scoreClarity(body, description) {
|
|
41242
|
+
const base2 = scoreClarityFromDensities(body);
|
|
41243
|
+
const budgetScore = description ? scoreDescriptionBudget(description) : 1;
|
|
41244
|
+
const noOp = noOpDensity(description);
|
|
41245
|
+
const descDup = description ? duplicationRatio(description, body, 12) : 0;
|
|
41246
|
+
const score = clamp(base2.score * budgetScore * (1 - noOp) * (1 - descDup));
|
|
41247
|
+
const findings = [...base2.findings ?? []];
|
|
41248
|
+
const recommendations = [...base2.recommendations ?? []];
|
|
41249
|
+
if (budgetScore < 1) {
|
|
41250
|
+
findings.push(`Description is ${description.length} chars, outside the 20\u2013500 char budget.`);
|
|
41251
|
+
recommendations.push("Tighten the description to what the command does and when to use it.");
|
|
41252
|
+
}
|
|
41253
|
+
if (noOp > 0.2) {
|
|
41254
|
+
findings.push("Description contains default-behavior phrases that add no signal (no-op candidates).");
|
|
41255
|
+
recommendations.push("Delete no-op phrasing from the description rather than trimming it.");
|
|
41256
|
+
}
|
|
41257
|
+
if (descDup > 0.3) {
|
|
41258
|
+
findings.push("Description restates body text near-verbatim (duplication).");
|
|
41259
|
+
recommendations.push("State the command purpose once in the description; do not repeat it in the body.");
|
|
41260
|
+
}
|
|
41261
|
+
return { score, note: base2.note, findings, recommendations };
|
|
41048
41262
|
}
|
|
41049
41263
|
function scoreArgumentHints(data) {
|
|
41050
41264
|
const hasKey = "argument-hint" in data;
|
|
@@ -41146,7 +41360,7 @@ function evaluateCommand(content, target) {
|
|
|
41146
41360
|
const body = extractBody(content);
|
|
41147
41361
|
const dimensions = {
|
|
41148
41362
|
completeness: scoreCompleteness2(data),
|
|
41149
|
-
clarity: scoreClarity(body),
|
|
41363
|
+
clarity: scoreClarity(body, typeof data.description === "string" ? data.description : ""),
|
|
41150
41364
|
"argument-hints": scoreArgumentHints(data),
|
|
41151
41365
|
"tool-references": scoreToolReferences(body, data),
|
|
41152
41366
|
"slash-syntax": scoreSlashSyntax(body, target)
|
|
@@ -41335,9 +41549,25 @@ function scorePlatformCoverage(data, body) {
|
|
|
41335
41549
|
return { score, note: `${platforms.length} platforms covered`, findings, recommendations: recs };
|
|
41336
41550
|
}
|
|
41337
41551
|
function scoreConciseness(body) {
|
|
41552
|
+
const lengthScore = scoreLength(body, 1000, 8000);
|
|
41553
|
+
const noOp = noOpDensity(body);
|
|
41554
|
+
const bodyDup = duplicationRatio(body, undefined, 12);
|
|
41555
|
+
const score = clamp(lengthScore * (1 - noOp) * (1 - bodyDup * 0.3));
|
|
41556
|
+
const findings = [];
|
|
41557
|
+
const recs = [];
|
|
41558
|
+
if (noOp > 0.2) {
|
|
41559
|
+
findings.push("Body contains default-behavior phrases that do not change model behavior (no-op candidates).");
|
|
41560
|
+
recs.push("Delete no-op instructions rather than trimming them \u2014 they add no signal.");
|
|
41561
|
+
}
|
|
41562
|
+
if (bodyDup > 0.15) {
|
|
41563
|
+
findings.push("Body repeats the same phrasing (n-gram duplication) in multiple places.");
|
|
41564
|
+
recs.push("Collapse duplicated phrasing into one authoritative section; cite it elsewhere.");
|
|
41565
|
+
}
|
|
41338
41566
|
return {
|
|
41339
|
-
score
|
|
41340
|
-
note: `Body length: ${body.length} chars
|
|
41567
|
+
score,
|
|
41568
|
+
note: `Body length: ${body.length} chars`,
|
|
41569
|
+
findings: findings.length > 0 ? findings : undefined,
|
|
41570
|
+
recommendations: recs.length > 0 ? recs : undefined
|
|
41341
41571
|
};
|
|
41342
41572
|
}
|
|
41343
41573
|
function scoreToneConsistency(body) {
|
|
@@ -41411,12 +41641,13 @@ var init_magent = __esm(() => {
|
|
|
41411
41641
|
function evaluateSkill(content, target) {
|
|
41412
41642
|
const data = parseFrontmatterSafe(content);
|
|
41413
41643
|
const body = extractBody(content);
|
|
41644
|
+
const description = typeof data?.description === "string" ? data.description : "";
|
|
41414
41645
|
const dimensions = {
|
|
41415
41646
|
completeness: scoreCompleteness4(content, data, body),
|
|
41416
41647
|
clarity: scoreClarity2(body),
|
|
41417
|
-
"trigger-accuracy": scoreTriggerAccuracy(body),
|
|
41648
|
+
"trigger-accuracy": scoreTriggerAccuracy(body, description, data),
|
|
41418
41649
|
"anti-hallucination": scoreAntiHallucination(body),
|
|
41419
|
-
conciseness: scoreConciseness2(body)
|
|
41650
|
+
conciseness: scoreConciseness2(body, description)
|
|
41420
41651
|
};
|
|
41421
41652
|
return {
|
|
41422
41653
|
type: "skill",
|
|
@@ -41435,7 +41666,8 @@ function scoreCompleteness4(content, data, body) {
|
|
|
41435
41666
|
const required2 = REQUIRED_FIELDS.skill;
|
|
41436
41667
|
const presence = scorePresence(presentKeys, required2);
|
|
41437
41668
|
const structure = hasPattern(body, [/^# /m, /^## /m, /^### /m]);
|
|
41438
|
-
const
|
|
41669
|
+
const disclosed = progressiveDisclosureShape(body) ? 1 : 0.7;
|
|
41670
|
+
const score = clamp(presence * structure * disclosed);
|
|
41439
41671
|
const keySet = new Set(presentKeys);
|
|
41440
41672
|
const missing = required2.filter((f) => !keySet.has(f));
|
|
41441
41673
|
const note = missing.length > 0 ? `Missing fields: ${missing.join(", ")}` : "All required fields present";
|
|
@@ -41449,13 +41681,31 @@ function scoreCompleteness4(content, data, body) {
|
|
|
41449
41681
|
findings.push("Body lacks section headings (# / ## / ###). Structure aids navigation.");
|
|
41450
41682
|
recommendations.push("Organize content with markdown headings for progressive disclosure");
|
|
41451
41683
|
}
|
|
41684
|
+
if (disclosed < 1) {
|
|
41685
|
+
findings.push("Body is over the disclosure budget with no references/ (or See Also) link.");
|
|
41686
|
+
recommendations.push("Move supporting detail into references/*.md and link it, rather than growing the body.");
|
|
41687
|
+
}
|
|
41452
41688
|
return { score, note, findings, recommendations };
|
|
41453
41689
|
}
|
|
41454
41690
|
function scoreClarity2(body) {
|
|
41455
|
-
|
|
41456
|
-
|
|
41457
|
-
|
|
41458
|
-
const
|
|
41691
|
+
const base2 = scoreClarityFromDensities(body);
|
|
41692
|
+
const checkability = completionCheckability(body);
|
|
41693
|
+
const score = clamp(base2.score * checkability);
|
|
41694
|
+
const findings = [...base2.findings ?? []];
|
|
41695
|
+
const recommendations = [...base2.recommendations ?? []];
|
|
41696
|
+
if (checkability < 1) {
|
|
41697
|
+
findings.push('Step-shaped content uses vague completion bounds (e.g. "as needed", "when ready").');
|
|
41698
|
+
recommendations.push("Replace vague bounds with a decidable done-condition per step.");
|
|
41699
|
+
}
|
|
41700
|
+
return { score, note: base2.note, findings, recommendations };
|
|
41701
|
+
}
|
|
41702
|
+
function scoreTriggerAccuracy(body, description, data) {
|
|
41703
|
+
const userInvoked = data?.["disable-model-invocation"] === true;
|
|
41704
|
+
if (userInvoked) {
|
|
41705
|
+
return scoreUserInvokedDescription(description);
|
|
41706
|
+
}
|
|
41707
|
+
const phrases = collectTriggerPhrases(body, description);
|
|
41708
|
+
const count2 = countTriggerBranches(phrases);
|
|
41459
41709
|
let score;
|
|
41460
41710
|
if (count2 >= 3 && count2 <= 10) {
|
|
41461
41711
|
score = 1;
|
|
@@ -41467,13 +41717,38 @@ function scoreTriggerAccuracy(body) {
|
|
|
41467
41717
|
const findings = [];
|
|
41468
41718
|
const recommendations = [];
|
|
41469
41719
|
if (count2 < 3) {
|
|
41470
|
-
findings.push(`Only ${count2} trigger
|
|
41471
|
-
recommendations.push("Add 1\u20132 more When-to-Use scenarios
|
|
41720
|
+
findings.push(`Only ${count2} distinct trigger branch(es) found; aim for 3\u201310 for reliable skill activation.`);
|
|
41721
|
+
recommendations.push("Add 1\u20132 more When-to-Use scenarios covering genuinely distinct branches.");
|
|
41472
41722
|
} else if (count2 > 10) {
|
|
41473
|
-
findings.push(`${count2} trigger
|
|
41723
|
+
findings.push(`${count2} distinct trigger branches may cause overlap with adjacent skills.`);
|
|
41474
41724
|
recommendations.push("Consolidate overlapping triggers or narrow the activation scope.");
|
|
41475
41725
|
}
|
|
41476
|
-
|
|
41726
|
+
if (phrases.length > count2) {
|
|
41727
|
+
findings.push(`${phrases.length} trigger phrase(s) collapse to ${count2} distinct branch(es) \u2014 some are synonym clusters.`);
|
|
41728
|
+
recommendations.push("Collapse synonym-cluster triggers into one phrase per genuine branch.");
|
|
41729
|
+
}
|
|
41730
|
+
return {
|
|
41731
|
+
score,
|
|
41732
|
+
note: `${count2} distinct trigger branch(es) (${phrases.length} phrases)`,
|
|
41733
|
+
findings,
|
|
41734
|
+
recommendations
|
|
41735
|
+
};
|
|
41736
|
+
}
|
|
41737
|
+
function scoreUserInvokedDescription(description) {
|
|
41738
|
+
const richness = descriptionTriggerRichness(description);
|
|
41739
|
+
const score = clamp(1 - richness);
|
|
41740
|
+
const findings = [];
|
|
41741
|
+
const recommendations = [];
|
|
41742
|
+
if (richness > 0.4) {
|
|
41743
|
+
findings.push("This skill is user-invoked (disable-model-invocation: true) but the description reads " + "trigger-rich. A user-invoked skill cannot be fired by other skills or commands.");
|
|
41744
|
+
recommendations.push("Rewrite the description as a one-line, human-facing summary \u2014 no branch list.");
|
|
41745
|
+
}
|
|
41746
|
+
return {
|
|
41747
|
+
score,
|
|
41748
|
+
note: `User-invoked: description trigger-richness ${richness.toFixed(2)} (lower is better)`,
|
|
41749
|
+
findings,
|
|
41750
|
+
recommendations
|
|
41751
|
+
};
|
|
41477
41752
|
}
|
|
41478
41753
|
function scoreAntiHallucination(body) {
|
|
41479
41754
|
const density = keywordDensity(body, [
|
|
@@ -41495,16 +41770,45 @@ function scoreAntiHallucination(body) {
|
|
|
41495
41770
|
}
|
|
41496
41771
|
return { score: density, note, findings, recommendations };
|
|
41497
41772
|
}
|
|
41498
|
-
function scoreConciseness2(body) {
|
|
41499
|
-
const
|
|
41500
|
-
|
|
41773
|
+
function scoreConciseness2(body, description) {
|
|
41774
|
+
const lengthScore = scoreLength(body, 500, 15000);
|
|
41775
|
+
const budgetScore = description ? scoreDescriptionBudget(description) : 1;
|
|
41776
|
+
const noOp = noOpDensity(body);
|
|
41777
|
+
const descDup = description ? duplicationRatio(description, body, 12) : 0;
|
|
41778
|
+
const bodyDup = duplicationRatio(body, undefined, 12);
|
|
41779
|
+
const score = clamp(lengthScore * budgetScore * (1 - noOp) * (1 - descDup) * (1 - bodyDup * 0.3));
|
|
41780
|
+
const findings = [];
|
|
41781
|
+
const recommendations = [];
|
|
41782
|
+
if (budgetScore < 1) {
|
|
41783
|
+
findings.push(`Description is ${description.length} chars, outside the 20\u2013500 char budget.`);
|
|
41784
|
+
recommendations.push("Tighten the description to the identity phrase plus distinct trigger branches.");
|
|
41785
|
+
}
|
|
41786
|
+
if (noOp > 0.2) {
|
|
41787
|
+
findings.push("Body contains default-behavior phrases that do not change model behavior (no-op candidates).");
|
|
41788
|
+
recommendations.push("Delete no-op instructions rather than trimming them \u2014 they add no signal.");
|
|
41789
|
+
}
|
|
41790
|
+
if (descDup > 0.3) {
|
|
41791
|
+
findings.push("Body restates the description near-verbatim (duplication).");
|
|
41792
|
+
recommendations.push("State identity once in the description; do not repeat it in the body.");
|
|
41793
|
+
}
|
|
41794
|
+
if (bodyDup > 0.15) {
|
|
41795
|
+
findings.push("Body repeats the same phrasing (n-gram duplication) in multiple places.");
|
|
41796
|
+
recommendations.push("Collapse duplicated phrasing into one authoritative section; cite it elsewhere.");
|
|
41797
|
+
}
|
|
41798
|
+
return { score, note: `Body length: ${body.length} chars`, findings, recommendations };
|
|
41799
|
+
}
|
|
41800
|
+
function collectTriggerPhrases(body, description) {
|
|
41801
|
+
const fromSection = triggerSectionPhrases(body);
|
|
41802
|
+
if (fromSection.length > 0)
|
|
41803
|
+
return fromSection;
|
|
41804
|
+
return descriptionTriggerPhrases(description);
|
|
41501
41805
|
}
|
|
41502
|
-
function
|
|
41806
|
+
function triggerSectionPhrases(body) {
|
|
41503
41807
|
const lines = body.split(`
|
|
41504
41808
|
`);
|
|
41505
41809
|
let inTriggerSection = false;
|
|
41506
41810
|
let sectionDepth = 0;
|
|
41507
|
-
|
|
41811
|
+
const phrases = [];
|
|
41508
41812
|
for (const line of lines) {
|
|
41509
41813
|
const headingMatch = line.match(/^(#{1,6})\s+(.+)/);
|
|
41510
41814
|
if (headingMatch) {
|
|
@@ -41519,19 +41823,18 @@ function countTriggerPhrases(body) {
|
|
|
41519
41823
|
continue;
|
|
41520
41824
|
}
|
|
41521
41825
|
if (inTriggerSection) {
|
|
41522
|
-
|
|
41523
|
-
|
|
41826
|
+
const listMatch = line.match(/^\s*(?:[-*+]|\d+[.)])\s+(.+)/);
|
|
41827
|
+
if (listMatch?.[1]) {
|
|
41828
|
+
phrases.push(listMatch[1].replace(/^["']|["']$/g, "").trim());
|
|
41524
41829
|
}
|
|
41525
41830
|
}
|
|
41526
41831
|
}
|
|
41527
|
-
|
|
41528
|
-
|
|
41529
|
-
|
|
41530
|
-
|
|
41531
|
-
|
|
41532
|
-
|
|
41533
|
-
}
|
|
41534
|
-
return count2;
|
|
41832
|
+
return phrases;
|
|
41833
|
+
}
|
|
41834
|
+
function descriptionTriggerPhrases(description) {
|
|
41835
|
+
if (!description)
|
|
41836
|
+
return [];
|
|
41837
|
+
return description.split(/[,;]|(?:\bor\b)/i).map((s) => s.trim()).filter((s) => s.length > 3);
|
|
41535
41838
|
}
|
|
41536
41839
|
var init_skill = __esm(() => {
|
|
41537
41840
|
init_heuristics();
|
|
@@ -95937,6 +96240,7 @@ async function replaySplit(backend, skill2, cases, split, toolsCalled) {
|
|
|
95937
96240
|
init_src();
|
|
95938
96241
|
|
|
95939
96242
|
// src/operations/evolve.ts
|
|
96243
|
+
var FAILURE_MODES = ["sprawl", "sediment", "duplication", "no-op", "premature-completion"];
|
|
95940
96244
|
function isHookApplyCapableOpt(opts) {
|
|
95941
96245
|
if (!opts)
|
|
95942
96246
|
return false;
|
|
@@ -96306,6 +96610,9 @@ async function ingestProposal(db2, type, name, resolvedPath, ingestPath, opts, b
|
|
|
96306
96610
|
if (!change.dimension || !change.location || !change.proposed || !change.reason) {
|
|
96307
96611
|
throw Object.assign(new Error(`Invalid ProposedChange: missing required field (dimension, location, proposed, reason)`), { code: 1 });
|
|
96308
96612
|
}
|
|
96613
|
+
if (change.failure_mode !== undefined && !FAILURE_MODES.includes(change.failure_mode)) {
|
|
96614
|
+
throw Object.assign(new Error(`Invalid failure_mode "${change.failure_mode}". Expected one of: ${FAILURE_MODES.join(", ")}`), { code: 1 });
|
|
96615
|
+
}
|
|
96309
96616
|
}
|
|
96310
96617
|
const proposalDao = new ProposalDao(db2);
|
|
96311
96618
|
const existingProposals = await proposalDao.getProposals(type, name);
|
|
@@ -96872,7 +97179,7 @@ function classifyFix(finding) {
|
|
|
96872
97179
|
if (finding.severity === "error") {
|
|
96873
97180
|
return "auto-apply";
|
|
96874
97181
|
}
|
|
96875
|
-
if (["description", "trigger-accuracy", "clarity", "conciseness"].includes(finding.field)) {
|
|
97182
|
+
if (["description", "trigger-accuracy", "clarity", "conciseness", "invocation-mode"].includes(finding.field)) {
|
|
96876
97183
|
return "suggest";
|
|
96877
97184
|
}
|
|
96878
97185
|
if (["skill-linkage", "tool-selection", "model-fit", "platform-coverage"].includes(finding.field)) {
|
|
@@ -97261,7 +97568,7 @@ function addTargetOption(cmd) {
|
|
|
97261
97568
|
return cmd.option("-t, --target <agent>", "Target agent platform", "claude");
|
|
97262
97569
|
}
|
|
97263
97570
|
function addScaffoldOptions(cmd) {
|
|
97264
|
-
return cmd.option("-d, --description <text>", "Content description").option("-t, --target <agent>", "Target agent platform", "claude").option("-o, --output <dir>", "Output directory (default: cwd)").option("--template <tier>", "Template tier (e.g. minimal / standard / specialist)").option("--tools <list>", "Comma-separated tool names to pre-populate frontmatter").option("--force", "Overwrite existing file if present");
|
|
97571
|
+
return cmd.option("-d, --description <text>", "Content description").option("-t, --target <agent>", "Target agent platform", "claude").option("-o, --output <dir>", "Output directory (default: cwd)").option("--template <tier>", "Template tier (e.g. minimal / standard / specialist)").option("--tools <list>", "Comma-separated tool names to pre-populate frontmatter").option("--invocation-mode <mode>", "Skill invocation axis: 'user' (disable-model-invocation + one-line description) " + "or 'model' (trigger-rich description, default)").option("--force", "Overwrite existing file if present");
|
|
97265
97572
|
}
|
|
97266
97573
|
function addEvolveOptions(cmd) {
|
|
97267
97574
|
return cmd.option("-t, --target <agent>", "Target agent platform", "claude").option("--from <date>", "Analyze evaluations since date (ISO 8601)").option("--propose-only", "Generate proposal without applying").option("--accept <id>", "Accept a specific proposal by ID").option("--reject <id>", "Reject a specific proposal").option("--json", "Output machine-readable JSON (envelope-out with --propose-only)").option("--ingest <file>", "Agent-authored proposal JSON (ingest-in mode)").option("--margin <n>", "\u0394-margin gate threshold for accept (default 0.05)", Number.parseFloat, 0.05).option("--eval-gate", "Enable empirical behavior gate (requires skills/<name>/eval/cases.yaml)").option("--analyze", "Print analysis summary (trends, score, data sources) without writing a proposal").option("--history", "List applied proposal versions from the store").option("--rollback <id>", "Rollback to a prior version by proposal_id (requires --confirm)").option("--confirm", "Confirm a destructive operation (required for --rollback)");
|
|
@@ -98451,6 +98758,13 @@ async function migrateSkills(sources, dest, opts) {
|
|
|
98451
98758
|
init_src();
|
|
98452
98759
|
|
|
98453
98760
|
// src/commands/skill.ts
|
|
98761
|
+
function parseInvocationMode(value) {
|
|
98762
|
+
if (value === undefined)
|
|
98763
|
+
return;
|
|
98764
|
+
if (value === "user" || value === "model")
|
|
98765
|
+
return value;
|
|
98766
|
+
throw new Error(`Invalid --invocation-mode "${value}". Expected 'user' or 'model'.`);
|
|
98767
|
+
}
|
|
98454
98768
|
async function skillScaffold(opts) {
|
|
98455
98769
|
const target = resolveTarget(opts);
|
|
98456
98770
|
const createdPath = await scaffold("skill", opts.name, {
|
|
@@ -98459,7 +98773,8 @@ async function skillScaffold(opts) {
|
|
|
98459
98773
|
output: opts.output,
|
|
98460
98774
|
force: opts.force,
|
|
98461
98775
|
template: opts.template,
|
|
98462
|
-
tools: opts.tools
|
|
98776
|
+
tools: opts.tools,
|
|
98777
|
+
invocationMode: parseInvocationMode(opts.invocationMode)
|
|
98463
98778
|
});
|
|
98464
98779
|
echo(`Created: ${createdPath}`);
|
|
98465
98780
|
return;
|
package/package.json
CHANGED
package/rubrics/agent.yaml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
version:
|
|
1
|
+
version: 2
|
|
2
2
|
type: agent
|
|
3
3
|
dimensions:
|
|
4
4
|
- name: completeness
|
|
@@ -6,10 +6,21 @@ dimensions:
|
|
|
6
6
|
criterion: >
|
|
7
7
|
Does the agent cover its stated scope end-to-end? Penalize missing workflow
|
|
8
8
|
phases, undocumented edge cases, and gaps between the stated trigger and
|
|
9
|
-
the actual execution path.
|
|
9
|
+
the actual execution path. Measurable proxies (folded in, not a new
|
|
10
|
+
dimension): the frontmatter description — the dispatch-time signal a
|
|
11
|
+
Task-tool orchestrator reads to pick this agent — sits within a 20-2000
|
|
12
|
+
char context-load budget (wider than skill’s 20-500: Claude Code's own
|
|
13
|
+
convention embeds few-shot <example> blocks directly in an agent's
|
|
14
|
+
description, unlike a skill's single-sentence trigger), is free of curated
|
|
15
|
+
no-op phrases, and does not restate the body near-verbatim. Progressive
|
|
16
|
+
disclosure (references/*.md) does NOT apply here — agents in this repo
|
|
17
|
+
are flat single-file documents with no companion references/ directory
|
|
18
|
+
convention, unlike skills. Whether the description's wording will actually
|
|
19
|
+
read well to a dispatching orchestrator is genuinely subjective — judge
|
|
20
|
+
that here, in the two-call seam, not via a deterministic proxy.
|
|
10
21
|
anchors:
|
|
11
|
-
excellent: "Full workflow from intake to delivery; every phase has explicit steps and exit conditions."
|
|
12
|
-
poor: "Scope claims coverage but execution path skips phases
|
|
22
|
+
excellent: "Full workflow from intake to delivery; every phase has explicit steps and exit conditions; description is tight, within budget, and not duplicated from the body."
|
|
23
|
+
poor: "Scope claims coverage but execution path skips phases, leaves edge cases unhandled, or the description is vague, no-op-heavy, or duplicated from the body."
|
|
13
24
|
|
|
14
25
|
- name: role-clarity
|
|
15
26
|
weight: 0.25
|
package/rubrics/command.yaml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
version:
|
|
1
|
+
version: 2
|
|
2
2
|
type: command
|
|
3
3
|
dimensions:
|
|
4
4
|
- name: completeness
|
|
@@ -16,10 +16,17 @@ dimensions:
|
|
|
16
16
|
criterion: >
|
|
17
17
|
Is the command's purpose and usage unambiguous? The description should
|
|
18
18
|
tell the user what it does and when to use it. Penalize descriptions
|
|
19
|
-
that restate the command name without adding information.
|
|
19
|
+
that restate the command name without adding information. Measurable
|
|
20
|
+
proxies (folded in, not a new dimension): description sits within a
|
|
21
|
+
20-500 char context-load budget; description is free of curated no-op
|
|
22
|
+
phrases ("be helpful", "think carefully") that add no signal; description
|
|
23
|
+
does not restate the command body near-verbatim (n-gram duplication).
|
|
24
|
+
Whether the description's actual WORDING is well-chosen for the intended
|
|
25
|
+
audience is genuinely subjective — judge that here, in the two-call seam,
|
|
26
|
+
not via a deterministic proxy.
|
|
20
27
|
anchors:
|
|
21
|
-
excellent: "Description states what the command does, when to use it, and what the output means."
|
|
22
|
-
poor: "Description restates the command name; user cannot tell when to use it."
|
|
28
|
+
excellent: "Description states what the command does, when to use it, and what the output means, within budget, with no no-op or duplicated phrasing."
|
|
29
|
+
poor: "Description restates the command name, exceeds the context-load budget, or duplicates the body verbatim; user cannot tell when to use it."
|
|
23
30
|
|
|
24
31
|
- name: argument-hints
|
|
25
32
|
weight: 0.20
|
package/rubrics/hook.yaml
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
version: 1
|
|
2
2
|
type: hook
|
|
3
|
+
# R2 scoping note (task 0070): description-budget / no-op-density / duplication proxies
|
|
4
|
+
# do NOT transfer to hooks. A hook definition is a JSON matcher/command table with no
|
|
5
|
+
# frontmatter description field and no markdown prose body — there is nothing for those
|
|
6
|
+
# proxies to measure. The trigger-equivalent concept here is the JSON `matcher` field,
|
|
7
|
+
# already scored by pattern-match-quality. This is a deliberate N/A, not a gap.
|
|
3
8
|
dimensions:
|
|
4
9
|
- name: correctness
|
|
5
10
|
weight: 0.25
|
package/rubrics/magent.yaml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
version:
|
|
1
|
+
version: 2
|
|
2
2
|
type: magent
|
|
3
3
|
dimensions:
|
|
4
4
|
- name: completeness
|
|
@@ -27,10 +27,17 @@ dimensions:
|
|
|
27
27
|
criterion: >
|
|
28
28
|
Is the config as short as possible while remaining complete? Penalize
|
|
29
29
|
redundant rule restating, boilerplate that adds no information, and
|
|
30
|
-
rules that could be merged without loss.
|
|
30
|
+
rules that could be merged without loss. Measurable proxies (folded in,
|
|
31
|
+
not a new dimension): body is free of curated no-op phrases ("be
|
|
32
|
+
helpful", "think carefully") that restate default model behavior without
|
|
33
|
+
changing it; body has low within-body n-gram duplication. A magent
|
|
34
|
+
config (AGENTS.md/CLAUDE.md) has no separate frontmatter description
|
|
35
|
+
field — it is read wholesale rather than dispatch-selected — so the
|
|
36
|
+
description-budget check that applies to skill/command/agent does not
|
|
37
|
+
apply here.
|
|
31
38
|
anchors:
|
|
32
|
-
excellent: "Minimal length with no redundancy; every rule carries unique information."
|
|
33
|
-
poor: "Redundant rules
|
|
39
|
+
excellent: "Minimal length with no redundancy; every rule carries unique information; no no-op phrasing."
|
|
40
|
+
poor: "Redundant rules, boilerplate, or no-op phrasing that adds no signal; rules could be merged without loss."
|
|
34
41
|
|
|
35
42
|
- name: tone-consistency
|
|
36
43
|
weight: 0.20
|
package/rubrics/skill.yaml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
version:
|
|
1
|
+
version: 2
|
|
2
2
|
type: skill
|
|
3
3
|
dimensions:
|
|
4
4
|
- name: completeness
|
|
@@ -6,31 +6,45 @@ dimensions:
|
|
|
6
6
|
criterion: >
|
|
7
7
|
Does the skill cover its stated purpose end-to-end? Penalize missing
|
|
8
8
|
steps, undocumented preconditions, and gaps between the trigger and the
|
|
9
|
-
delivered outcome. A complete skill leaves no implicit step.
|
|
9
|
+
delivered outcome. A complete skill leaves no implicit step. Also check
|
|
10
|
+
progressive-disclosure shape: a body that has grown past a reasonable
|
|
11
|
+
length budget without moving supporting detail into references/*.md (or
|
|
12
|
+
linking a "See Also"/"Additional Resources" section) is incomplete in a
|
|
13
|
+
structural sense — coverage exists but isn't disclosed in a navigable way.
|
|
10
14
|
anchors:
|
|
11
|
-
excellent: "Every phase from trigger to outcome is explicit; preconditions and exit conditions stated
|
|
12
|
-
poor: "Purpose claims coverage but steps are implicit or preconditions unstated."
|
|
15
|
+
excellent: "Every phase from trigger to outcome is explicit; preconditions and exit conditions stated; a body over the length budget discloses detail via references/."
|
|
16
|
+
poor: "Purpose claims coverage but steps are implicit or preconditions unstated; a long body with no references/ link buries detail instead of disclosing it."
|
|
13
17
|
|
|
14
18
|
- name: clarity
|
|
15
19
|
weight: 0.25
|
|
16
20
|
criterion: >
|
|
17
21
|
Is the skill's instruction unambiguous to a fresh agent? Penalize
|
|
18
22
|
vague verbs ('handle', 'process'), undefined terms, and instructions
|
|
19
|
-
that require external context the skill does not provide.
|
|
23
|
+
that require external context the skill does not provide. For
|
|
24
|
+
step/workflow-shaped content, also check completion-criteria
|
|
25
|
+
checkability: each step should read as a decidable done-condition.
|
|
26
|
+
Penalize vague bounds ("understanding reached", "as needed", "as
|
|
27
|
+
appropriate") that leave a reader unable to tell when a step is done.
|
|
20
28
|
anchors:
|
|
21
|
-
excellent: "Concrete verbs, defined terms, self-contained instructions an agent can execute without guessing."
|
|
22
|
-
poor: "Vague verbs and undefined terms; agent must guess the intended behavior."
|
|
29
|
+
excellent: "Concrete verbs, defined terms, self-contained instructions an agent can execute without guessing; every step has a decidable done-condition."
|
|
30
|
+
poor: "Vague verbs and undefined terms; agent must guess the intended behavior; steps end on vague bounds like 'as needed' with no checkable condition."
|
|
23
31
|
|
|
24
32
|
- name: trigger-accuracy
|
|
25
33
|
weight: 0.20
|
|
26
34
|
criterion: >
|
|
27
35
|
Does the skill fire on the right inputs and not on adjacent ones? The
|
|
28
|
-
description must precisely scope when the skill activates.
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
description must precisely scope when the skill activates. Score by
|
|
37
|
+
DISTINCT trigger branches, not raw phrase count — a description with 8
|
|
38
|
+
near-synonymous phrasings of the same branch ("review this code" /
|
|
39
|
+
"review the code" / "review code") is still one branch, not eight.
|
|
40
|
+
Reward description phrasing that covers 3-10 genuinely distinct
|
|
41
|
+
branches (one phrase per branch); penalize triggers that are too broad
|
|
42
|
+
(fires on unrelated requests), too narrow (misses the intended case),
|
|
43
|
+
or padded with synonym clusters that inflate the apparent count without
|
|
44
|
+
adding coverage.
|
|
31
45
|
anchors:
|
|
32
|
-
excellent: "Trigger description scopes activation precisely; no overlap with adjacent skills."
|
|
33
|
-
poor: "Trigger is broad enough to fire on unrelated requests
|
|
46
|
+
excellent: "Trigger description scopes activation precisely across 3-10 genuinely distinct branches; no synonym-cluster padding; no overlap with adjacent skills."
|
|
47
|
+
poor: "Trigger is broad enough to fire on unrelated requests, narrow enough to miss the intended case, or pads the same branch with several near-identical phrasings."
|
|
34
48
|
|
|
35
49
|
- name: anti-hallucination
|
|
36
50
|
weight: 0.15
|
|
@@ -47,7 +61,17 @@ dimensions:
|
|
|
47
61
|
criterion: >
|
|
48
62
|
Is the skill as short as possible while remaining complete? Penalize
|
|
49
63
|
redundant restating, boilerplate that adds no information, and steps
|
|
50
|
-
that could be merged without loss.
|
|
64
|
+
that could be merged without loss. Specifically check: (1) description
|
|
65
|
+
char budget — a model-invoked description sits in context every turn it
|
|
66
|
+
is a candidate, so it should be no longer than the identity phrase plus
|
|
67
|
+
its distinct trigger branches require; (2) no-op density — instructions
|
|
68
|
+
that restate default model behavior ("be helpful", "think carefully")
|
|
69
|
+
change nothing and should be deleted, not trimmed; (3) duplication —
|
|
70
|
+
the body should not restate the description near-verbatim, and should
|
|
71
|
+
not repeat the same phrasing in multiple places within itself. Leading-
|
|
72
|
+
word quality (is this the RIGHT tight/imperative word for the described
|
|
73
|
+
action, for this specific model) is genuinely subjective — judge it
|
|
74
|
+
here, in the two-call seam, not via a deterministic proxy.
|
|
51
75
|
anchors:
|
|
52
|
-
excellent: "Minimal length with no redundancy;
|
|
53
|
-
poor: "Redundant restating and boilerplate; steps could be merged without loss."
|
|
76
|
+
excellent: "Minimal length with no redundancy; description stays within its context-load budget; no no-op instructions; no restated or duplicated phrasing; leading words are precise and model-appropriate."
|
|
77
|
+
poor: "Redundant restating and boilerplate; steps could be merged without loss; description is bloated; body pads out no-op instructions or duplicates the description verbatim."
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: <!-- NAME -->
|
|
3
|
+
# Description rules: front-load the leading identity phrase; one trigger per genuine
|
|
4
|
+
# branch (collapse synonym triggers into one); never restate the body's identity line.
|
|
3
5
|
description: <!-- DESCRIPTION -->
|
|
4
6
|
license: Apache-2.0
|
|
5
7
|
metadata:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: <!-- NAME -->
|
|
3
|
+
# Description rules: front-load the leading identity phrase; one trigger per genuine
|
|
4
|
+
# branch (collapse synonym triggers into one); never restate the body's identity line.
|
|
3
5
|
description: <!-- DESCRIPTION -->
|
|
4
6
|
license: Apache-2.0
|
|
5
7
|
metadata:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: <!-- NAME -->
|
|
3
|
+
# Description rules: front-load the leading identity phrase; one trigger per genuine
|
|
4
|
+
# branch (collapse synonym triggers into one); never restate the body's identity line.
|
|
3
5
|
description: <!-- DESCRIPTION -->
|
|
4
6
|
license: Apache-2.0
|
|
5
7
|
metadata:
|