skilld 1.5.0 → 1.5.1

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.
Files changed (76) hide show
  1. package/dist/_chunks/agent.mjs +2 -2
  2. package/dist/_chunks/assemble.mjs +1 -0
  3. package/dist/_chunks/assemble.mjs.map +1 -1
  4. package/dist/_chunks/author.mjs +4 -2
  5. package/dist/_chunks/author.mjs.map +1 -1
  6. package/dist/_chunks/cache.mjs +2 -39
  7. package/dist/_chunks/cache.mjs.map +1 -1
  8. package/dist/_chunks/cache2.mjs +2 -1
  9. package/dist/_chunks/cache2.mjs.map +1 -1
  10. package/dist/_chunks/cli-helpers.mjs +6 -28
  11. package/dist/_chunks/cli-helpers.mjs.map +1 -1
  12. package/dist/_chunks/cli-helpers2.mjs +11 -0
  13. package/dist/_chunks/core.mjs +1 -0
  14. package/dist/_chunks/embedding-cache.mjs +3 -60
  15. package/dist/_chunks/embedding-cache2.mjs +61 -0
  16. package/dist/_chunks/embedding-cache2.mjs.map +1 -0
  17. package/dist/_chunks/index.d.mts +13 -21
  18. package/dist/_chunks/index.d.mts.map +1 -1
  19. package/dist/_chunks/index2.d.mts +32 -600
  20. package/dist/_chunks/index2.d.mts.map +1 -1
  21. package/dist/_chunks/index3.d.mts +615 -0
  22. package/dist/_chunks/index3.d.mts.map +1 -0
  23. package/dist/_chunks/install.mjs +6 -4
  24. package/dist/_chunks/install.mjs.map +1 -1
  25. package/dist/_chunks/list.mjs +2 -1
  26. package/dist/_chunks/list.mjs.map +1 -1
  27. package/dist/_chunks/pool.mjs +2 -123
  28. package/dist/_chunks/pool2.mjs +118 -0
  29. package/dist/_chunks/pool2.mjs.map +1 -0
  30. package/dist/_chunks/prepare.mjs +34 -78
  31. package/dist/_chunks/prepare.mjs.map +1 -1
  32. package/dist/_chunks/prepare2.mjs +93 -0
  33. package/dist/_chunks/prepare2.mjs.map +1 -0
  34. package/dist/_chunks/retriv.mjs +172 -0
  35. package/dist/_chunks/retriv.mjs.map +1 -0
  36. package/dist/_chunks/search-interactive.mjs +4 -3
  37. package/dist/_chunks/search-interactive.mjs.map +1 -1
  38. package/dist/_chunks/search.mjs +12 -320
  39. package/dist/_chunks/search2.mjs +319 -0
  40. package/dist/_chunks/search2.mjs.map +1 -0
  41. package/dist/_chunks/setup.mjs +3 -2
  42. package/dist/_chunks/setup.mjs.map +1 -1
  43. package/dist/_chunks/skills.mjs +1 -1
  44. package/dist/_chunks/sync-shared.mjs +2 -0
  45. package/dist/_chunks/sync-shared2.mjs +4 -3
  46. package/dist/_chunks/sync-shared2.mjs.map +1 -1
  47. package/dist/_chunks/sync.mjs +7 -7
  48. package/dist/_chunks/sync.mjs.map +1 -1
  49. package/dist/_chunks/sync2.mjs +21 -0
  50. package/dist/_chunks/uninstall.mjs +5 -2
  51. package/dist/_chunks/uninstall.mjs.map +1 -1
  52. package/dist/_chunks/wizard.mjs +186 -0
  53. package/dist/_chunks/wizard.mjs.map +1 -0
  54. package/dist/agent/index.mjs +1 -0
  55. package/dist/cache/index.d.mts +1 -1
  56. package/dist/cache/index.mjs +2 -1
  57. package/dist/cli-entry.d.mts +1 -0
  58. package/dist/cli-entry.mjs +11 -0
  59. package/dist/cli-entry.mjs.map +1 -0
  60. package/dist/cli.mjs +12 -188
  61. package/dist/cli.mjs.map +1 -1
  62. package/dist/index.d.mts +3 -3
  63. package/dist/index.mjs +3 -2
  64. package/dist/prepare.d.mts +1 -0
  65. package/dist/prepare.mjs +93 -0
  66. package/dist/prepare.mjs.map +1 -0
  67. package/dist/retriv/index.d.mts +2 -46
  68. package/dist/retriv/index.mjs +2 -171
  69. package/dist/sources/index.d.mts +1 -1
  70. package/dist/types.d.mts +1 -1
  71. package/package.json +1 -1
  72. package/dist/_chunks/embedding-cache.mjs.map +0 -1
  73. package/dist/_chunks/pool.mjs.map +0 -1
  74. package/dist/_chunks/search.mjs.map +0 -1
  75. package/dist/retriv/index.d.mts.map +0 -1
  76. package/dist/retriv/index.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"prepare.mjs","names":["agents"],"sources":["../../src/commands/prepare.ts"],"sourcesContent":["/**\n * Prepare command — lightweight hook for package.json \"prepare\" script.\n *\n * Designed to run on every `pnpm install` / `npm install`. Blocking, fast, no LLM calls.\n * 1. Restore broken symlinks from lockfile (like `install` but skips doc fetching)\n * 2. Auto-install shipped skills from deps (just symlinks + lockfile writes)\n * 3. Report outdated skills count and suggest `skilld update`\n */\n\nimport type { SkillInfo } from '../core/lockfile.ts'\nimport { existsSync, mkdirSync, symlinkSync } from 'node:fs'\nimport * as p from '@clack/prompts'\nimport { defineCommand } from 'citty'\nimport { join } from 'pathe'\nimport { agents, linkSkillToAgents } from '../agent/index.ts'\nimport { getShippedSkills, linkShippedSkill, resolvePkgDir } from '../cache/index.ts'\nimport { resolveAgent } from '../cli-helpers.ts'\nimport { mergeLocks, readLock, writeLock } from '../core/lockfile.ts'\nimport { getSharedSkillsDir } from '../core/shared.ts'\nimport { getProjectState } from '../core/skills.ts'\n\nexport const prepareCommandDef = defineCommand({\n meta: { name: 'prepare', description: 'Restore references and sync shipped skills (for package.json hooks)' },\n args: {\n agent: {\n type: 'enum' as const,\n options: Object.keys(agents),\n alias: 'a',\n description: 'Target agent',\n },\n },\n async run({ args }) {\n const cwd = process.cwd()\n\n const agent = resolveAgent(args.agent)\n if (!agent || agent === 'none')\n return\n\n const agentConfig = agents[agent]\n const shared = getSharedSkillsDir(cwd)\n const skillsDir = shared || join(cwd, agentConfig.skillsDir)\n\n // ── 1. Restore broken symlinks from lockfile ──\n\n const allSkillsDirs = shared\n ? [shared]\n : Object.values(agents).map(t => join(cwd, t.skillsDir))\n const allLocks = allSkillsDirs\n .map(dir => readLock(dir))\n .filter((l): l is NonNullable<typeof l> => !!l && Object.keys(l.skills).length > 0)\n\n if (allLocks.length > 0) {\n const lock = mergeLocks(allLocks)\n\n for (const [name, info] of Object.entries(lock.skills)) {\n if (!info.version)\n continue\n\n if (info.source === 'shipped') {\n const skillDir = join(skillsDir, name)\n if (!existsSync(skillDir)) {\n const pkgName = info.packageName || name\n const shipped = getShippedSkills(pkgName, cwd, info.version)\n const match = shipped.find(s => s.skillName === name)\n if (match)\n linkShippedSkill(skillsDir, name, match.skillDir)\n }\n continue\n }\n\n // Non-shipped: restore .skilld/pkg symlink if broken\n restorePkgSymlink(skillsDir, name, info, cwd)\n }\n }\n\n // ── 2. Auto-install shipped skills from deps ──\n\n const state = await getProjectState(cwd)\n let shippedCount = 0\n\n if (state.shipped.length > 0) {\n mkdirSync(skillsDir, { recursive: true })\n\n for (const entry of state.shipped) {\n const version = state.deps.get(entry.packageName)?.replace(/^[\\^~>=<]+/, '') || '0.0.0'\n\n for (const skill of entry.skills) {\n linkShippedSkill(skillsDir, skill.skillName, skill.skillDir)\n writeLock(skillsDir, skill.skillName, {\n packageName: entry.packageName,\n version,\n source: 'shipped',\n syncedAt: new Date().toISOString().split('T')[0],\n generator: 'skilld',\n })\n\n if (shared)\n linkSkillToAgents(skill.skillName, shared, cwd, agent)\n\n shippedCount++\n }\n }\n\n if (shippedCount > 0)\n p.log.success(`Installed ${shippedCount} shipped skill${shippedCount > 1 ? 's' : ''}`)\n }\n\n // ── 3. Report outdated skills ──\n\n // Re-read state after shipped installs so they don't show as missing\n const freshState = shippedCount > 0 ? await getProjectState(cwd) : state\n\n if (freshState.outdated.length > 0) {\n const n = freshState.outdated.length\n p.log.info(`${n} package${n > 1 ? 's' : ''} ha${n > 1 ? 've' : 's'} new features and/or breaking changes. Run \\`skilld update\\` to sync.`)\n }\n },\n})\n\n/** Restore .skilld/pkg symlink to node_modules if broken */\nfunction restorePkgSymlink(skillsDir: string, name: string, info: SkillInfo, cwd: string): void {\n const refsDir = join(skillsDir, name, '.skilld')\n const pkgLink = join(refsDir, 'pkg')\n\n // Only fix if the skill dir exists but the pkg symlink is broken\n if (!existsSync(join(skillsDir, name)))\n return\n\n if (existsSync(pkgLink))\n return\n\n const pkgName = info.packageName || name\n const pkgDir = resolvePkgDir(pkgName, cwd, info.version)\n if (!pkgDir)\n return\n\n mkdirSync(refsDir, { recursive: true })\n symlinkSync(pkgDir, pkgLink)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqBA,MAAa,oBAAoB,cAAc;CAC7C,MAAM;EAAE,MAAM;EAAW,aAAa;EAAuE;CAC7G,MAAM,EACJ,OAAO;EACL,MAAM;EACN,SAAS,OAAO,KAAKA,QAAO;EAC5B,OAAO;EACP,aAAa;EACd,EACF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,MAAM,QAAQ,KAAK;EAEzB,MAAM,QAAQ,aAAa,KAAK,MAAM;AACtC,MAAI,CAAC,SAAS,UAAU,OACtB;EAEF,MAAM,cAAcA,QAAO;EAC3B,MAAM,SAAS,mBAAmB,IAAI;EACtC,MAAM,YAAY,UAAU,KAAK,KAAK,YAAY,UAAU;EAO5D,MAAM,YAHgB,SAClB,CAAC,OAAO,GACR,OAAO,OAAOA,QAAO,CAAC,KAAI,MAAK,KAAK,KAAK,EAAE,UAAU,CAAC,EAEvD,KAAI,QAAO,SAAS,IAAI,CAAC,CACzB,QAAQ,MAAkC,CAAC,CAAC,KAAK,OAAO,KAAK,EAAE,OAAO,CAAC,SAAS,EAAE;AAErF,MAAI,SAAS,SAAS,GAAG;GACvB,MAAM,OAAO,WAAW,SAAS;AAEjC,QAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,KAAK,OAAO,EAAE;AACtD,QAAI,CAAC,KAAK,QACR;AAEF,QAAI,KAAK,WAAW,WAAW;AAE7B,SAAI,CAAC,WADY,KAAK,WAAW,KAAK,CACb,EAAE;MAGzB,MAAM,QADU,iBADA,KAAK,eAAe,MACM,KAAK,KAAK,QAAQ,CACtC,MAAK,MAAK,EAAE,cAAc,KAAK;AACrD,UAAI,MACF,kBAAiB,WAAW,MAAM,MAAM,SAAS;;AAErD;;AAIF,sBAAkB,WAAW,MAAM,MAAM,IAAI;;;EAMjD,MAAM,QAAQ,MAAM,gBAAgB,IAAI;EACxC,IAAI,eAAe;AAEnB,MAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,aAAU,WAAW,EAAE,WAAW,MAAM,CAAC;AAEzC,QAAK,MAAM,SAAS,MAAM,SAAS;IACjC,MAAM,UAAU,MAAM,KAAK,IAAI,MAAM,YAAY,EAAE,QAAQ,cAAc,GAAG,IAAI;AAEhF,SAAK,MAAM,SAAS,MAAM,QAAQ;AAChC,sBAAiB,WAAW,MAAM,WAAW,MAAM,SAAS;AAC5D,eAAU,WAAW,MAAM,WAAW;MACpC,aAAa,MAAM;MACnB;MACA,QAAQ;MACR,2BAAU,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,IAAI,CAAC;MAC9C,WAAW;MACZ,CAAC;AAEF,SAAI,OACF,mBAAkB,MAAM,WAAW,QAAQ,KAAK,MAAM;AAExD;;;AAIJ,OAAI,eAAe,EACjB,GAAE,IAAI,QAAQ,aAAa,aAAa,gBAAgB,eAAe,IAAI,MAAM,KAAK;;EAM1F,MAAM,aAAa,eAAe,IAAI,MAAM,gBAAgB,IAAI,GAAG;AAEnE,MAAI,WAAW,SAAS,SAAS,GAAG;GAClC,MAAM,IAAI,WAAW,SAAS;AAC9B,KAAE,IAAI,KAAK,GAAG,EAAE,UAAU,IAAI,IAAI,MAAM,GAAG,KAAK,IAAI,IAAI,OAAO,IAAI,uEAAuE;;;CAG/I,CAAC;;AAGF,SAAS,kBAAkB,WAAmB,MAAc,MAAiB,KAAmB;CAC9F,MAAM,UAAU,KAAK,WAAW,MAAM,UAAU;CAChD,MAAM,UAAU,KAAK,SAAS,MAAM;AAGpC,KAAI,CAAC,WAAW,KAAK,WAAW,KAAK,CAAC,CACpC;AAEF,KAAI,WAAW,QAAQ,CACrB;CAGF,MAAM,SAAS,cADC,KAAK,eAAe,MACE,KAAK,KAAK,QAAQ;AACxD,KAAI,CAAC,OACH;AAEF,WAAU,SAAS,EAAE,WAAW,MAAM,CAAC;AACvC,aAAY,QAAQ,QAAQ"}
