@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 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
- return name;
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 (statSync(srcPath).isDirectory()) {
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
- if (!existsSync4(join4(pluginRoot, "plugin.json"))) {
13865
- throw new Error(`plugin.json not found in resolved plugin root: ${pluginRoot}`);
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 statSync2 } from "fs";
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 = statSync2(skillPath).isDirectory() ? skillPath : dirname3(skillPath);
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: ["name", "description"],
14147
+ command: ["description"],
14126
14148
  agent: ["name", "description", "model", "tools"],
14127
- hook: ["name", "description", "event"],
14128
- magent: ["name", "description"]
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 scoreCorrectness(data) {
14228
- const raw = data.event;
14229
- let base;
14230
- let note;
14231
- if (typeof raw === "string" && raw.trim().length > 0) {
14232
- const event = raw.trim();
14233
- if (KNOWN_HOOK_EVENTS.includes(event)) {
14234
- base = 1;
14235
- note = `Valid event: ${event}`;
14236
- } else {
14237
- base = 0.5;
14238
- note = `Unknown event: ${event}`;
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 { score: clamp(base), note };
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 scorePatternMatchQuality(body) {
14272
- const hasSpecificGlob = /\*\.[a-z]+\b/i.test(body);
14273
- const patternDensity = keywordDensity(body, ["match", "pattern", "regex", "glob"]);
14274
- let score;
14275
- let note;
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 parsed = parseFrontmatterSafe(content);
14290
- const data = parsed ?? {};
14291
- const body = extractBody(content);
14292
- const isParseError = parsed === null;
14293
- const dimensions = (() => {
14294
- if (isParseError) {
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
- correctness: { score: 0.1, note: errNote },
14298
- "event-coverage": { score: 0, note: errNote },
14299
- safety: { score: 0, note: errNote },
14300
- "pattern-match-quality": { score: 0, note: errNote }
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
- correctness: scoreCorrectness(data),
14305
- "event-coverage": scoreEventCoverage(data, body),
14306
- safety: scoreSafety(body),
14307
- "pattern-match-quality": scorePatternMatchQuality(body)
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 statSync3 } from "fs";
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 = statSync3(resolvedPath);
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
- return _validateContent(type, content, {
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 readdirSync2, readFileSync as readFileSync6, renameSync, rmSync as rmSync2, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
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) => readdirSync2(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 = statSync4(path);
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 statSync5 } from "fs";
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 = statSync5(cwd5);
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 note = missing.length > 0 ? `Missing: ${missing.join(", ")}` : "All required fields present";
40380
- return { score: clamp(score), note };
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
- return { score, note };
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
- return { score, note: `${count2} tools selected` };
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
- return { score, note };
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
- return { score, note };
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 argsFactor = Array.isArray(data.arguments) ? 1 : 0;
40493
- const score = clamp(base2 * (0.5 + 0.5 * argsFactor));
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
- return { score, note };
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 args = data.arguments;
40507
- if (!Array.isArray(args)) {
40508
- return { score: 0, note: "No arguments array found" };
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 (args.length === 0) {
40511
- return { score: 0, note: "0/0 arguments have hints" };
40512
- }
40513
- let withHints = 0;
40514
- for (const arg of args) {
40515
- if (typeof arg === "object" && arg !== null && "name" in arg && "description" in arg) {
40516
- withHints++;
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 score = clamp(withHints / args.length);
40520
- return { score, note: `${withHints}/${args.length} arguments have hints` };
40521
- }
40522
- function scoreToolReferences(body) {
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 weightedCount = structuredCount + Math.min(backtickCount, 1);
40719
+ const bodyWeighted = structuredCount + Math.min(backtickCount, 1);
40526
40720
  let score;
40527
40721
  let note;
40528
- if (structuredCount >= 1 || weightedCount >= 2) {
40722
+ if (toolCount >= 5) {
40529
40723
  score = 1;
40530
- note = "Uses tool references";
40531
- } else if (weightedCount === 1) {
40532
- score = 0.6;
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
- return { score, note };
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: clamp(found / MAGENT_SECTIONS.length),
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
- return { score, note: `${platforms.length} platforms covered` };
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
- return { score: density, note: `${count2} safety markers found` };
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 = [/^## IDENTITY/m, /^## SOUL/m, /^## AGENTS/m, /^## USER/m];
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
- return { score, note };
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
- return { score, note: `${count2} trigger phrases found` };
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
- return { score: density, note };
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, 5000);
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 join7 } from "path";
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 = join7(homeDir, ".superskill", "rubrics", `${type}.yaml`);
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 = join7(import.meta.dir, "..", "rubrics", `${type}.yaml`);
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 = join7(import.meta.dir, "..", "..", "rubrics", `${type}.yaml`);
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 statSync6(path7, optionsOrSettings) {
46912
+ function statSync7(path7, optionsOrSettings) {
46643
46913
  const settings = getSettings(optionsOrSettings);
46644
46914
  return sync.read(path7, settings);
46645
46915
  }
46646
- exports.statSync = statSync6;
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 join8 } from "path";
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 join82 } from "path";
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 ${join8(rest2.outputRoot ?? process.cwd(), rest2.relativeDirPath, rest2.relativeFilePath)}: ${formatError2(parseResult.error)}`);
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 ${join8(this.relativeDirPath, this.relativeFilePath)}: ${formatError2(result.error)}`)
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 = join8(outputRoot, _RulesyncCommand.getSettablePaths().relativeDirPath, relativeFilePath);
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 = join82(".config", "amp");
69807
- AMP_SKILLS_PROJECT_DIR = join82(AMP_AGENTS_DIR, "skills");
69808
- AMP_SKILLS_GLOBAL_DIR = join82(".config", "agents", "skills");
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 readdirSync3,
96352
+ readdirSync as readdirSync4,
96008
96353
  readFileSync as readFileSync13,
96009
96354
  rmSync as rmSync4,
96010
- statSync as statSync6,
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 readdirSync3(agentsDir)) {
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(join223(fallback, "plugin.json")))
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 readdirSync3(dir)) {
96717
+ for (const entry of readdirSync4(dir)) {
96373
96718
  const path11 = join223(dir, entry);
96374
- const stats = statSync6(path11);
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 readdirSync3(source)) {
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 (statSync6(sourcePath).isDirectory()) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/superskill",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "A manager for multi-agent skill, slash command, subagent, hook, MCP and etc.",
5
5
  "keywords": [
6
6
  "cli",
@@ -4,13 +4,13 @@ dimensions:
4
4
  - name: completeness
5
5
  weight: 0.25
6
6
  criterion: >
7
- Does the command cover its stated function end-to-end? Penalize missing
8
- argument validation, undocumented flags, and incomplete output. A
9
- complete command handles the happy path and the documented error paths.
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: "All arguments validated, all flags documented, output covers success and error paths."
12
- poor: "Happy path only; argument validation and error paths undocumented."
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
- Are argument hints present and accurate? Each argument should have a
28
- hint that helps the user supply the right value. Penalize missing hints,
29
- hints that restate the argument name, and hints that mislead.
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: "Every argument has a hint that guides the user to the correct value."
32
- poor: "Hints missing, or restate the argument name without guidance."
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.30
5
+ weight: 0.25
6
6
  criterion: >
7
- Does the hook's logic produce the intended effect? Penalize hooks that
8
- fire on the wrong event, mutate state the spec says they shouldn't, or
9
- exit with the wrong code on a documented path.
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: "Logic matches the spec on every documented path; correct event, mutation, and exit code."
12
- poor: "Logic diverges from spec on at least one documented path."
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.30
15
+ weight: 0.20
16
16
  criterion: >
17
- Does the hook handle all events it claims to? Penalize hooks that
18
- register for an event but silently no-op on it, or that miss an event
19
- the spec requires. Coverage means the hook acts on every registered event.
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: "Every registered event has a real handler; no silent no-ops; no missing required events."
22
- poor: "Registered events include silent no-ops, or required events are missing."
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.25
27
+ weight: 0.35
26
28
  criterion: >
27
- Does the hook avoid destructive side effects? Penalize hooks that
28
- force-push, delete without confirmation, bypass gates, or modify files
29
- outside their stated scope. Safety is about blast radius, not just correctness.
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 destructive side effect without an explicit guard; blast radius matches stated scope."
32
- poor: "Destructive side effects (force-push, delete, bypass) without guards."
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.15
38
+ weight: 0.20
36
39
  criterion: >
37
- Are the matchers precise? Penalize matchers that over-match (fire on
38
- unrelated commands) or under-match (miss the intended case). A precise
39
- matcher fires on exactly the intended inputs.
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: "Matchers fire on exactly the intended inputs; no over- or under-matching."
42
- poor: "Matchers over-match (fire on unrelated commands) or under-match (miss intended case)."
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."
@@ -4,13 +4,14 @@ dimensions:
4
4
  - name: completeness
5
5
  weight: 0.25
6
6
  criterion: >
7
- Does the main-agent config cover its stated scope end-to-end? Penalize
8
- missing routing rules, undocumented fallbacks, and gaps between the
9
- stated trigger and the actual execution path.
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: "Routing, fallbacks, and execution path are fully explicit; no implicit steps."
12
- poor: "Scope claims coverage but routing or fallbacks are implicit."
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: >