skilld 0.15.4 → 1.0.0

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 (39) hide show
  1. package/README.md +6 -5
  2. package/dist/_chunks/agent.mjs +6 -9
  3. package/dist/_chunks/agent.mjs.map +1 -1
  4. package/dist/_chunks/cache2.mjs +68 -3
  5. package/dist/_chunks/cache2.mjs.map +1 -0
  6. package/dist/_chunks/formatting.mjs +549 -1
  7. package/dist/_chunks/formatting.mjs.map +1 -1
  8. package/dist/_chunks/install.mjs +536 -12
  9. package/dist/_chunks/install.mjs.map +1 -0
  10. package/dist/_chunks/list.mjs +60 -3
  11. package/dist/_chunks/list.mjs.map +1 -0
  12. package/dist/_chunks/pool.mjs +167 -113
  13. package/dist/_chunks/pool.mjs.map +1 -1
  14. package/dist/_chunks/pool2.mjs +115 -0
  15. package/dist/_chunks/pool2.mjs.map +1 -0
  16. package/dist/_chunks/prompts.mjs +2 -2
  17. package/dist/_chunks/prompts.mjs.map +1 -1
  18. package/dist/_chunks/search-interactive.mjs +236 -5
  19. package/dist/_chunks/search-interactive.mjs.map +1 -0
  20. package/dist/_chunks/search.mjs +12 -171
  21. package/dist/_chunks/sync.mjs +9 -98
  22. package/dist/_chunks/sync.mjs.map +1 -1
  23. package/dist/_chunks/sync2.mjs +1 -2
  24. package/dist/_chunks/uninstall.mjs +200 -8
  25. package/dist/_chunks/uninstall.mjs.map +1 -0
  26. package/dist/cli.mjs +99 -836
  27. package/dist/cli.mjs.map +1 -1
  28. package/dist/retriv/index.mjs +1 -1
  29. package/dist/retriv/index.mjs.map +1 -1
  30. package/package.json +3 -3
  31. package/dist/_chunks/config2.mjs +0 -12
  32. package/dist/_chunks/remove.mjs +0 -12
  33. package/dist/_chunks/search-interactive2.mjs +0 -236
  34. package/dist/_chunks/search-interactive2.mjs.map +0 -1
  35. package/dist/_chunks/search.mjs.map +0 -1
  36. package/dist/_chunks/search2.mjs +0 -13
  37. package/dist/_chunks/skills.mjs +0 -552
  38. package/dist/_chunks/skills.mjs.map +0 -1
  39. package/dist/_chunks/status.mjs +0 -13
@@ -1,4 +1,552 @@
1
+ import { n as yamlParseKV, r as yamlUnescape, t as yamlEscape } from "./yaml.mjs";
2
+ import { i as parseFrontmatter } from "./markdown.mjs";
3
+ import { a as semverGt, n as getSharedSkillsDir } from "./shared.mjs";
4
+ import { s as readLocalDependencies } from "./sources.mjs";
5
+ import { _ as targets, g as getAgentVersion, h as detectTargetAgent, m as detectInstalledAgents } from "./prompts.mjs";
6
+ import { a as getModelName } from "./agent.mjs";
7
+ import { homedir } from "node:os";
8
+ import { join } from "pathe";
9
+ import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
10
+ import { fileURLToPath } from "node:url";
1
11
  import * as p from "@clack/prompts";