1
+ {"version":3,"file":"prepare.mjs","names":[],"sources":["../../src/core/prepare.ts"],"sourcesContent":["/**\n * Shared prepare utilities used by both the fast entry (src/prepare.ts)\n * and the full CLI command (src/commands/prepare.ts).\n *\n * Keep this module lightweight: no imports from agent/, cache/storage.ts,\n * or any module that pulls in sanitize/clack/citty.\n */\n\nimport type { SkillInfo } from './lockfile.ts'\nimport { existsSync, lstatSync, mkdirSync, readdirSync, rmSync, symlinkSync, unlinkSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { getCacheDir } from '../cache/version.ts'\n\n/** Resolve package directory: node_modules first, then global cache */\nexport function resolvePkgDir(name: string, cwd: string, version?: string): string | null {\n const nodeModulesPath = join(cwd, 'node_modules', name)\n if (existsSync(nodeModulesPath))\n return nodeModulesPath\n\n if (version) {\n const cachedPkgDir = join(getCacheDir(name, version), 'pkg')\n if (existsSync(join(cachedPkgDir, 'package.json')))\n return cachedPkgDir\n }\n\n return null\n}\n\n/** Restore .skilld/pkg symlink to node_modules if broken */\nexport function restorePkgSymlink(skillsDir: string, name: string, info: SkillInfo, cwd: string): void {\n const refsDir = join(skillsDir, name, '.skilld')\n const pkgLink = join(refsDir, 'pkg')\n\n if (!existsSync(join(skillsDir, name)))\n return\n\n if (existsSync(pkgLink))\n return\n\n const pkgName = info.packageName || name\n const pkgDir = resolvePkgDir(pkgName, cwd, info.version)\n if (!pkgDir)\n return\n\n mkdirSync(refsDir, { recursive: true })\n symlinkSync(pkgDir, pkgLink)\n}\n\nexport interface ShippedSkill {\n skillName: string\n skillDir: string\n}\n\n/** Check if package ships a skills/ directory with SKILL.md or _SKILL.md subdirs */\nexport function getShippedSkills(name: string, cwd: string, version?: string): ShippedSkill[] {\n const pkgPath = resolvePkgDir(name, cwd, version)\n if (!pkgPath)\n return []\n\n const skillsPath = join(pkgPath, 'skills')\n if (!existsSync(skillsPath))\n return []\n\n return readdirSync(skillsPath, { withFileTypes: true })\n .filter(d => d.isDirectory() && (existsSync(join(skillsPath, d.name, 'SKILL.md')) || existsSync(join(skillsPath, d.name, '_SKILL.md'))))\n .map(d => ({ skillName: d.name, skillDir: join(skillsPath, d.name) }))\n}\n\n/** Create symlink from skills dir to shipped skill dir */\nexport function linkShippedSkill(baseDir: string, skillName: string, targetDir: string): void {\n const linkPath = join(baseDir, skillName)\n if (existsSync(linkPath)) {\n const stat = lstatSync(linkPath)\n if (stat.isSymbolicLink())\n unlinkSync(linkPath)\n else rmSync(linkPath, { recursive: true, force: true })\n }\n symlinkSync(targetDir, linkPath)\n}\n"],"mappings":";;;;;AAcA,SAAgB,cAAc,MAAc,KAAa,SAAiC;CACxF,MAAM,kBAAkB,KAAK,KAAK,gBAAgB,KAAK;AACvD,KAAI,WAAW,gBAAgB,CAC7B,QAAO;AAET,KAAI,SAAS;EACX,MAAM,eAAe,KAAK,YAAY,MAAM,QAAQ,EAAE,MAAM;AAC5D,MAAI,WAAW,KAAK,cAAc,eAAe,CAAC,CAChD,QAAO;;AAGX,QAAO;;;AAIT,SAAgB,kBAAkB,WAAmB,MAAc,MAAiB,KAAmB;CACrG,MAAM,UAAU,KAAK,WAAW,MAAM,UAAU;CAChD,MAAM,UAAU,KAAK,SAAS,MAAM;AAEpC,KAAI,CAAC,WAAW,KAAK,WAAW,KAAK,CAAC,CACpC;AAEF,KAAI,WAAW,QAAQ,CACrB;CAGF,MAAM,SAAS,cADC,KAAK,eAAe,MACE,KAAK,KAAK,QAAQ;AACxD,KAAI,CAAC,OACH;AAEF,WAAU,SAAS,EAAE,WAAW,MAAM,CAAC;AACvC,aAAY,QAAQ,QAAQ;;;AAS9B,SAAgB,iBAAiB,MAAc,KAAa,SAAkC;CAC5F,MAAM,UAAU,cAAc,MAAM,KAAK,QAAQ;AACjD,KAAI,CAAC,QACH,QAAO,EAAE;CAEX,MAAM,aAAa,KAAK,SAAS,SAAS;AAC1C,KAAI,CAAC,WAAW,WAAW,CACzB,QAAO,EAAE;AAEX,QAAO,YAAY,YAAY,EAAE,eAAe,MAAM,CAAC,CACpD,QAAO,MAAK,EAAE,aAAa,KAAK,WAAW,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC,IAAI,WAAW,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC,EAAE,CACvI,KAAI,OAAM;EAAE,WAAW,EAAE;EAAM,UAAU,KAAK,YAAY,EAAE,KAAA;EAAO,EAAE;;;AAI1E,SAAgB,iBAAiB,SAAiB,WAAmB,WAAyB;CAC5F,MAAM,WAAW,KAAK,SAAS,UAAU;AACzC,KAAI,WAAW,SAAS,CAEtB,KADa,UAAU,SAAS,CACvB,gBAAgB,CACvB,YAAW,SAAS;KACjB,QAAO,UAAU;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC;AAEzD,aAAY,WAAW,SAAS"}
@@ -0,0 +1,93 @@
1
+ import "./agent.mjs";
2
+ import "./config.mjs";
3
+ import { i as restorePkgSymlink, n as linkShippedSkill, t as getShippedSkills } from "./prepare.mjs";
4
+ import "./sanitize.mjs";
5
+ import "./cache.mjs";
6
+ import "./yaml.mjs";
7
+ import "./markdown.mjs";
8
+ import { n as getSharedSkillsDir } from "./shared.mjs";
9
+ import "./sources.mjs";
10
+ import { a as targets } from "./detect.mjs";
11
+ import { i as linkSkillToAgents } from "./prompts.mjs";
12
+ import { y as resolveAgent } from "./cli-helpers.mjs";
13
+ import { i as readLock, s as writeLock } from "./lockfile.mjs";
14
+ import { t as getProjectState } from "./skills.mjs";
15
+ import { join } from "pathe";
16
+ import { existsSync, mkdirSync } from "node:fs";
17
+ import * as p from "@clack/prompts";
18
+ import { defineCommand } from "citty";
19
+ //#region src/commands/prepare.ts
20
+ /**
21
+ * Prepare command — lightweight hook for package.json "prepare" script.
22
+ *
23
+ * Designed to run on every `pnpm install` / `npm install`. Blocking, fast, no LLM calls.
24
+ * 1. Restore broken symlinks from lockfile (like `install` but skips doc fetching)
25
+ * 2. Auto-install shipped skills from deps (just symlinks + lockfile writes)
26
+ * 3. Report outdated skills count and suggest `skilld update`
27
+ */
28
+ const prepareCommandDef = defineCommand({
29
+ meta: {
30
+ name: "prepare",
31
+ description: "Restore references and sync shipped skills (for package.json hooks)"
32
+ },
33
+ args: { agent: {
34
+ type: "enum",
35
+ options: Object.keys(targets),
36
+ alias: "a",
37
+ description: "Target agent"
38
+ } },
39
+ async run({ args }) {
40
+ const cwd = process.cwd();
41
+ const agent = resolveAgent(args.agent);
42
+ if (!agent || agent === "none") return;
43
+ const agentConfig = targets[agent];
44
+ const shared = getSharedSkillsDir(cwd);
45
+ const skillsDir = shared || join(cwd, agentConfig.skillsDir);
46
+ const lock = readLock(skillsDir);
47
+ if (lock && Object.keys(lock.skills).length > 0) {
48
+ let allIntact = true;
49
+ for (const [name, info] of Object.entries(lock.skills)) {
50
+ if (!info.version) continue;
51
+ if (existsSync(join(skillsDir, name))) {
52
+ if (info.source !== "shipped") restorePkgSymlink(skillsDir, name, info, cwd);
53
+ continue;
54
+ }
55
+ allIntact = false;
56
+ if (info.source === "shipped") {
57
+ const match = getShippedSkills(info.packageName || name, cwd, info.version).find((s) => s.skillName === name);
58
+ if (match) linkShippedSkill(skillsDir, name, match.skillDir);
59
+ }
60
+ }
61
+ if (allIntact) return;
62
+ }
63
+ const state = await getProjectState(cwd);
64
+ let shippedCount = 0;
65
+ if (state.shipped.length > 0) {
66
+ mkdirSync(skillsDir, { recursive: true });
67
+ for (const entry of state.shipped) {
68
+ const version = state.deps.get(entry.packageName)?.replace(/^[\^~>=<]+/, "") || "0.0.0";
69
+ for (const skill of entry.skills) {
70
+ linkShippedSkill(skillsDir, skill.skillName, skill.skillDir);
71
+ writeLock(skillsDir, skill.skillName, {
72
+ packageName: entry.packageName,
73
+ version,
74
+ source: "shipped",
75
+ syncedAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
76
+ generator: "skilld"
77
+ });
78
+ if (shared) linkSkillToAgents(skill.skillName, shared, cwd, agent);
79
+ shippedCount++;
80
+ }
81
+ }
82
+ if (shippedCount > 0) p.log.success(`Installed ${shippedCount} shipped skill${shippedCount > 1 ? "s" : ""}`);
83
+ }
84
+ if (state.outdated.length > 0) {
85
+ const n = state.outdated.length;
86
+ p.log.info(`${n} package${n > 1 ? "s" : ""} ha${n > 1 ? "ve" : "s"} new features and/or breaking changes. Run \`skilld update\` to sync.`);
87
+ }
88
+ }
89
+ });
90
+ //#endregion
91
+ export { prepareCommandDef };
92
+
93
+ //# sourceMappingURL=prepare2.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prepare2.mjs","names":["agents"],"sources":["../../src/commands/prepare.ts"],"sourcesContent":["/**\n * Prepare command — lightweight hook for package.json \"prepare\" script.\n *\n * Designed to run on every `pnpm install` / `npm install`. Blocking, fast, no LLM calls.\n * 1. Restore broken symlinks from lockfile (like `install` but skips doc fetching)\n * 2. Auto-install shipped skills from deps (just symlinks + lockfile writes)\n * 3. Report outdated skills count and suggest `skilld update`\n */\n\nimport { existsSync, mkdirSync } from 'node:fs'\nimport * as p from '@clack/prompts'\nimport { defineCommand } from 'citty'\nimport { join } from 'pathe'\nimport { agents, linkSkillToAgents } from '../agent/index.ts'\nimport { resolveAgent } from '../cli-helpers.ts'\nimport { readLock, writeLock } from '../core/lockfile.ts'\nimport { getShippedSkills, linkShippedSkill, restorePkgSymlink } from '../core/prepare.ts'\nimport { getSharedSkillsDir } from '../core/shared.ts'\nimport { getProjectState } from '../core/skills.ts'\n\nexport const prepareCommandDef = defineCommand({\n meta: { name: 'prepare', description: 'Restore references and sync shipped skills (for package.json hooks)' },\n args: {\n agent: {\n type: 'enum' as const,\n options: Object.keys(agents),\n alias: 'a',\n description: 'Target agent',\n },\n },\n async run({ args }) {\n const cwd = process.cwd()\n\n const agent = resolveAgent(args.agent)\n if (!agent || agent === 'none')\n return\n\n const agentConfig = agents[agent]\n const shared = getSharedSkillsDir(cwd)\n const skillsDir = shared || join(cwd, agentConfig.skillsDir)\n\n // ── Fast path: read primary lockfile, check all skills intact ──\n\n const lock = readLock(skillsDir)\n if (lock && Object.keys(lock.skills).length > 0) {\n let allIntact = true\n\n for (const [name, info] of Object.entries(lock.skills)) {\n if (!info.version)\n continue\n\n const skillDir = join(skillsDir, name)\n if (existsSync(skillDir)) {\n // Skill dir exists; for non-shipped, also check .skilld/pkg symlink\n if (info.source !== 'shipped')\n restorePkgSymlink(skillsDir, name, info, cwd)\n continue\n }\n\n // Skill dir missing, needs restore\n allIntact = false\n\n if (info.source === 'shipped') {\n const pkgName = info.packageName || name\n const shipped = getShippedSkills(pkgName, cwd, info.version)\n const match = shipped.find(s => s.skillName === name)\n if (match)\n linkShippedSkill(skillsDir, name, match.skillDir)\n }\n }\n\n // If all skills intact, skip expensive getProjectState entirely\n if (allIntact)\n return\n }\n\n // ── Slow path: discover new shipped skills + report outdated ──\n\n const state = await getProjectState(cwd)\n let shippedCount = 0\n\n if (state.shipped.length > 0) {\n mkdirSync(skillsDir, { recursive: true })\n\n for (const entry of state.shipped) {\n const version = state.deps.get(entry.packageName)?.replace(/^[\\^~>=<]+/, '') || '0.0.0'\n\n for (const skill of entry.skills) {\n linkShippedSkill(skillsDir, skill.skillName, skill.skillDir)\n writeLock(skillsDir, skill.skillName, {\n packageName: entry.packageName,\n version,\n source: 'shipped',\n syncedAt: new Date().toISOString().split('T')[0],\n generator: 'skilld',\n })\n\n if (shared)\n linkSkillToAgents(skill.skillName, shared, cwd, agent)\n\n shippedCount++\n }\n }\n\n if (shippedCount > 0)\n p.log.success(`Installed ${shippedCount} shipped skill${shippedCount > 1 ? 's' : ''}`)\n }\n\n if (state.outdated.length > 0) {\n const n = state.outdated.length\n p.log.info(`${n} package${n > 1 ? 's' : ''} ha${n > 1 ? 've' : 's'} new features and/or breaking changes. Run \\`skilld update\\` to sync.`)\n }\n },\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,oBAAoB,cAAc;CAC7C,MAAM;EAAE,MAAM;EAAW,aAAa;EAAuE;CAC7G,MAAM,EACJ,OAAO;EACL,MAAM;EACN,SAAS,OAAO,KAAKA,QAAO;EAC5B,OAAO;EACP,aAAa;EACd,EACF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,MAAM,QAAQ,KAAK;EAEzB,MAAM,QAAQ,aAAa,KAAK,MAAM;AACtC,MAAI,CAAC,SAAS,UAAU,OACtB;EAEF,MAAM,cAAcA,QAAO;EAC3B,MAAM,SAAS,mBAAmB,IAAI;EACtC,MAAM,YAAY,UAAU,KAAK,KAAK,YAAY,UAAU;EAI5D,MAAM,OAAO,SAAS,UAAU;AAChC,MAAI,QAAQ,OAAO,KAAK,KAAK,OAAO,CAAC,SAAS,GAAG;GAC/C,IAAI,YAAY;AAEhB,QAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,KAAK,OAAO,EAAE;AACtD,QAAI,CAAC,KAAK,QACR;AAGF,QAAI,WADa,KAAK,WAAW,KAAK,CACd,EAAE;AAExB,SAAI,KAAK,WAAW,UAClB,mBAAkB,WAAW,MAAM,MAAM,IAAI;AAC/C;;AAIF,gBAAY;AAEZ,QAAI,KAAK,WAAW,WAAW;KAG7B,MAAM,QADU,iBADA,KAAK,eAAe,MACM,KAAK,KAAK,QAAQ,CACtC,MAAK,MAAK,EAAE,cAAc,KAAK;AACrD,SAAI,MACF,kBAAiB,WAAW,MAAM,MAAM,SAAS;;;AAKvD,OAAI,UACF;;EAKJ,MAAM,QAAQ,MAAM,gBAAgB,IAAI;EACxC,IAAI,eAAe;AAEnB,MAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,aAAU,WAAW,EAAE,WAAW,MAAM,CAAC;AAEzC,QAAK,MAAM,SAAS,MAAM,SAAS;IACjC,MAAM,UAAU,MAAM,KAAK,IAAI,MAAM,YAAY,EAAE,QAAQ,cAAc,GAAG,IAAI;AAEhF,SAAK,MAAM,SAAS,MAAM,QAAQ;AAChC,sBAAiB,WAAW,MAAM,WAAW,MAAM,SAAS;AAC5D,eAAU,WAAW,MAAM,WAAW;MACpC,aAAa,MAAM;MACnB;MACA,QAAQ;MACR,2BAAU,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,IAAI,CAAC;MAC9C,WAAW;MACZ,CAAC;AAEF,SAAI,OACF,mBAAkB,MAAM,WAAW,QAAQ,KAAK,MAAM;AAExD;;;AAIJ,OAAI,eAAe,EACjB,GAAE,IAAI,QAAQ,aAAa,aAAa,gBAAgB,eAAe,IAAI,MAAM,KAAK;;AAG1F,MAAI,MAAM,SAAS,SAAS,GAAG;GAC7B,MAAM,IAAI,MAAM,SAAS;AACzB,KAAE,IAAI,KAAK,GAAG,EAAE,UAAU,IAAI,IAAI,MAAM,GAAG,KAAK,IAAI,IAAI,OAAO,IAAI,uEAAuE;;;CAG/I,CAAC"}
@@ -0,0 +1,172 @@
1
+ import { a as stripFrontmatter } from "./markdown.mjs";
2
+ //#region src/retriv/index.ts
3
+ var SearchDepsUnavailableError = class extends Error {
4
+ constructor(cause) {
5
+ super("Search dependencies unavailable (sqlite-vec or retriv not installed). Search indexing skipped.");
6
+ this.name = "SearchDepsUnavailableError";
7
+ this.cause = cause;
8
+ }
9
+ };
10
+ async function getDb(config) {
11
+ let createRetriv, autoChunker, sqliteMod, sqliteVec, transformersJs, cachedEmbeddings;
12
+ try {
13
+ [{createRetriv}, {autoChunker}, sqliteMod, sqliteVec, {transformersJs}, {cachedEmbeddings}] = await Promise.all([
14
+ import("retriv"),
15
+ import("retriv/chunkers/auto"),
16
+ import("retriv/db/sqlite"),
17
+ import("sqlite-vec"),
18
+ import("retriv/embeddings/transformers-js"),
19
+ import("./embedding-cache.mjs")
20
+ ]);
21
+ } catch (err) {
22
+ if (err?.code === "ERR_MODULE_NOT_FOUND") throw new SearchDepsUnavailableError(err);
23
+ throw err;
24
+ }
25
+ const embeddings = await cachedEmbeddings(transformersJs());
26
+ return createRetriv({
27
+ driver: sqliteMod.default({
28
+ path: config.dbPath,
29
+ embeddings,
30
+ sqliteVec
31
+ }),
32
+ chunking: autoChunker()
33
+ });
34
+ }
35
+ /**
36
+ * Index documents in-process (no worker thread).
37
+ * Preferred for tests and environments where worker_threads is unreliable.
38
+ */
39
+ async function createIndexDirect(documents, config) {
40
+ const db = await getDb(config);
41
+ if (config.removeIds?.length) await db.remove?.(config.removeIds);
42
+ await db.index(documents, { onProgress: config.onProgress });
43
+ await db.close?.();
44
+ }
45
+ /**
46
+ * Index documents in a background worker thread.
47
+ * Falls back to direct indexing if worker fails to spawn.
48
+ */
49
+ async function createIndex(documents, config) {
50
+ const { createIndexInWorker } = await import("./pool.mjs");
51
+ return createIndexInWorker(documents, config);
52
+ }
53
+ /**
54
+ * List all raw document IDs in an existing index.
55
+ * Returns chunk IDs (e.g. "doc-id#chunk-0") for chunked docs.
56
+ * Queries sqlite directly to bypass createRetriv's parent-ID deduplication,
57
+ * so callers can use these IDs for exact removal and parent-ID grouping.
58
+ */
59
+ async function listIndexIds(config) {
60
+ const nodeSqlite = globalThis.process?.getBuiltinModule?.("node:sqlite");
61
+ if (!nodeSqlite) return [];
62
+ const db = new nodeSqlite.DatabaseSync(config.dbPath, {
63
+ open: true,
64
+ readOnly: true
65
+ });
66
+ try {
67
+ return db.prepare("SELECT id FROM documents_meta").all().map((r) => r.id);
68
+ } finally {
69
+ db.close();
70
+ }
71
+ }
72
+ /**
73
+ * Remove documents by ID from an existing index.
74
+ */
75
+ async function removeFromIndex(ids, config) {
76
+ if (ids.length === 0) return;
77
+ const db = await getDb(config);
78
+ await db.remove?.(ids);
79
+ await db.close?.();
80
+ }
81
+ async function search(query, config, options = {}) {
82
+ const { limit = 10, filter } = options;
83
+ const db = await getDb(config);
84
+ const results = await db.search(query, {
85
+ limit,
86
+ filter,
87
+ returnContent: true,
88
+ returnMetadata: true,
89
+ returnMeta: true
90
+ });
91
+ await db.close?.();
92
+ return results.map((r) => ({
93
+ id: r.id,
94
+ content: r.content ?? "",
95
+ score: r.score,
96
+ metadata: r.metadata ?? {},
97
+ highlights: r._meta?.highlights ?? [],
98
+ lineRange: r._chunk?.lineRange,
99
+ entities: r._chunk?.entities,
100
+ scope: r._chunk?.scope
101
+ }));
102
+ }
103
+ /**
104
+ * Search and return formatted snippets
105
+ */
106
+ async function searchSnippets(query, config, options = {}) {
107
+ return toSnippets(await search(query, config, options));
108
+ }
109
+ function toSnippets(results) {
110
+ return results.map((r) => {
111
+ const content = stripFrontmatter(r.content);
112
+ const source = r.metadata.source || r.id;
113
+ const lines = content.split("\n").length;
114
+ return {
115
+ package: r.metadata.package || "unknown",
116
+ source,
117
+ lineStart: r.lineRange?.[0] ?? 1,
118
+ lineEnd: r.lineRange?.[1] ?? lines,
119
+ content,
120
+ score: r.score,
121
+ highlights: r.highlights,
122
+ entities: r.entities,
123
+ scope: r.scope
124
+ };
125
+ });
126
+ }
127
+ async function openPool(dbPaths) {
128
+ const pool = /* @__PURE__ */ new Map();
129
+ await Promise.all(dbPaths.map(async (dbPath) => {
130
+ const db = await getDb({ dbPath });
131
+ pool.set(dbPath, db);
132
+ }));
133
+ return pool;
134
+ }
135
+ async function searchPooled(query, pool, options = {}) {
136
+ const { limit = 10, filter } = options;
137
+ const fetchLimit = limit * 2;
138
+ const allResults = await Promise.all(Array.from(pool.values(), async (db) => {
139
+ return (await db.search(query, {
140
+ limit: fetchLimit,
141
+ filter,
142
+ returnContent: true,
143
+ returnMetadata: true,
144
+ returnMeta: true
145
+ })).map((r) => ({
146
+ id: r.id,
147
+ content: r.content ?? "",
148
+ score: r.score,
149
+ metadata: r.metadata ?? {},
150
+ highlights: r._meta?.highlights ?? [],
151
+ lineRange: r._chunk?.lineRange,
152
+ entities: r._chunk?.entities,
153
+ scope: r._chunk?.scope
154
+ }));
155
+ }));
156
+ const seen = /* @__PURE__ */ new Set();
157
+ return toSnippets(allResults.flat().sort((a, b) => b.score - a.score).filter((r) => {
158
+ const lr = r.lineRange;
159
+ const key = `${r.metadata.source || r.id}:${lr?.[0]}-${lr?.[1]}`;
160
+ if (seen.has(key)) return false;
161
+ seen.add(key);
162
+ return true;
163
+ }).slice(0, limit));
164
+ }
165
+ async function closePool(pool) {
166
+ await Promise.all(Array.from(pool.values(), (db) => db.close?.()));
167
+ pool.clear();
168
+ }
169
+ //#endregion
170
+ export { getDb as a, removeFromIndex as c, searchSnippets as d, createIndexDirect as i, search as l, closePool as n, listIndexIds as o, createIndex as r, openPool as s, SearchDepsUnavailableError as t, searchPooled as u };
171
+
172
+ //# sourceMappingURL=retriv.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retriv.mjs","names":[],"sources":["../../src/retriv/index.ts"],"sourcesContent":["import type { ChunkEntity, Document, IndexConfig, IndexPhase, IndexProgress, SearchFilter, SearchOptions, SearchResult, SearchSnippet } from './types.ts'\nimport { stripFrontmatter } from '../core/markdown.ts'\n\nexport type { ChunkEntity, Document, IndexConfig, IndexPhase, IndexProgress, SearchFilter, SearchOptions, SearchResult, SearchSnippet }\n\ntype RetrivInstance = Awaited<ReturnType<typeof getDb>>\n\nexport class SearchDepsUnavailableError extends Error {\n constructor(cause: unknown) {\n super('Search dependencies unavailable (sqlite-vec or retriv not installed). Search indexing skipped.')\n this.name = 'SearchDepsUnavailableError'\n this.cause = cause\n }\n}\n\n// Dynamic imports: retriv/chunkers/auto eagerly loads typescript which may not be installed (e.g. npx)\nexport async function getDb(config: Pick<IndexConfig, 'dbPath'>) {\n let createRetriv, autoChunker, sqliteMod, sqliteVec, transformersJs, cachedEmbeddings\n try {\n ;([\n { createRetriv },\n { autoChunker },\n sqliteMod,\n sqliteVec,\n { transformersJs },\n { cachedEmbeddings },\n ] = await Promise.all([\n import('retriv'),\n import('retriv/chunkers/auto'),\n import('retriv/db/sqlite'),\n import('sqlite-vec'),\n import('retriv/embeddings/transformers-js'),\n import('./embedding-cache.ts'),\n ]))\n }\n catch (err: any) {\n if (err?.code === 'ERR_MODULE_NOT_FOUND')\n throw new SearchDepsUnavailableError(err)\n throw err\n }\n const embeddings = await cachedEmbeddings(transformersJs())\n return createRetriv({\n driver: sqliteMod.default({\n path: config.dbPath,\n embeddings,\n sqliteVec,\n }),\n chunking: autoChunker(),\n })\n}\n\n/**\n * Index documents in-process (no worker thread).\n * Preferred for tests and environments where worker_threads is unreliable.\n */\nexport async function createIndexDirect(\n documents: Document[],\n config: IndexConfig & { removeIds?: string[] },\n): Promise<void> {\n const db = await getDb(config)\n if (config.removeIds?.length)\n await db.remove?.(config.removeIds)\n await db.index(documents, { onProgress: config.onProgress })\n await db.close?.()\n}\n\n/**\n * Index documents in a background worker thread.\n * Falls back to direct indexing if worker fails to spawn.\n */\nexport async function createIndex(\n documents: Document[],\n config: IndexConfig & { removeIds?: string[] },\n): Promise<void> {\n // Dynamic import justified: search/searchSnippets shouldn't pull in worker_threads\n const { createIndexInWorker } = await import('./pool.ts')\n return createIndexInWorker(documents, config)\n}\n\n/**\n * List all raw document IDs in an existing index.\n * Returns chunk IDs (e.g. \"doc-id#chunk-0\") for chunked docs.\n * Queries sqlite directly to bypass createRetriv's parent-ID deduplication,\n * so callers can use these IDs for exact removal and parent-ID grouping.\n */\nexport async function listIndexIds(\n config: Pick<IndexConfig, 'dbPath'>,\n): Promise<string[]> {\n const nodeSqlite = globalThis.process?.getBuiltinModule?.('node:sqlite') as typeof import('node:sqlite') | undefined\n if (!nodeSqlite)\n return []\n const db = new nodeSqlite.DatabaseSync(config.dbPath, { open: true, readOnly: true })\n try {\n const rows = db.prepare('SELECT id FROM documents_meta').all() as Array<{ id: string }>\n return rows.map(r => r.id)\n }\n finally {\n db.close()\n }\n}\n\n/**\n * Remove documents by ID from an existing index.\n */\nexport async function removeFromIndex(\n ids: string[],\n config: Pick<IndexConfig, 'dbPath'>,\n): Promise<void> {\n if (ids.length === 0)\n return\n const db = await getDb(config)\n await db.remove?.(ids)\n await db.close?.()\n}\n\nexport async function search(\n query: string,\n config: IndexConfig,\n options: SearchOptions = {},\n): Promise<SearchResult[]> {\n const { limit = 10, filter } = options\n const db = await getDb(config)\n const results = await db.search(query, { limit, filter, returnContent: true, returnMetadata: true, returnMeta: true })\n await db.close?.()\n\n return results.map(r => ({\n id: r.id,\n content: r.content ?? '',\n score: r.score,\n metadata: r.metadata ?? {},\n highlights: r._meta?.highlights ?? [],\n lineRange: r._chunk?.lineRange,\n entities: r._chunk?.entities,\n scope: r._chunk?.scope,\n }))\n}\n\n/**\n * Search and return formatted snippets\n */\nexport async function searchSnippets(\n query: string,\n config: IndexConfig,\n options: SearchOptions = {},\n): Promise<SearchSnippet[]> {\n const results = await search(query, config, options)\n return toSnippets(results)\n}\n\nfunction toSnippets(results: SearchResult[]): SearchSnippet[] {\n return results.map((r) => {\n const content = stripFrontmatter(r.content)\n const source = r.metadata.source || r.id\n const lines = content.split('\\n').length\n\n return {\n package: r.metadata.package || 'unknown',\n source,\n lineStart: r.lineRange?.[0] ?? 1,\n lineEnd: r.lineRange?.[1] ?? lines,\n content,\n score: r.score,\n highlights: r.highlights,\n entities: r.entities,\n scope: r.scope,\n }\n })\n}\n\n// ── Pooled DB access for interactive search ──\n\nexport async function openPool(dbPaths: string[]): Promise<Map<string, RetrivInstance>> {\n const pool = new Map<string, RetrivInstance>()\n await Promise.all(dbPaths.map(async (dbPath) => {\n const db = await getDb({ dbPath })\n pool.set(dbPath, db)\n }))\n return pool\n}\n\nexport async function searchPooled(\n query: string,\n pool: Map<string, RetrivInstance>,\n options: SearchOptions = {},\n): Promise<SearchSnippet[]> {\n const { limit = 10, filter } = options\n const fetchLimit = limit * 2 // Over-fetch to compensate for dedup\n const allResults = await Promise.all(\n Array.from(pool.values(), async (db) => {\n const results = await db.search(query, { limit: fetchLimit, filter, returnContent: true, returnMetadata: true, returnMeta: true })\n return results.map(r => ({\n id: r.id,\n content: r.content ?? '',\n score: r.score,\n metadata: r.metadata ?? {},\n highlights: r._meta?.highlights ?? [],\n lineRange: r._chunk?.lineRange as [number, number] | undefined,\n entities: r._chunk?.entities,\n scope: r._chunk?.scope,\n }))\n }),\n )\n // Deduplicate by source+lineRange (overlapping chunks from same doc)\n const seen = new Set<string>()\n const merged = allResults.flat()\n .sort((a, b) => b.score - a.score)\n .filter((r) => {\n const lr = r.lineRange\n const key = `${r.metadata.source || r.id}:${lr?.[0]}-${lr?.[1]}`\n if (seen.has(key))\n return false\n seen.add(key)\n return true\n })\n .slice(0, limit)\n return toSnippets(merged)\n}\n\nexport async function closePool(pool: Map<string, RetrivInstance>): Promise<void> {\n await Promise.all(Array.from(pool.values(), db => db.close?.()))\n pool.clear()\n}\n"],"mappings":"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"}
@@ -1,10 +1,11 @@
1
1
  import "./agent.mjs";
