skilld 0.15.1 → 0.15.3

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/cli.mjs CHANGED
@@ -776,15 +776,26 @@ function highlightTerms(content, terms) {
776
776
  const pattern = new RegExp(`(${sorted.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})`, "gi");
777
777
  return content.replace(pattern, "\x1B[33m$1\x1B[0m");
778
778
  }
779
- function formatSnippet(r) {
779
+ function scoreLabel(pct) {
780
+ return `${pct >= 70 ? "\x1B[32m" : pct >= 40 ? "\x1B[33m" : "\x1B[90m"}${pct}%\x1B[0m`;
781
+ }
782
+ function normalizeScores(results) {
783
+ const map = /* @__PURE__ */ new Map();
784
+ const max = results.reduce((m, r) => Math.max(m, r.score), 0);
785
+ for (const r of results) map.set(r, max > 0 ? Math.round(r.score / max * 100) : 0);
786
+ return map;
787
+ }
788
+ function formatSnippet(r, versions, pct) {
780
789
  const refPath = `.claude/skills/${r.package}/.skilld/${r.source}`;
781
790
  const lineRange = r.lineStart === r.lineEnd ? `L${r.lineStart}` : `L${r.lineStart}-${r.lineEnd}`;
782
- const score = `\x1B[90m${r.score.toFixed(2)}\x1B[0m`;
791
+ const score = pct != null ? scoreLabel(pct) : `\x1B[90m${r.score.toFixed(2)}\x1B[0m`;
792
+ const version = versions?.get(r.package);
793
+ const pkgLabel = version ? `${r.package}@${version}` : r.package;
783
794
  const scopeStr = r.scope?.length ? `${r.scope.map((e) => e.name).join(".")} → ` : "";
784
795
  const entityStr = r.entities?.map((e) => e.signature || `${e.type} ${e.name}`).join(", ");
785
796
  const highlighted = highlightTerms(r.content, r.highlights);
786
797
  return [
787
- `${r.package} ${score}${entityStr ? ` \x1B[36m${scopeStr}${entityStr}\x1B[0m` : ""}`,
798
+ `${pkgLabel} ${score}${entityStr ? ` \x1B[36m${scopeStr}${entityStr}\x1B[0m` : ""}`,
788
799
  `\x1B[90m${refPath}:${lineRange}\x1B[0m`,
789
800
  ` ${highlighted.replace(/\n/g, "\n ")}`
790
801
  ].join("\n");
@@ -3428,35 +3439,56 @@ const removeCommandDef = defineCommand({
3428
3439
  });
3429
3440
  var search_exports = /* @__PURE__ */ __exportAll({
3430
3441
  findPackageDbs: () => findPackageDbs,
3442
+ getPackageVersions: () => getPackageVersions,
3443
+ listLockPackages: () => listLockPackages,
3431
3444
  parseFilterPrefix: () => parseFilterPrefix,
3432
3445
  searchCommand: () => searchCommand,
3433
3446
  searchCommandDef: () => searchCommandDef
3434
3447
  });
3435
3448
  function findPackageDbs(packageFilter) {
3436
- const cwd = process.cwd();
3449
+ const lock = readProjectLock(process.cwd());
3450
+ if (!lock) return [];
3451
+ return filterLockDbs(lock, packageFilter);
3452
+ }
3453
+ function getPackageVersions(cwd = process.cwd()) {
3454
+ const lock = readProjectLock(cwd);
3455
+ const map = /* @__PURE__ */ new Map();
3456
+ if (!lock) return map;
3457
+ for (const s of Object.values(lock.skills)) if (s.packageName && s.version) map.set(s.packageName, s.version);
3458
+ return map;
3459
+ }
3460
+ function readProjectLock(cwd) {
3437
3461
  const shared = getSharedSkillsDir(cwd);
3438
3462
  if (shared) {
3439
3463
  const lock = readLock(shared);
3440
- if (lock) return filterLockDbs(lock, packageFilter);
3464
+ if (lock) return lock;
3441
3465
  }
3442
3466
  const agent = detectTargetAgent();
3443
- if (!agent) return [];
3444
- const lock = readLock(`${cwd}/${targets[agent].skillsDir}`);
3467
+ if (!agent) return null;
3468
+ return readLock(`${cwd}/${targets[agent].skillsDir}`);
3469
+ }
3470
+ function listLockPackages(cwd = process.cwd()) {
3471
+ const lock = readProjectLock(cwd);
3445
3472
  if (!lock) return [];
3446
- return filterLockDbs(lock, packageFilter);
3473
+ const seen = /* @__PURE__ */ new Map();
3474
+ for (const s of Object.values(lock.skills)) if (s.packageName && s.version) seen.set(s.packageName, s.version);
3475
+ return [...seen].map(([name, version]) => `${name}@${version}`);
3447
3476
  }
3448
3477
  function filterLockDbs(lock, packageFilter) {
3449
3478
  if (!lock) return [];
3450
- const normalize = (s) => s.toLowerCase().replace(/[-_@/]/g, "");
3479
+ const tokenize = (s) => s.toLowerCase().replace(/@/g, "").split(/[-_/]+/).filter(Boolean);
3451
3480
  return Object.values(lock.skills).filter((info) => {
3452
3481
  if (!info.packageName || !info.version) return false;
3453
3482
  if (!packageFilter) return true;
3454
- const f = normalize(packageFilter);
3455
- return normalize(info.packageName).includes(f) || normalize(info.packageName) === f;
3483
+ const filterTokens = tokenize(packageFilter);
3484
+ const nameTokens = tokenize(info.packageName);
3485
+ return filterTokens.every((ft) => nameTokens.some((nt) => nt.includes(ft) || ft.includes(nt)));
3456
3486
  }).map((info) => {
3457
3487
  const exact = getPackageDbPath(info.packageName, info.version);
3458
3488
  if (existsSync(exact)) return exact;
3459
- return findAnyPackageDb(info.packageName);
3489
+ const fallback = findAnyPackageDb(info.packageName);
3490
+ if (fallback) p.log.warn(`Using cached search index for ${info.packageName} (v${info.version} not indexed). Run \`skilld update ${info.packageName}\` to re-index.`);
3491
+ return fallback;
3460
3492
  }).filter((db) => !!db);
3461
3493
  }
3462
3494
  function findAnyPackageDb(name) {
@@ -3499,23 +3531,36 @@ function parseFilterPrefix(rawQuery) {
3499
3531
  }
3500
3532
  async function searchCommand(rawQuery, packageFilter) {
3501
3533
  const dbs = findPackageDbs(packageFilter);
3534
+ const versions = getPackageVersions();
3502
3535
  if (dbs.length === 0) {
3503
- if (packageFilter) p.log.warn(`No docs indexed for "${packageFilter}". Run \`skilld add ${packageFilter}\` first.`);
3504
- else p.log.warn("No docs indexed yet. Run `skilld add <package>` first.");
3536
+ if (packageFilter) {
3537
+ const available = listLockPackages();
3538
+ if (available.length > 0) p.log.warn(`No docs indexed for "${packageFilter}". Available: ${available.join(", ")}`);
3539
+ else p.log.warn(`No docs indexed for "${packageFilter}". Run \`skilld add ${packageFilter}\` first.`);
3540
+ } else p.log.warn("No docs indexed yet. Run `skilld add <package>` first.");
3505
3541
  return;
3506
3542
  }
3507
3543
  const { query, filter } = parseFilterPrefix(rawQuery);
3508
3544
  const start = performance.now();
3509
- const merged = (await Promise.all(dbs.map((dbPath) => searchSnippets(query, { dbPath }, {
3510
- limit: filter ? 10 : 5,
3545
+ const allResults = await Promise.all(dbs.map((dbPath) => searchSnippets(query, { dbPath }, {
3546
+ limit: filter ? 20 : 10,
3511
3547
  filter
3512
- })))).flat().sort((a, b) => b.score - a.score).slice(0, 5);
3548
+ })));
3549
+ const seen = /* @__PURE__ */ new Set();
3550
+ const merged = allResults.flat().sort((a, b) => b.score - a.score).filter((r) => {
3551
+ const key = `${r.source}:${r.lineStart}-${r.lineEnd}`;
3552
+ if (seen.has(key)) return false;
3553
+ seen.add(key);
3554
+ return true;
3555
+ }).slice(0, 5);
3513
3556
  const elapsed = ((performance.now() - start) / 1e3).toFixed(2);
3514
3557
  if (merged.length === 0) {
3515
3558
  p.log.warn(`No results for "${query}"`);
3516
3559
  return;
3517
3560
  }
3518
- const output = sanitizeMarkdown(merged.map((r) => formatSnippet(r)).join("\n\n"));
3561
+ for (const r of merged) r.content = sanitizeMarkdown(r.content);
3562
+ const scores = normalizeScores(merged);
3563
+ const output = merged.map((r) => formatSnippet(r, versions, scores.get(r))).join("\n\n");
3519
3564
  const summary = `${merged.length} results (${elapsed}s)`;
3520
3565
  if (!!detectCurrentAgent()) {
3521
3566
  const sanitized = output.replace(/<\/search-results>/gi, "&lt;/search-results&gt;");
@@ -3563,11 +3608,6 @@ function filterToSearchFilter(label) {
3563
3608
  if (label === "releases") return { type: "release" };
3564
3609
  return { type: { $in: ["doc", "docs"] } };
3565
3610
  }
3566
- function scoreColor(score) {
3567
- if (score >= .7) return "\x1B[32m";
3568
- if (score >= .4) return "\x1B[33m";
3569
- return "\x1B[90m";
3570
- }
3571
3611
  const SPINNER_FRAMES = [
3572
3612
  "◐",
3573
3613
  "◓",
@@ -3576,8 +3616,13 @@ const SPINNER_FRAMES = [
3576
3616
  ];
3577
3617
  async function interactiveSearch(packageFilter) {
3578
3618
  const dbs = findPackageDbs(packageFilter);
3619
+ const versions = getPackageVersions();
3579
3620
  if (dbs.length === 0) {
3580
- const msg = packageFilter ? `No docs indexed for "${packageFilter}". Run \`skilld add ${packageFilter}\` first.` : "No docs indexed yet. Run `skilld add <package>` first.";
3621
+ let msg;
3622
+ if (packageFilter) {
3623
+ const available = listLockPackages();
3624
+ msg = available.length > 0 ? `No docs indexed for "${packageFilter}". Available: ${available.join(", ")}` : `No docs indexed for "${packageFilter}". Run \`skilld add ${packageFilter}\` first.`;
3625
+ } else msg = "No docs indexed yet. Run `skilld add <package>` first.";
3581
3626
  process.stderr.write(`\x1B[33m${msg}\x1B[0m\n`);
3582
3627
  return;
3583
3628
  }
@@ -3628,18 +3673,21 @@ async function interactiveSearch(packageFilter) {
3628
3673
  } else {
3629
3674
  lines.push("");
3630
3675
  const shown = results.slice(0, maxResults);
3676
+ const scores = normalizeScores(results);
3631
3677
  for (let i = 0; i < shown.length; i++) {
3632
3678
  const r = shown[i];
3633
3679
  const selected = i === selectedIndex;
3634
3680
  const bullet = selected ? "\x1B[36m●\x1B[0m" : "\x1B[90m○\x1B[0m";
3635
- const sc = scoreColor(r.score);
3681
+ const sc = scoreLabel(scores.get(r) ?? 0);
3636
3682
  const { title, path, preview } = formatCompactSnippet(r, cols);
3637
3683
  const highlighted = highlightTerms(preview, r.highlights);
3684
+ const ver = versions.get(r.package);
3685
+ const pkgLabel = ver ? `${r.package}@${ver}` : r.package;
3638
3686
  if (selected) {
3639
- lines.push(` ${bullet} \x1B[1m${r.package}\x1B[0m ${sc}${r.score.toFixed(2)}\x1B[0m \x1B[36m${title}\x1B[0m`);
3687
+ lines.push(` ${bullet} \x1B[1m${pkgLabel}\x1B[0m ${sc} \x1B[36m${title}\x1B[0m`);
3640
3688
  lines.push(` \x1B[90m${path}\x1B[0m`);
3641
3689
  lines.push(` ${highlighted}`);
3642
- } else lines.push(` ${bullet} \x1B[90m${r.package}\x1B[0m ${sc}${r.score.toFixed(2)}\x1B[0m \x1B[90m${title}\x1B[0m`);
3690
+ } else lines.push(` ${bullet} \x1B[90m${pkgLabel}\x1B[0m ${sc} \x1B[90m${title}\x1B[0m`);
3643
3691
  }
3644
3692
  }
3645
3693
  lines.push("");
@@ -3715,9 +3763,10 @@ async function interactiveSearch(packageFilter) {
3715
3763
  const refPath = `.claude/skills/${r.package}/.skilld/${r.source}`;
3716
3764
  const lineRange = r.lineStart === r.lineEnd ? `L${r.lineStart}` : `L${r.lineStart}-${r.lineEnd}`;
3717
3765
  const highlighted = highlightTerms(sanitizeMarkdown(r.content), r.highlights);
3766
+ const rVer = versions.get(r.package);
3718
3767
  const out = [
3719
3768
  "",
3720
- ` \x1B[1m${r.package}\x1B[0m ${scoreColor(r.score)}${r.score.toFixed(2)}\x1B[0m`,
3769
+ ` \x1B[1m${rVer ? `${r.package}@${rVer}` : r.package}\x1B[0m ${scoreLabel(normalizeScores(results).get(r) ?? 0)}`,
3721
3770
  ` \x1B[90m${refPath}:${lineRange}\x1B[0m`,
3722
3771
  "",
3723
3772
  ` ${highlighted.replace(/\n/g, "\n ")}`,