12
+ import { detectCurrentAgent } from "unagent/env";
13
+ import { dirname as dirname$1, resolve as resolve$1 } from "node:path";
14
+ const defaultFeatures = {
15
+ search: true,
16
+ issues: true,
17
+ discussions: true,
18
+ releases: true
19
+ };
20
+ const CONFIG_DIR = join(homedir(), ".skilld");
21
+ const CONFIG_PATH = join(CONFIG_DIR, "config.yaml");
22
+ function hasConfig() {
23
+ return existsSync(CONFIG_PATH);
24
+ }
25
+ /** Whether the first-run wizard has been completed (not just agent selection) */
26
+ function hasCompletedWizard() {
27
+ if (!existsSync(CONFIG_PATH)) return false;
28
+ const config = readConfig();
29
+ return config.features !== void 0 || config.model !== void 0 || config.skipLlm !== void 0;
30
+ }
31
+ function readConfig() {
32
+ if (!existsSync(CONFIG_PATH)) return {};
33
+ const content = readFileSync(CONFIG_PATH, "utf-8");
34
+ const config = {};
35
+ let inBlock = null;
36
+ const projects = [];
37
+ const features = {};
38
+ for (const line of content.split("\n")) {
39
+ if (line.startsWith("projects:")) {
40
+ inBlock = "projects";
41
+ continue;
42
+ }
43
+ if (line.startsWith("features:")) {
44
+ inBlock = "features";
45
+ continue;
46
+ }
47
+ if (inBlock === "projects") {
48
+ if (line.startsWith(" - ")) {
49
+ projects.push(yamlUnescape(line.slice(4)));
50
+ continue;
51
+ }
52
+ inBlock = null;
53
+ }
54
+ if (inBlock === "features") {
55
+ const m = line.match(/^ {2}(\w+):\s*(.+)/);
56
+ if (m) {
57
+ const key = m[1];
58
+ if (key in defaultFeatures) features[key] = m[2] === "true";
59
+ continue;
60
+ }
61
+ inBlock = null;
62
+ }
63
+ const kv = yamlParseKV(line);
64
+ if (!kv) continue;
65
+ const [key, value] = kv;
66
+ if (key === "model" && value) config.model = value;
67
+ if (key === "agent" && value) config.agent = value;
68
+ if (key === "skipLlm") config.skipLlm = value === "true";
69
+ }
70
+ if (projects.length > 0) config.projects = projects;
71
+ if (Object.keys(features).length > 0) config.features = {
72
+ ...defaultFeatures,
73
+ ...features
74
+ };
75
+ return config;
76
+ }
77
+ function writeConfig(config) {
78
+ mkdirSync(CONFIG_DIR, {
79
+ recursive: true,
80
+ mode: 448
81
+ });
82
+ let yaml = "";
83
+ if (config.model) yaml += `model: ${config.model}\n`;
84
+ if (config.agent) yaml += `agent: ${config.agent}\n`;
85
+ if (config.skipLlm) yaml += `skipLlm: true\n`;
86
+ if (config.features) {
87
+ yaml += "features:\n";
88
+ for (const [k, v] of Object.entries(config.features)) yaml += ` ${k}: ${v}\n`;
89
+ }
90
+ if (config.projects?.length) {
91
+ yaml += "projects:\n";
92
+ for (const p of config.projects) yaml += ` - ${yamlEscape(p)}\n`;
93
+ }
94
+ writeFileSync(CONFIG_PATH, yaml, { mode: 384 });
95
+ }
96
+ function updateConfig(updates) {
97
+ writeConfig({
98
+ ...readConfig(),
99
+ ...updates
100
+ });
101
+ }
102
+ function registerProject(projectPath) {
103
+ const config = readConfig();
104
+ const projects = new Set(config.projects || []);
105
+ projects.add(projectPath);
106
+ writeConfig({
107
+ ...config,
108
+ projects: [...projects]
109
+ });
110
+ }
111
+ function unregisterProject(projectPath) {
112
+ const config = readConfig();
113
+ const projects = (config.projects || []).filter((p) => p !== projectPath);
114
+ writeConfig({
115
+ ...config,
116
+ projects
117
+ });
118
+ }
119
+ function getRegisteredProjects() {
120
+ return readConfig().projects || [];
121
+ }
122
+ function findPackageJson() {
123
+ let dir = dirname$1(fileURLToPath(import.meta.url));
124
+ for (let i = 0; i < 5; i++) {
125
+ const candidate = resolve$1(dir, "package.json");
126
+ try {
127
+ const content = readFileSync(candidate, "utf8");
128
+ if (content) return content;
129
+ } catch {}
130
+ dir = resolve$1(dir, "..");
131
+ }
132
+ return "{\"version\":\"0.0.0\"}";
133
+ }
134
+ const version = JSON.parse(findPackageJson()).version;
135
+ const sharedArgs = {
136
+ global: {
137
+ type: "boolean",
138
+ alias: "g",
139
+ description: "Install globally to ~/.skilld/skills",
140
+ default: false
141
+ },
142
+ agent: {
143
+ type: "enum",
144
+ options: Object.keys(targets),
145
+ alias: "a",
146
+ description: "Agent where skills are installed"
147
+ },
148
+ model: {
149
+ type: "string",
150
+ alias: "m",
151
+ description: "LLM model for skill generation",
152
+ valueHint: "id"
153
+ },
154
+ yes: {
155
+ type: "boolean",
156
+ alias: "y",
157
+ description: "Skip prompts, use defaults",
158
+ default: false
159
+ },
160
+ force: {
161
+ type: "boolean",
162
+ alias: "f",
163
+ description: "Ignore all caches, re-fetch docs and regenerate",
164
+ default: false
165
+ },
166
+ debug: {
167
+ type: "boolean",
168
+ description: "Save raw LLM output to logs/ for each section",
169
+ default: false
170
+ }
171
+ };
172
+ /** Check if the current environment supports interactive prompts */
173
+ function isInteractive() {
174
+ if (detectCurrentAgent()) return false;
175
+ if (process.env.CI) return false;
176
+ if (!process.stdout.isTTY) return false;
177
+ return true;
178
+ }
179
+ /** Exit with error if interactive terminal is required but unavailable */
180
+ function requireInteractive(command) {
181
+ if (!isInteractive()) {
182
+ console.error(`Error: \`skilld ${command}\` requires an interactive terminal`);
183
+ process.exit(1);
184
+ }
185
+ }
186
+ /** Resolve agent from flags/cwd/config. cwd is source of truth over config. */
187
+ function resolveAgent(agentFlag) {
188
+ return agentFlag ?? detectTargetAgent() ?? readConfig().agent ?? null;
189
+ }
190
+ /** Prompt user to pick an agent when auto-detection fails */
191
+ async function promptForAgent() {
192
+ const installed = detectInstalledAgents();
193
+ if (!isInteractive()) {
194
+ if (installed.length === 1) {
195
+ updateConfig({ agent: installed[0] });
196
+ return installed[0];
197
+ }
198
+ console.error("Error: could not auto-detect agent. Pass --agent <name> to specify.");
199
+ process.exit(1);
200
+ }
201
+ const options = (installed.length ? installed : Object.keys(targets)).map((id) => ({
202
+ label: targets[id].displayName,
203
+ value: id,
204
+ hint: targets[id].skillsDir
205
+ }));
206
+ const hint = installed.length ? `Detected ${installed.map((t) => targets[t].displayName).join(", ")} but couldn't determine which to use` : "No agents auto-detected";
207
+ p.log.warn(`Could not detect which coding agent to install skills for.\n ${hint}`);
208
+ const choice = await p.select({
209
+ message: "Which coding agent should skills be installed for?",
210
+ options
211
+ });
212
+ if (p.isCancel(choice)) return null;
213
+ updateConfig({ agent: choice });
214
+ p.log.success(`Default agent set to ${targets[choice].displayName}`);
215
+ return choice;
216
+ }
217
+ /** Get installed LLM generators with working CLIs (verified via --version) */
218
+ function getInstalledGenerators() {
219
+ return detectInstalledAgents().filter((id) => targets[id].cli).map((id) => {
220
+ const ver = getAgentVersion(id);
221
+ return ver ? {
222
+ name: targets[id].displayName,
223
+ version: ver
224
+ } : null;
225
+ }).filter((a) => a !== null);
226
+ }
227
+ function relativeTime(date) {
228
+ const diff = Date.now() - date.getTime();
229
+ const mins = Math.floor(diff / 6e4);
230
+ const hours = Math.floor(diff / 36e5);
231
+ const days = Math.floor(diff / 864e5);
232
+ if (mins < 1) return "just now";
233
+ if (mins < 60) return `${mins}m ago`;
234
+ if (hours < 24) return `${hours}h ago`;
235
+ return `${days}d ago`;
236
+ }
237
+ function getLastSynced(state) {
238
+ let latest = null;
239
+ for (const skill of state.skills) if (skill.info?.syncedAt) {
240
+ const d = new Date(skill.info.syncedAt);
241
+ if (!latest || d > latest) latest = d;
242
+ }
243
+ return latest ? relativeTime(latest) : null;
244
+ }
245
+ function introLine({ state, generators, modelId }) {
246
+ const name = "\x1B[1m\x1B[35mskilld\x1B[0m";
247
+ const ver = `\x1B[90mv${version}\x1B[0m`;
248
+ const lastSynced = getLastSynced(state);
249
+ const synced = lastSynced ? ` · \x1B[90msynced ${lastSynced}\x1B[0m` : "";
250
+ const modelStr = modelId ? ` · ${getModelName(modelId)}` : "";
251
+ const genStr = generators?.length ? generators.map((g) => `${g.name} v${g.version}`).join(", ") : "";
252
+ return `${name} ${ver}${synced}${genStr ? `\n\x1B[90m↳ ${genStr}${modelStr}\x1B[0m` : ""}`;
253
+ }
254
+ function formatStatus(synced, outdated) {
255
+ const parts = [];
256
+ if (synced > 0) parts.push(`\x1B[32m${synced} synced\x1B[0m`);
257
+ if (outdated > 0) parts.push(`\x1B[33m${outdated} outdated\x1B[0m`);
258
+ return `Skills: ${parts.join(" · ")}`;
259
+ }
260
+ function getRepoHint(name, cwd) {
261
+ const pkgJsonPath = join(cwd, "node_modules", name, "package.json");
262
+ if (!existsSync(pkgJsonPath)) return void 0;
263
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
264
+ const url = typeof pkg.repository === "string" ? pkg.repository : pkg.repository?.url;
265
+ if (!url) return void 0;
266
+ return url.replace(/^git\+/, "").replace(/\.git$/, "").replace(/^git:\/\//, "https://").replace(/^ssh:\/\/git@github\.com/, "https://github.com").replace(/^https?:\/\/(www\.)?github\.com\//, "");
267
+ }
268
+ function parsePackages(packages) {
269
+ if (!packages) return [];
270
+ return packages.split(",").map((s) => {
271
+ const trimmed = s.trim();
272
+ const atIdx = trimmed.lastIndexOf("@");
273
+ if (atIdx <= 0) return {
274
+ name: trimmed,
275
+ version: ""
276
+ };
277
+ return {
278
+ name: trimmed.slice(0, atIdx),
279
+ version: trimmed.slice(atIdx + 1)
280
+ };
281
+ }).filter((p) => p.name);
282
+ }
283
+ function serializePackages(pkgs) {
284
+ return pkgs.map((p) => `${p.name}@${p.version}`).join(", ");
285
+ }
286
+ const SKILL_FM_KEYS = [
287
+ "packageName",
288
+ "version",
289
+ "packages",
290
+ "repo",
291
+ "source",
292
+ "syncedAt",
293
+ "generator",
294
+ "path",
295
+ "ref",
296
+ "commit"
297
+ ];
298
+ function parseSkillFrontmatter(skillPath) {
299
+ if (!existsSync(skillPath)) return null;
300
+ const fm = parseFrontmatter(readFileSync(skillPath, "utf-8"));
301
+ if (Object.keys(fm).length === 0) return null;
302
+ const info = {};
303
+ for (const key of SKILL_FM_KEYS) if (fm[key]) info[key] = fm[key];
304
+ return info;
305
+ }
306
+ function readLock(skillsDir) {
307
+ const lockPath = join(skillsDir, "skilld-lock.yaml");
308
+ if (!existsSync(lockPath)) return null;
309
+ const content = readFileSync(lockPath, "utf-8");
310
+ const skills = {};
311
+ let currentSkill = null;
312
+ for (const line of content.split("\n")) {
313
+ const skillMatch = line.match(/^ {2}(\S+):$/);
314
+ if (skillMatch) {
315
+ currentSkill = skillMatch[1];
316
+ skills[currentSkill] = {};
317
+ continue;
318
+ }
319
+ if (currentSkill && line.startsWith(" ")) {
320
+ const kv = yamlParseKV(line);
321
+ if (kv) skills[currentSkill][kv[0]] = kv[1];
322
+ }
323
+ }
324
+ return { skills };
325
+ }
326
+ function serializeLock(lock) {
327
+ let yaml = "skills:\n";
328
+ for (const [name, skill] of Object.entries(lock.skills)) {
329
+ yaml += ` ${name}:\n`;
330
+ if (skill.packageName) yaml += ` packageName: ${yamlEscape(skill.packageName)}\n`;
331
+ if (skill.version) yaml += ` version: ${yamlEscape(skill.version)}\n`;
332
+ if (skill.packages) yaml += ` packages: ${yamlEscape(skill.packages)}\n`;
333
+ if (skill.repo) yaml += ` repo: ${yamlEscape(skill.repo)}\n`;
334
+ if (skill.source) yaml += ` source: ${yamlEscape(skill.source)}\n`;
335
+ if (skill.syncedAt) yaml += ` syncedAt: ${yamlEscape(skill.syncedAt)}\n`;
336
+ if (skill.generator) yaml += ` generator: ${yamlEscape(skill.generator)}\n`;
337
+ if (skill.path) yaml += ` path: ${yamlEscape(skill.path)}\n`;
338
+ if (skill.ref) yaml += ` ref: ${yamlEscape(skill.ref)}\n`;
339
+ if (skill.commit) yaml += ` commit: ${yamlEscape(skill.commit)}\n`;
340
+ }
341
+ return yaml;
342
+ }
343
+ function writeLock(skillsDir, skillName, info) {
344
+ const lockPath = join(skillsDir, "skilld-lock.yaml");
345
+ let lock = { skills: {} };
346
+ if (existsSync(lockPath)) lock = readLock(skillsDir) || { skills: {} };
347
+ const existing = lock.skills[skillName];
348
+ if (existing && info.packageName) {
349
+ const existingPkgs = parsePackages(existing.packages);
350
+ if (existing.packageName && !existingPkgs.some((p) => p.name === existing.packageName)) existingPkgs.unshift({
351
+ name: existing.packageName,
352
+ version: existing.version || ""
353
+ });
354
+ const idx = existingPkgs.findIndex((p) => p.name === info.packageName);
355
+ if (idx >= 0) existingPkgs[idx].version = info.version || "";
356
+ else existingPkgs.push({
357
+ name: info.packageName,
358
+ version: info.version || ""
359
+ });
360
+ info.packages = serializePackages(existingPkgs);
361
+ info.packageName = existingPkgs[0].name;
362
+ info.version = existingPkgs[0].version;
363
+ if (!info.repo && existing.repo) info.repo = existing.repo;
364
+ if (!info.source && existing.source) info.source = existing.source;
365
+ if (!info.generator && existing.generator) info.generator = existing.generator;
366
+ }
367
+ lock.skills[skillName] = info;
368
+ writeFileSync(lockPath, serializeLock(lock));
369
+ }
370
+ /**
371
+ * Merge multiple lockfiles, preferring the most recently synced entry per skill.
372
+ */
373
+ function mergeLocks(locks) {
374
+ const merged = {};
375
+ for (const lock of locks) for (const [name, info] of Object.entries(lock.skills)) {
376
+ const existing = merged[name];
377
+ if (!existing || info.syncedAt && (!existing.syncedAt || info.syncedAt > existing.syncedAt)) merged[name] = info;
378
+ }
379
+ return { skills: merged };
380
+ }
381
+ /**
382
+ * Sync a lockfile to all other dirs that already have a skilld-lock.yaml.
383
+ * Only updates existing lockfiles — does not create new ones.
384
+ */
385
+ function syncLockfilesToDirs(sourceLock, dirs) {
386
+ for (const dir of dirs) {
387
+ const lockPath = join(dir, "skilld-lock.yaml");
388
+ if (!existsSync(lockPath)) continue;
389
+ const existing = readLock(dir);
390
+ if (!existing) continue;
391
+ writeFileSync(lockPath, serializeLock(mergeLocks([existing, sourceLock])));
392
+ }
393
+ }
394
+ function removeLockEntry(skillsDir, skillName) {
395
+ const lockPath = join(skillsDir, "skilld-lock.yaml");
396
+ const lock = readLock(skillsDir);
397
+ if (!lock) return;
398
+ delete lock.skills[skillName];
399
+ if (Object.keys(lock.skills).length === 0) {
400
+ unlinkSync(lockPath);
401
+ return;
402
+ }
403
+ writeFileSync(lockPath, serializeLock(lock));
404
+ }
405
+ function* iterateSkills(opts = {}) {
406
+ const { scope = "all", cwd = process.cwd() } = opts;
407
+ const agentTypes = opts.agents ?? Object.keys(targets);
408
+ const sharedDir = getSharedSkillsDir(cwd);
409
+ let yieldedLocal = false;
410
+ if (sharedDir && (scope === "local" || scope === "all")) {
411
+ yieldedLocal = true;
412
+ const lock = readLock(sharedDir);
413
+ const entries = readdirSync(sharedDir).filter((f) => !f.startsWith(".") && f !== "skilld-lock.yaml");
414
+ const firstAgent = agentTypes[0] ?? Object.keys(targets)[0];
415
+ for (const name of entries) {
416
+ const dir = join(sharedDir, name);
417
+ if (lock?.skills[name]) yield {
418
+ name,
419
+ dir,
420
+ agent: firstAgent,
421
+ info: lock.skills[name],
422
+ scope: "local"
423
+ };
424
+ else {
425
+ const info = parseSkillFrontmatter(join(dir, ".skilld", "_SKILL.md"));
426
+ if (info?.generator === "skilld") yield {
427
+ name,
428
+ dir,
429
+ agent: firstAgent,
430
+ info,
431
+ scope: "local"
432
+ };
433
+ }
434
+ }
435
+ }
436
+ for (const agentType of agentTypes) {
437
+ const agent = targets[agentType];
438
+ if (!yieldedLocal && (scope === "local" || scope === "all")) {
439
+ const localDir = join(cwd, agent.skillsDir);
440
+ if (existsSync(localDir)) {
441
+ const lock = readLock(localDir);
442
+ const entries = readdirSync(localDir).filter((f) => !f.startsWith(".") && f !== "skilld-lock.yaml");
443
+ for (const name of entries) {
444
+ const dir = join(localDir, name);
445
+ if (lock?.skills[name]) yield {
446
+ name,
447
+ dir,
448
+ agent: agentType,
449
+ info: lock.skills[name],
450
+ scope: "local"
451
+ };
452
+ else {
453
+ const info = parseSkillFrontmatter(join(dir, ".skilld", "_SKILL.md"));
454
+ if (info?.generator === "skilld") yield {
455
+ name,
456
+ dir,
457
+ agent: agentType,
458
+ info,
459
+ scope: "local"
460
+ };
461
+ }
462
+ }
463
+ }
464
+ }
465
+ if ((scope === "global" || scope === "all") && agent.globalSkillsDir) {
466
+ const globalDir = agent.globalSkillsDir;
467
+ if (existsSync(globalDir)) {
468
+ const lock = readLock(globalDir);
469
+ const entries = readdirSync(globalDir).filter((f) => !f.startsWith(".") && f !== "skilld-lock.yaml");
470
+ for (const name of entries) {
471
+ const dir = join(globalDir, name);
472
+ if (lock?.skills[name]) yield {
473
+ name,
474
+ dir,
475
+ agent: agentType,
476
+ info: lock.skills[name],
477
+ scope: "global"
478
+ };
479
+ else {
480
+ const info = parseSkillFrontmatter(join(dir, ".skilld", "_SKILL.md"));
481
+ if (info?.generator === "skilld") yield {
482
+ name,
483
+ dir,
484
+ agent: agentType,
485
+ info,
486
+ scope: "global"
487
+ };
488
+ }
489
+ }
490
+ }
491
+ }
492
+ }
493
+ }
494
+ function isOutdated(skill, depVersion) {
495
+ if (!skill.info?.version) return true;
496
+ return semverGt(depVersion.replace(/^[\^~]/, ""), skill.info.version);
497
+ }
498
+ async function getProjectState(cwd = process.cwd()) {
499
+ const skills = [...iterateSkills({
500
+ scope: "local",
501
+ cwd
502
+ })];
503
+ const localDeps = await readLocalDependencies(cwd).catch(() => []);
504
+ const deps = new Map(localDeps.map((d) => [d.name, d.version]));
505
+ const skillByName = new Map(skills.map((s) => [s.name, s]));
506
+ const skillByPkgName = /* @__PURE__ */ new Map();
507
+ for (const s of skills) {
508
+ if (s.info?.packageName) skillByPkgName.set(s.info.packageName, s);
509
+ for (const pkg of parsePackages(s.info?.packages)) skillByPkgName.set(pkg.name, s);
510
+ }
511
+ const missing = [];
512
+ const outdated = [];
513
+ const synced = [];
514
+ const matchedSkillNames = /* @__PURE__ */ new Set();
515
+ for (const [pkgName, version] of deps) {
516
+ const normalizedName = pkgName.replace(/^@/, "").replace(/\//g, "-");
517
+ const skill = skillByName.get(`${normalizedName}-skilld`) || skillByName.get(normalizedName) || skillByName.get(pkgName) || skillByPkgName.get(pkgName);
518
+ if (!skill) missing.push(pkgName);
519
+ else {
520
+ matchedSkillNames.add(skill.name);
521
+ if (isOutdated(skill, version)) outdated.push({
522
+ ...skill,
523
+ packageName: pkgName,
524
+ latestVersion: version
525
+ });
526
+ else synced.push({
527
+ ...skill,
528
+ packageName: pkgName,
529
+ latestVersion: version
530
+ });
531
+ }
532
+ }
533
+ return {
534
+ skills,
535
+ deps,
536
+ missing,
537
+ outdated,
538
+ synced,
539
+ unmatched: skills.filter((s) => !matchedSkillNames.has(s.name))
540
+ };
541
+ }
542
+ function getSkillsDir(agent, scope, cwd = process.cwd()) {
543
+ const agentConfig = targets[agent];
544
+ if (scope === "global") {
545
+ if (!agentConfig.globalSkillsDir) throw new Error(`Agent ${agent} does not support global skills`);
546
+ return agentConfig.globalSkillsDir;
547
+ }
548
+ return getSharedSkillsDir(cwd) || join(cwd, agentConfig.skillsDir);
549
+ }
2
550
  function timeAgo(iso) {
3
551
  if (!iso) return "";
4
552
  const diff = Date.now() - new Date(iso).getTime();
@@ -81,6 +629,6 @@ function formatCompactSnippet(r, cols) {
81
629
  preview: firstLine.length > maxPreview ? `${firstLine.slice(0, maxPreview - 1)}…` : firstLine
82
630
  };
83
631
  }
84
- export { highlightTerms as a, timeAgo as c, formatSource as i, timedSpinner as l, formatDuration as n, normalizeScores as o, formatSnippet as r, scoreLabel as s, formatCompactSnippet as t };
632
+ export { version as A, introLine as C, requireInteractive as D, relativeTime as E, readConfig as F, registerProject as I, unregisterProject as L, getRegisteredProjects as M, hasCompletedWizard as N, resolveAgent as O, hasConfig as P, updateConfig as R, getRepoHint as S, promptForAgent as T, removeLockEntry as _, highlightTerms as a, formatStatus as b, timeAgo as c, getSkillsDir as d, isOutdated as f, readLock as g, parsePackages as h, formatSource as i, defaultFeatures as j, sharedArgs as k, timedSpinner as l, mergeLocks as m, formatDuration as n, normalizeScores as o, iterateSkills as p, formatSnippet as r, scoreLabel as s, formatCompactSnippet as t, getProjectState as u, syncLockfilesToDirs as v, isInteractive as w, getInstalledGenerators as x, writeLock as y };
85
633
 
86
634
  //# sourceMappingURL=formatting.mjs.map