2
2
  import "./config.mjs";
3
+ import "./prepare.mjs";
3
4
  import { n as sanitizeMarkdown } from "./sanitize.mjs";
4
5
  import "./cache.mjs";
5
6
  import "./yaml.mjs";
6
7
  import "./markdown.mjs";
7
- import { SearchDepsUnavailableError, closePool, openPool, searchPooled } from "../retriv/index.mjs";
8
+ import { n as closePool, s as openPool, t as SearchDepsUnavailableError, u as searchPooled } from "./retriv.mjs";
8
9
  import "./shared.mjs";
9
10
  import "./sources.mjs";
10
11
  import "./detect.mjs";
@@ -13,8 +14,8 @@ import "./cli-helpers.mjs";
13
14
  import "./lockfile.mjs";
14
15
  import "./skills.mjs";
15
16
  import { a as highlightTerms, o as normalizeScores, s as scoreLabel, t as formatCompactSnippet } from "./formatting.mjs";
16
- import "../cli.mjs";
17
- import { findPackageDbs, getPackageVersions, listLockPackages, parseFilterPrefix } from "./search.mjs";
17
+ import "./core.mjs";
18
+ import { a as parseFilterPrefix, i as listLockPackages, r as getPackageVersions, t as findPackageDbs } from "./search2.mjs";
18
19
  import { createLogUpdate } from "log-update";
19
20
  //#region src/commands/search-interactive.ts
20
21
  const FILTER_CYCLE = [
@@ -1 +1 @@
1
- {"version":3,"file":"search-interactive.mjs","names":[],"sources":["../../src/commands/search-interactive.ts"],"sourcesContent":["import type { SearchFilter, SearchSnippet } from '../retriv/index.ts'\nimport { createLogUpdate } from 'log-update'\nimport { formatCompactSnippet, highlightTerms, normalizeScores, sanitizeMarkdown, scoreLabel } from '../core/index.ts'\nimport { closePool, openPool, SearchDepsUnavailableError, searchPooled } from '../retriv/index.ts'\nimport { findPackageDbs, getPackageVersions, listLockPackages, parseFilterPrefix } from './search.ts'\n\nconst FILTER_CYCLE = [undefined, 'docs', 'issues', 'releases'] as const\ntype FilterLabel = typeof FILTER_CYCLE[number]\n\nfunction filterToSearchFilter(label: FilterLabel): SearchFilter | undefined {\n if (!label)\n return undefined\n if (label === 'issues')\n return { type: 'issue' }\n if (label === 'releases')\n return { type: 'release' }\n return { type: { $in: ['doc', 'docs'] } }\n}\n\nconst SPINNER_FRAMES = ['◐', '◓', '◑', '◒']\n\nexport async function interactiveSearch(packageFilter?: string): Promise<void> {\n const dbs = findPackageDbs(packageFilter)\n const versions = getPackageVersions()\n if (dbs.length === 0) {\n let msg: string\n if (packageFilter) {\n const available = listLockPackages()\n msg = available.length > 0\n ? `No docs indexed for \"${packageFilter}\". Available: ${available.join(', ')}`\n : `No docs indexed for \"${packageFilter}\". Run \\`skilld add ${packageFilter}\\` first.`\n }\n else {\n msg = 'No docs indexed yet. Run `skilld add <package>` first.'\n }\n process.stderr.write(`\\x1B[33m${msg}\\x1B[0m\\n`)\n return\n }\n\n const logUpdate = createLogUpdate(process.stderr, { showCursor: true })\n let pool: Awaited<ReturnType<typeof openPool>>\n try {\n pool = await openPool(dbs)\n }\n catch (err) {\n if (err instanceof SearchDepsUnavailableError) {\n process.stderr.write('\\x1B[31mSearch requires native dependencies (sqlite-vec) that are not installed.\\nInstall skilld globally or in a project to use search: npm i -g skilld\\x1B[0m\\n')\n return\n }\n throw err\n }\n\n // State\n let query = ''\n let results: SearchSnippet[] = []\n let selectedIndex = 0\n let isSearching = false\n let searchId = 0\n let filterIndex = 0\n let error = ''\n let elapsed = 0\n let spinFrame = 0\n let debounceTimer: ReturnType<typeof setTimeout> | null = null\n\n const cols = process.stdout.columns || 80\n const maxResults = 7\n const titleLabel = packageFilter ? `Search ${packageFilter} docs` : 'Search docs'\n\n function getFilterLabel(): string {\n const f = FILTER_CYCLE[filterIndex]\n if (!f)\n return ''\n return `\\x1B[36m${f}:\\x1B[0m`\n }\n\n function render() {\n const lines: string[] = []\n\n // Title\n lines.push('')\n lines.push(` \\x1B[1m${titleLabel}\\x1B[0m`)\n lines.push('')\n\n // Input line\n const filterPrefix = getFilterLabel()\n const prefix = filterPrefix ? `${filterPrefix}` : ''\n lines.push(` \\x1B[36m❯\\x1B[0m ${prefix}${query}\\x1B[7m \\x1B[0m`)\n\n // Separator / spinner\n if (isSearching) {\n const frame = SPINNER_FRAMES[spinFrame % SPINNER_FRAMES.length]\n lines.push(` \\x1B[36m${frame}\\x1B[0m \\x1B[90mSearching…\\x1B[0m`)\n }\n else {\n lines.push(` \\x1B[90m${'─'.repeat(Math.min(cols - 4, 40))}\\x1B[0m`)\n }\n\n // Results or empty state\n if (error) {\n lines.push('')\n lines.push(` \\x1B[31m${error}\\x1B[0m`)\n }\n else if (query.length === 0) {\n lines.push('')\n lines.push(' \\x1B[90mType to search…\\x1B[0m')\n }\n else if (query.length < 2 && !isSearching) {\n lines.push('')\n lines.push(' \\x1B[90mKeep typing…\\x1B[0m')\n }\n else if (results.length === 0 && !isSearching) {\n lines.push('')\n lines.push(' \\x1B[90mNo results\\x1B[0m')\n }\n else {\n lines.push('')\n const shown = results.slice(0, maxResults)\n const scores = normalizeScores(results)\n for (let i = 0; i < shown.length; i++) {\n const r = shown[i]!\n const selected = i === selectedIndex\n const bullet = selected ? '\\x1B[36m●\\x1B[0m' : '\\x1B[90m○\\x1B[0m'\n const sc = scoreLabel(scores.get(r) ?? 0)\n const { title, path, preview } = formatCompactSnippet(r, cols)\n const highlighted = highlightTerms(preview, r.highlights)\n\n const ver = versions.get(r.package)\n const pkgLabel = ver ? `${r.package}@${ver}` : r.package\n\n if (selected) {\n lines.push(` ${bullet} \\x1B[1m${pkgLabel}\\x1B[0m ${sc} \\x1B[36m${title}\\x1B[0m`)\n lines.push(` \\x1B[90m${path}\\x1B[0m`)\n lines.push(` ${highlighted}`)\n }\n else {\n lines.push(` ${bullet} \\x1B[90m${pkgLabel}\\x1B[0m ${sc} \\x1B[90m${title}\\x1B[0m`)\n }\n }\n }\n\n // Footer\n lines.push('')\n const parts: string[] = []\n if (results.length > 0)\n parts.push(`${results.length} results`)\n if (elapsed > 0 && !isSearching)\n parts.push(`${elapsed.toFixed(2)}s`)\n const footer = parts.length > 0 ? `${parts.join(' · ')} ` : ''\n lines.push(` \\x1B[90m${footer}↑↓ navigate ↵ select tab filter esc quit\\x1B[0m`)\n lines.push('')\n\n logUpdate(lines.join('\\n'))\n }\n\n async function doSearch() {\n const id = ++searchId\n const fullQuery = query.trim()\n if (fullQuery.length < 2) {\n results = []\n isSearching = false\n render()\n return\n }\n\n isSearching = true\n error = ''\n render()\n\n // Spin animation\n const spinInterval = setInterval(() => {\n spinFrame++\n if (isSearching)\n render()\n }, 80)\n\n const { query: parsed, filter: parsedFilter } = parseFilterPrefix(fullQuery)\n const filter = parsedFilter || filterToSearchFilter(FILTER_CYCLE[filterIndex])\n const start = performance.now()\n\n const res = await searchPooled(parsed, pool, { limit: maxResults, filter }).catch((e) => {\n if (id === searchId)\n error = e instanceof Error ? e.message : String(e)\n return [] as SearchSnippet[]\n })\n\n clearInterval(spinInterval)\n\n // Discard stale results\n if (id !== searchId)\n return\n\n results = res\n elapsed = (performance.now() - start) / 1000\n selectedIndex = 0\n isSearching = false\n render()\n }\n\n function scheduleSearch() {\n if (debounceTimer)\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(doSearch, 100)\n }\n\n // Show initial state\n render()\n\n // Raw stdin for keystroke handling\n const { stdin } = process\n if (stdin.isTTY)\n stdin.setRawMode(true)\n stdin.resume()\n stdin.setEncoding('utf-8')\n\n return new Promise<void>((resolve) => {\n function cleanup() {\n if (debounceTimer)\n clearTimeout(debounceTimer)\n if (stdin.isTTY)\n stdin.setRawMode(false)\n stdin.removeListener('data', onData)\n stdin.pause()\n closePool(pool)\n }\n\n function exit() {\n cleanup()\n logUpdate.done()\n resolve()\n }\n\n function selectResult() {\n if (results.length === 0 || selectedIndex >= results.length)\n return\n const r = results[selectedIndex]!\n cleanup()\n logUpdate.done()\n\n // Print full result\n const refPath = `.claude/skills/${r.package}/.skilld/${r.source}`\n const lineRange = r.lineStart === r.lineEnd ? `L${r.lineStart}` : `L${r.lineStart}-${r.lineEnd}`\n const highlighted = highlightTerms(sanitizeMarkdown(r.content), r.highlights)\n const rVer = versions.get(r.package)\n const rLabel = rVer ? `${r.package}@${rVer}` : r.package\n const rScores = normalizeScores(results)\n const out = [\n '',\n ` \\x1B[1m${rLabel}\\x1B[0m ${scoreLabel(rScores.get(r) ?? 0)}`,\n ` \\x1B[90m${refPath}:${lineRange}\\x1B[0m`,\n '',\n ` ${highlighted.replace(/\\n/g, '\\n ')}`,\n '',\n ].join('\\n')\n process.stdout.write(`${out}\\n`)\n resolve()\n }\n\n function onData(data: string) {\n // Ctrl+C\n if (data === '\\x03') {\n exit()\n return\n }\n\n // Escape\n if (data === '\\x1B' || data === '\\x1B\\x1B') {\n exit()\n return\n }\n\n // Enter\n if (data === '\\r' || data === '\\n') {\n selectResult()\n return\n }\n\n // Tab — cycle filter\n if (data === '\\t') {\n filterIndex = (filterIndex + 1) % FILTER_CYCLE.length\n if (query.length >= 2)\n scheduleSearch()\n render()\n return\n }\n\n // Backspace\n if (data === '\\x7F' || data === '\\b') {\n if (query.length > 0) {\n query = query.slice(0, -1)\n scheduleSearch()\n render()\n }\n return\n }\n\n // Arrow keys (escape sequences)\n if (data === '\\x1B[A' || data === '\\x1BOA') {\n // Up\n if (selectedIndex > 0) {\n selectedIndex--\n render()\n }\n return\n }\n if (data === '\\x1B[B' || data === '\\x1BOB') {\n // Down\n if (selectedIndex < results.length - 1) {\n selectedIndex++\n render()\n }\n return\n }\n\n // Ignore other escape sequences\n if (data.startsWith('\\x1B'))\n return\n\n // Printable characters\n query += data\n scheduleSearch()\n render()\n }\n\n stdin.on('data', onData)\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"search-interactive.mjs","names":[],"sources":["../../src/commands/search-interactive.ts"],"sourcesContent":["import type { SearchFilter, SearchSnippet } from '../retriv/index.ts'\nimport { createLogUpdate } from 'log-update'\nimport { formatCompactSnippet, highlightTerms, normalizeScores, sanitizeMarkdown, scoreLabel } from '../core/index.ts'\nimport { closePool, openPool, SearchDepsUnavailableError, searchPooled } from '../retriv/index.ts'\nimport { findPackageDbs, getPackageVersions, listLockPackages, parseFilterPrefix } from './search.ts'\n\nconst FILTER_CYCLE = [undefined, 'docs', 'issues', 'releases'] as const\ntype FilterLabel = typeof FILTER_CYCLE[number]\n\nfunction filterToSearchFilter(label: FilterLabel): SearchFilter | undefined {\n if (!label)\n return undefined\n if (label === 'issues')\n return { type: 'issue' }\n if (label === 'releases')\n return { type: 'release' }\n return { type: { $in: ['doc', 'docs'] } }\n}\n\nconst SPINNER_FRAMES = ['◐', '◓', '◑', '◒']\n\nexport async function interactiveSearch(packageFilter?: string): Promise<void> {\n const dbs = findPackageDbs(packageFilter)\n const versions = getPackageVersions()\n if (dbs.length === 0) {\n let msg: string\n if (packageFilter) {\n const available = listLockPackages()\n msg = available.length > 0\n ? `No docs indexed for \"${packageFilter}\". Available: ${available.join(', ')}`\n : `No docs indexed for \"${packageFilter}\". Run \\`skilld add ${packageFilter}\\` first.`\n }\n else {\n msg = 'No docs indexed yet. Run `skilld add <package>` first.'\n }\n process.stderr.write(`\\x1B[33m${msg}\\x1B[0m\\n`)\n return\n }\n\n const logUpdate = createLogUpdate(process.stderr, { showCursor: true })\n let pool: Awaited<ReturnType<typeof openPool>>\n try {\n pool = await openPool(dbs)\n }\n catch (err) {\n if (err instanceof SearchDepsUnavailableError) {\n process.stderr.write('\\x1B[31mSearch requires native dependencies (sqlite-vec) that are not installed.\\nInstall skilld globally or in a project to use search: npm i -g skilld\\x1B[0m\\n')\n return\n }\n throw err\n }\n\n // State\n let query = ''\n let results: SearchSnippet[] = []\n let selectedIndex = 0\n let isSearching = false\n let searchId = 0\n let filterIndex = 0\n let error = ''\n let elapsed = 0\n let spinFrame = 0\n let debounceTimer: ReturnType<typeof setTimeout> | null = null\n\n const cols = process.stdout.columns || 80\n const maxResults = 7\n const titleLabel = packageFilter ? `Search ${packageFilter} docs` : 'Search docs'\n\n function getFilterLabel(): string {\n const f = FILTER_CYCLE[filterIndex]\n if (!f)\n return ''\n return `\\x1B[36m${f}:\\x1B[0m`\n }\n\n function render() {\n const lines: string[] = []\n\n // Title\n lines.push('')\n lines.push(` \\x1B[1m${titleLabel}\\x1B[0m`)\n lines.push('')\n\n // Input line\n const filterPrefix = getFilterLabel()\n const prefix = filterPrefix ? `${filterPrefix}` : ''\n lines.push(` \\x1B[36m❯\\x1B[0m ${prefix}${query}\\x1B[7m \\x1B[0m`)\n\n // Separator / spinner\n if (isSearching) {\n const frame = SPINNER_FRAMES[spinFrame % SPINNER_FRAMES.length]\n lines.push(` \\x1B[36m${frame}\\x1B[0m \\x1B[90mSearching…\\x1B[0m`)\n }\n else {\n lines.push(` \\x1B[90m${'─'.repeat(Math.min(cols - 4, 40))}\\x1B[0m`)\n }\n\n // Results or empty state\n if (error) {\n lines.push('')\n lines.push(` \\x1B[31m${error}\\x1B[0m`)\n }\n else if (query.length === 0) {\n lines.push('')\n lines.push(' \\x1B[90mType to search…\\x1B[0m')\n }\n else if (query.length < 2 && !isSearching) {\n lines.push('')\n lines.push(' \\x1B[90mKeep typing…\\x1B[0m')\n }\n else if (results.length === 0 && !isSearching) {\n lines.push('')\n lines.push(' \\x1B[90mNo results\\x1B[0m')\n }\n else {\n lines.push('')\n const shown = results.slice(0, maxResults)\n const scores = normalizeScores(results)\n for (let i = 0; i < shown.length; i++) {\n const r = shown[i]!\n const selected = i === selectedIndex\n const bullet = selected ? '\\x1B[36m●\\x1B[0m' : '\\x1B[90m○\\x1B[0m'\n const sc = scoreLabel(scores.get(r) ?? 0)\n const { title, path, preview } = formatCompactSnippet(r, cols)\n const highlighted = highlightTerms(preview, r.highlights)\n\n const ver = versions.get(r.package)\n const pkgLabel = ver ? `${r.package}@${ver}` : r.package\n\n if (selected) {\n lines.push(` ${bullet} \\x1B[1m${pkgLabel}\\x1B[0m ${sc} \\x1B[36m${title}\\x1B[0m`)\n lines.push(` \\x1B[90m${path}\\x1B[0m`)\n lines.push(` ${highlighted}`)\n }\n else {\n lines.push(` ${bullet} \\x1B[90m${pkgLabel}\\x1B[0m ${sc} \\x1B[90m${title}\\x1B[0m`)\n }\n }\n }\n\n // Footer\n lines.push('')\n const parts: string[] = []\n if (results.length > 0)\n parts.push(`${results.length} results`)\n if (elapsed > 0 && !isSearching)\n parts.push(`${elapsed.toFixed(2)}s`)\n const footer = parts.length > 0 ? `${parts.join(' · ')} ` : ''\n lines.push(` \\x1B[90m${footer}↑↓ navigate ↵ select tab filter esc quit\\x1B[0m`)\n lines.push('')\n\n logUpdate(lines.join('\\n'))\n }\n\n async function doSearch() {\n const id = ++searchId\n const fullQuery = query.trim()\n if (fullQuery.length < 2) {\n results = []\n isSearching = false\n render()\n return\n }\n\n isSearching = true\n error = ''\n render()\n\n // Spin animation\n const spinInterval = setInterval(() => {\n spinFrame++\n if (isSearching)\n render()\n }, 80)\n\n const { query: parsed, filter: parsedFilter } = parseFilterPrefix(fullQuery)\n const filter = parsedFilter || filterToSearchFilter(FILTER_CYCLE[filterIndex])\n const start = performance.now()\n\n const res = await searchPooled(parsed, pool, { limit: maxResults, filter }).catch((e) => {\n if (id === searchId)\n error = e instanceof Error ? e.message : String(e)\n return [] as SearchSnippet[]\n })\n\n clearInterval(spinInterval)\n\n // Discard stale results\n if (id !== searchId)\n return\n\n results = res\n elapsed = (performance.now() - start) / 1000\n selectedIndex = 0\n isSearching = false\n render()\n }\n\n function scheduleSearch() {\n if (debounceTimer)\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(doSearch, 100)\n }\n\n // Show initial state\n render()\n\n // Raw stdin for keystroke handling\n const { stdin } = process\n if (stdin.isTTY)\n stdin.setRawMode(true)\n stdin.resume()\n stdin.setEncoding('utf-8')\n\n return new Promise<void>((resolve) => {\n function cleanup() {\n if (debounceTimer)\n clearTimeout(debounceTimer)\n if (stdin.isTTY)\n stdin.setRawMode(false)\n stdin.removeListener('data', onData)\n stdin.pause()\n closePool(pool)\n }\n\n function exit() {\n cleanup()\n logUpdate.done()\n resolve()\n }\n\n function selectResult() {\n if (results.length === 0 || selectedIndex >= results.length)\n return\n const r = results[selectedIndex]!\n cleanup()\n logUpdate.done()\n\n // Print full result\n const refPath = `.claude/skills/${r.package}/.skilld/${r.source}`\n const lineRange = r.lineStart === r.lineEnd ? `L${r.lineStart}` : `L${r.lineStart}-${r.lineEnd}`\n const highlighted = highlightTerms(sanitizeMarkdown(r.content), r.highlights)\n const rVer = versions.get(r.package)\n const rLabel = rVer ? `${r.package}@${rVer}` : r.package\n const rScores = normalizeScores(results)\n const out = [\n '',\n ` \\x1B[1m${rLabel}\\x1B[0m ${scoreLabel(rScores.get(r) ?? 0)}`,\n ` \\x1B[90m${refPath}:${lineRange}\\x1B[0m`,\n '',\n ` ${highlighted.replace(/\\n/g, '\\n ')}`,\n '',\n ].join('\\n')\n process.stdout.write(`${out}\\n`)\n resolve()\n }\n\n function onData(data: string) {\n // Ctrl+C\n if (data === '\\x03') {\n exit()\n return\n }\n\n // Escape\n if (data === '\\x1B' || data === '\\x1B\\x1B') {\n exit()\n return\n }\n\n // Enter\n if (data === '\\r' || data === '\\n') {\n selectResult()\n return\n }\n\n // Tab — cycle filter\n if (data === '\\t') {\n filterIndex = (filterIndex + 1) % FILTER_CYCLE.length\n if (query.length >= 2)\n scheduleSearch()\n render()\n return\n }\n\n // Backspace\n if (data === '\\x7F' || data === '\\b') {\n if (query.length > 0) {\n query = query.slice(0, -1)\n scheduleSearch()\n render()\n }\n return\n }\n\n // Arrow keys (escape sequences)\n if (data === '\\x1B[A' || data === '\\x1BOA') {\n // Up\n if (selectedIndex > 0) {\n selectedIndex--\n render()\n }\n return\n }\n if (data === '\\x1B[B' || data === '\\x1BOB') {\n // Down\n if (selectedIndex < results.length - 1) {\n selectedIndex++\n render()\n }\n return\n }\n\n // Ignore other escape sequences\n if (data.startsWith('\\x1B'))\n return\n\n // Printable characters\n query += data\n scheduleSearch()\n render()\n }\n\n stdin.on('data', onData)\n })\n}\n"],"mappings":"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"}