skilld 1.7.3 → 1.7.4

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 (96) hide show
  1. package/dist/_chunks/agent.mjs +693 -599
  2. package/dist/_chunks/agent.mjs.map +1 -1
  3. package/dist/_chunks/assemble.mjs +3 -3
  4. package/dist/_chunks/author.mjs +51 -121
  5. package/dist/_chunks/author.mjs.map +1 -1
  6. package/dist/_chunks/cache.mjs +315 -9
  7. package/dist/_chunks/cache.mjs.map +1 -1
  8. package/dist/_chunks/cache2.mjs +2 -2
  9. package/dist/_chunks/cli-helpers.mjs +3 -3
  10. package/dist/_chunks/core.mjs +7 -4
  11. package/dist/_chunks/detect.mjs +1 -1
  12. package/dist/_chunks/embedding-cache2.mjs +2 -2
  13. package/dist/_chunks/index.d.mts +305 -112
  14. package/dist/_chunks/index.d.mts.map +1 -1
  15. package/dist/_chunks/index2.d.mts +267 -32
  16. package/dist/_chunks/index2.d.mts.map +1 -1
  17. package/dist/_chunks/index3.d.mts +32 -577
  18. package/dist/_chunks/index3.d.mts.map +1 -1
  19. package/dist/_chunks/index4.d.mts +553 -0
  20. package/dist/_chunks/index4.d.mts.map +1 -0
  21. package/dist/_chunks/install.mjs +48 -88
  22. package/dist/_chunks/install.mjs.map +1 -1
  23. package/dist/_chunks/list.mjs +1 -1
  24. package/dist/_chunks/lockfile.mjs +29 -6
  25. package/dist/_chunks/lockfile.mjs.map +1 -1
  26. package/dist/_chunks/monorepo.mjs +71 -0
  27. package/dist/_chunks/monorepo.mjs.map +1 -0
  28. package/dist/_chunks/{shared.mjs → package-registry.mjs} +2 -40
  29. package/dist/_chunks/package-registry.mjs.map +1 -0
  30. package/dist/_chunks/paths.mjs +49 -0
  31. package/dist/_chunks/paths.mjs.map +1 -0
  32. package/dist/_chunks/pool2.mjs +1 -1
  33. package/dist/_chunks/prepare.mjs +1 -1
  34. package/dist/_chunks/prepare2.mjs +5 -5
  35. package/dist/_chunks/prepare2.mjs.map +1 -1
  36. package/dist/_chunks/prompts.mjs +366 -18
  37. package/dist/_chunks/prompts.mjs.map +1 -1
  38. package/dist/_chunks/search-helpers.mjs +5 -6
  39. package/dist/_chunks/search-helpers.mjs.map +1 -1
  40. package/dist/_chunks/search-interactive.mjs +1 -1
  41. package/dist/_chunks/search.mjs +1 -2
  42. package/dist/_chunks/search.mjs.map +1 -1
  43. package/dist/_chunks/semver.mjs +13 -0
  44. package/dist/_chunks/semver.mjs.map +1 -0
  45. package/dist/_chunks/skill-installer.mjs +2 -0
  46. package/dist/_chunks/skill-installer2.mjs +155 -0
  47. package/dist/_chunks/skill-installer2.mjs.map +1 -0
  48. package/dist/_chunks/skills.mjs +10 -9
  49. package/dist/_chunks/skills.mjs.map +1 -1
  50. package/dist/_chunks/sources.mjs +549 -372
  51. package/dist/_chunks/sources.mjs.map +1 -1
  52. package/dist/_chunks/sync-pipeline.mjs +952 -0
  53. package/dist/_chunks/sync-pipeline.mjs.map +1 -0
  54. package/dist/_chunks/sync-registry.mjs +19 -13
  55. package/dist/_chunks/sync-registry.mjs.map +1 -1
  56. package/dist/_chunks/sync.mjs +797 -886
  57. package/dist/_chunks/sync.mjs.map +1 -1
  58. package/dist/_chunks/sync2.mjs +4 -2
  59. package/dist/_chunks/types.d.mts +65 -77
  60. package/dist/_chunks/types.d.mts.map +1 -1
  61. package/dist/_chunks/types2.d.mts +88 -0
  62. package/dist/_chunks/types2.d.mts.map +1 -0
  63. package/dist/_chunks/uninstall.mjs +7 -8
  64. package/dist/_chunks/uninstall.mjs.map +1 -1
  65. package/dist/_chunks/upload.mjs +2 -2
  66. package/dist/_chunks/validate.mjs +1 -1
  67. package/dist/_chunks/version.mjs +3 -13
  68. package/dist/_chunks/version.mjs.map +1 -1
  69. package/dist/_chunks/wizard.mjs +2 -2
  70. package/dist/agent/index.d.mts +2 -346
  71. package/dist/agent/index.mjs +2 -3
  72. package/dist/cache/index.d.mts +2 -2
  73. package/dist/cache/index.mjs +4 -3
  74. package/dist/cli.mjs +12 -13
  75. package/dist/cli.mjs.map +1 -1
  76. package/dist/index.d.mts +5 -4
  77. package/dist/index.mjs +4 -3
  78. package/dist/prepare.mjs +2 -2
  79. package/dist/prepare.mjs.map +1 -1
  80. package/dist/retriv/index.d.mts +2 -2
  81. package/dist/retriv/worker.d.mts +1 -1
  82. package/dist/sources/index.d.mts +3 -2
  83. package/dist/sources/index.mjs +3 -3
  84. package/dist/types.d.mts +3 -3
  85. package/package.json +2 -2
  86. package/dist/_chunks/config.mjs +0 -122
  87. package/dist/_chunks/config.mjs.map +0 -1
  88. package/dist/_chunks/prefix.mjs +0 -108
  89. package/dist/_chunks/prefix.mjs.map +0 -1
  90. package/dist/_chunks/shared.mjs.map +0 -1
  91. package/dist/_chunks/skill.mjs +0 -329
  92. package/dist/_chunks/skill.mjs.map +0 -1
  93. package/dist/_chunks/sync-shared.mjs +0 -2
  94. package/dist/_chunks/sync-shared2.mjs +0 -1020
  95. package/dist/_chunks/sync-shared2.mjs.map +0 -1
  96. package/dist/agent/index.d.mts.map +0 -1
@@ -0,0 +1,155 @@
1
+ import { c as SHARED_SKILLS_DIR, d as getSharedSkillsDir } from "./paths.mjs";
2
+ import { F as registerProject } from "./cache.mjs";
3
+ import { n as linkShippedSkill, t as getShippedSkills } from "./prepare.mjs";
4
+ import { a as targets } from "./detect.mjs";
5
+ import "./agent.mjs";
6
+ import { o as linkSkillToAgents, v as todayIsoDate } from "./prompts.mjs";
7
+ import { p as isInteractive } from "./cli-helpers.mjs";
8
+ import { c as readLock, d as writeLock, l as removeLockEntry, r as findSkillDirsByPackage } from "./lockfile.mjs";
9
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
10
+ import { join, relative } from "pathe";
11
+ import * as p from "@clack/prompts";
12
+ function handleShippedSkills(packageName, version, cwd, agent, global) {
13
+ const shippedSkills = getShippedSkills(packageName, cwd, version);
14
+ if (shippedSkills.length === 0) return null;
15
+ const baseDir = resolveBaseDir(cwd, agent, global);
16
+ mkdirSync(baseDir, { recursive: true });
17
+ for (const shipped of shippedSkills) {
18
+ linkShippedSkill(baseDir, shipped.skillName, shipped.skillDir);
19
+ writeLock(baseDir, shipped.skillName, {
20
+ packageName,
21
+ version,
22
+ source: "shipped",
23
+ syncedAt: todayIsoDate(),
24
+ generator: "skilld"
25
+ });
26
+ }
27
+ if (!global) registerProject(cwd);
28
+ return {
29
+ shipped: shippedSkills,
30
+ baseDir
31
+ };
32
+ }
33
+ function resolveBaseDir(cwd, agent, global) {
34
+ if (global) return targets[agent].globalSkillsDir;
35
+ const shared = getSharedSkillsDir(cwd);
36
+ if (shared) return shared;
37
+ const agentConfig = targets[agent];
38
+ return join(cwd, agentConfig.skillsDir);
39
+ }
40
+ function installSkill(opts) {
41
+ const { cwd, agent, global, baseDir, skillDirName, lock, dedupePackageName, skipLinkAgents } = opts;
42
+ writeLock(baseDir, skillDirName, lock);
43
+ if (dedupePackageName) {
44
+ const current = readLock(baseDir);
45
+ if (current) for (const stale of findSkillDirsByPackage(current, dedupePackageName, skillDirName)) {
46
+ removeLockEntry(baseDir, stale);
47
+ const staleDir = join(baseDir, stale);
48
+ if (existsSync(staleDir)) rmSync(staleDir, { recursive: true });
49
+ }
50
+ }
51
+ const shared = global ? false : getSharedSkillsDir(cwd) ?? false;
52
+ if (shared && !skipLinkAgents) linkSkillToAgents(skillDirName, shared, cwd, agent);
53
+ if (!global) registerProject(cwd);
54
+ return { shared };
55
+ }
56
+ async function ensureProjectFiles(opts) {
57
+ const { cwd, agent, global } = opts;
58
+ if (global) return;
59
+ await ensureGitignore(opts.shared ?? getSharedSkillsDir(cwd) ? SHARED_SKILLS_DIR : targets[agent].skillsDir, cwd, false);
60
+ await ensureAgentInstructions(agent, cwd, false);
61
+ }
62
+ function linkShippedToAgents(shipped, cwd, agent, global) {
63
+ if (global) return;
64
+ const shared = getSharedSkillsDir(cwd);
65
+ if (!shared) return;
66
+ for (const s of shipped) linkSkillToAgents(s.skillName, shared, cwd, agent);
67
+ }
68
+ async function ensureGitignore(skillsDir, cwd, isGlobal) {
69
+ if (isGlobal) return;
70
+ const gitignorePath = join(cwd, ".gitignore");
71
+ const pattern = ".skilld";
72
+ if (existsSync(gitignorePath)) {
73
+ if (readFileSync(gitignorePath, "utf-8").split("\n").some((line) => line.trim() === pattern)) return;
74
+ }
75
+ if (!isInteractive()) {
76
+ const entry = `\n# Skilld references (recreated by \`skilld install\`)\n${pattern}\n`;
77
+ if (existsSync(gitignorePath)) appendFileSync(gitignorePath, `${readFileSync(gitignorePath, "utf-8").endsWith("\n") ? "" : "\n"}${entry}`);
78
+ else writeFileSync(gitignorePath, entry);
79
+ return;
80
+ }
81
+ const relSkillsDir = relative(cwd, skillsDir) || ".";
82
+ p.log.info(`\x1B[1mGit guidance:\x1B[0m\n \x1B[32m✓\x1B[0m Commit: \x1B[36m${relSkillsDir}/*/SKILL.md\x1B[0m\n \x1B[32m✓\x1B[0m Commit: \x1B[36m${relSkillsDir}/skilld-lock.yaml\x1B[0m\n \x1B[31m✗\x1B[0m Ignore: \x1B[36m${pattern}\x1B[0m \x1B[90m(recreated by \`skilld install\`)\x1B[0m`);
83
+ const add = await p.confirm({
84
+ message: `Add \`${pattern}\` to .gitignore?`,
85
+ initialValue: true
86
+ });
87
+ if (p.isCancel(add) || !add) return;
88
+ const entry = `\n# Skilld references (recreated by \`skilld install\`)\n${pattern}\n`;
89
+ if (existsSync(gitignorePath)) appendFileSync(gitignorePath, `${readFileSync(gitignorePath, "utf-8").endsWith("\n") ? "" : "\n"}${entry}`);
90
+ else writeFileSync(gitignorePath, entry);
91
+ p.log.success("Updated .gitignore");
92
+ }
93
+ const SKILLD_MARKER_START = "<!-- skilld -->";
94
+ const SKILLD_MARKER_END = "<!-- /skilld -->";
95
+ const DEFAULT_SKILL_HINT = "Before modifying code, evaluate each installed skill against the current task.\nFor each skill, determine YES/NO relevance and invoke all YES skills before proceeding.";
96
+ function getSkillInstructions(agent) {
97
+ return `${SKILLD_MARKER_START}\n${targets[agent].skillActivationHint || DEFAULT_SKILL_HINT}\n${SKILLD_MARKER_END}`;
98
+ }
99
+ function getMdcSkillInstructions(agent) {
100
+ return `---\ndescription: "Activates installed skilld skills before code changes"\nalwaysApply: true\n---\n\n${targets[agent].skillActivationHint || DEFAULT_SKILL_HINT}`;
101
+ }
102
+ async function ensureAgentInstructions(agent, cwd, isGlobal) {
103
+ if (isGlobal) return;
104
+ const agentConfig = targets[agent];
105
+ if (!agentConfig.instructionFile) return;
106
+ const filePath = join(cwd, agentConfig.instructionFile);
107
+ if (agentConfig.instructionFile.endsWith(".mdc")) {
108
+ if (existsSync(filePath)) return;
109
+ const content = `${getMdcSkillInstructions(agent)}\n`;
110
+ if (!isInteractive()) {
111
+ mkdirSync(join(filePath, ".."), { recursive: true });
112
+ writeFileSync(filePath, content);
113
+ return;
114
+ }
115
+ p.note(`This tells your agent to check installed skills before making
116
+ code changes. Without it, skills are available but may not
117
+ activate automatically.
118
+
119
+ \x1B[90m${getMdcSkillInstructions(agent)}\x1B[0m`, `Create ${agentConfig.instructionFile}`);
120
+ const add = await p.confirm({
121
+ message: `Create ${agentConfig.instructionFile} with skill activation instructions?`,
122
+ initialValue: true
123
+ });
124
+ if (p.isCancel(add) || !add) return;
125
+ mkdirSync(join(filePath, ".."), { recursive: true });
126
+ writeFileSync(filePath, content);
127
+ p.log.success(`Created ${agentConfig.instructionFile}`);
128
+ return;
129
+ }
130
+ if (existsSync(filePath)) {
131
+ if (readFileSync(filePath, "utf-8").includes("<!-- skilld -->")) return;
132
+ }
133
+ if (!isInteractive()) {
134
+ if (existsSync(filePath)) appendFileSync(filePath, `${readFileSync(filePath, "utf-8").endsWith("\n") ? "" : "\n"}\n${getSkillInstructions(agent)}\n`);
135
+ else writeFileSync(filePath, `${getSkillInstructions(agent)}\n`);
136
+ return;
137
+ }
138
+ const action = existsSync(filePath) ? "Append to" : "Create";
139
+ p.note(`This tells your agent to check installed skills before making
140
+ code changes. Without it, skills are available but may not
141
+ activate automatically.
142
+
143
+ \x1B[90m${getSkillInstructions(agent).replace(/\n/g, "\n")}\x1B[0m`, `${action} ${agentConfig.instructionFile}`);
144
+ const add = await p.confirm({
145
+ message: `${action} ${agentConfig.instructionFile} with skill activation instructions?`,
146
+ initialValue: true
147
+ });
148
+ if (p.isCancel(add) || !add) return;
149
+ if (existsSync(filePath)) appendFileSync(filePath, `${readFileSync(filePath, "utf-8").endsWith("\n") ? "" : "\n"}\n${getSkillInstructions(agent)}\n`);
150
+ else writeFileSync(filePath, `${getSkillInstructions(agent)}\n`);
151
+ p.log.success(`Updated ${agentConfig.instructionFile}`);
152
+ }
153
+ export { ensureProjectFiles as a, linkShippedToAgents as c, ensureGitignore as i, resolveBaseDir as l, SKILLD_MARKER_START as n, handleShippedSkills as o, ensureAgentInstructions as r, installSkill as s, SKILLD_MARKER_END as t };
154
+
155
+ //# sourceMappingURL=skill-installer2.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-installer2.mjs","names":["agents"],"sources":["../../src/agent/skill-installer.ts"],"sourcesContent":["/**\n * SkillInstaller: persists a built skill (lockfile, agent linking, project\n * registration) and ensures supporting project files (.gitignore, agent\n * instruction file).\n *\n * Public entry points:\n * - `installSkill` — finalize a built skill: lockfile, dedupe,\n * agent linking, project registration\n * - `ensureProjectFiles` — once-per-session: gitignore + agent\n * instructions\n * - `handleShippedSkills` — link skills shipped in node_modules\n * - `resolveBaseDir` — agent's per-project or global skills dir\n */\n\nimport type { SkillInfo } from '../core/lockfile.ts'\nimport type { AgentType } from './index.ts'\nimport { appendFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport * as p from '@clack/prompts'\nimport { join, relative } from 'pathe'\nimport { getShippedSkills, linkShippedSkill } from '../cache/index.ts'\nimport { isInteractive } from '../cli-helpers.ts'\nimport { registerProject } from '../core/config.ts'\nimport { todayIsoDate } from '../core/formatting.ts'\nimport { findSkillDirsByPackage, readLock, removeLockEntry, writeLock } from '../core/lockfile.ts'\nimport { getSharedSkillsDir, SHARED_SKILLS_DIR } from '../core/paths.ts'\nimport { agents } from './index.ts'\nimport { linkSkillToAgents } from './install.ts'\n\nexport interface HandleShippedResult {\n shipped: Array<{ skillName: string, skillDir: string }>\n baseDir: string\n}\n\n/** Link shipped skills, write lock entries, register project. Returns result or null if no shipped skills. */\nexport function handleShippedSkills(\n packageName: string,\n version: string,\n cwd: string,\n agent: AgentType,\n global: boolean,\n): HandleShippedResult | null {\n const shippedSkills = getShippedSkills(packageName, cwd, version)\n if (shippedSkills.length === 0)\n return null\n\n const baseDir = resolveBaseDir(cwd, agent, global)\n mkdirSync(baseDir, { recursive: true })\n\n for (const shipped of shippedSkills) {\n linkShippedSkill(baseDir, shipped.skillName, shipped.skillDir)\n writeLock(baseDir, shipped.skillName, {\n packageName,\n version,\n source: 'shipped',\n syncedAt: todayIsoDate(),\n generator: 'skilld',\n })\n }\n\n if (!global)\n registerProject(cwd)\n\n return { shipped: shippedSkills, baseDir }\n}\n\n/** Resolve the base skills directory for an agent */\nexport function resolveBaseDir(cwd: string, agent: AgentType, global: boolean): string {\n if (global) {\n const agentConfig = agents[agent]\n return agentConfig.globalSkillsDir\n }\n const shared = getSharedSkillsDir(cwd)\n if (shared)\n return shared\n const agentConfig = agents[agent]\n return join(cwd, agentConfig.skillsDir)\n}\n\nexport interface InstallSkillOptions {\n cwd: string\n agent: AgentType\n global: boolean\n /** Pre-resolved base skills dir (use `resolveBaseDir`). */\n baseDir: string\n skillDirName: string\n /** Lockfile entry to write. */\n lock: SkillInfo\n /**\n * If set, remove other lockfile entries that reference this package name\n * (and delete their on-disk skill dirs). Used when a sync renames or merges\n * a skill so stale entries don't linger.\n */\n dedupePackageName?: string\n /** Skip linking the shared dir to per-agent dirs (caller does it later). */\n skipLinkAgents?: boolean\n}\n\nexport interface InstallSkillResult {\n /** Shared skills dir if active, otherwise false. */\n shared: string | false\n}\n\n/**\n * Persist a built skill: write its lockfile entry, dedupe stale entries,\n * link the shared dir into each detected agent, and register the project.\n *\n * Idempotent. SKILL.md and reference files must already be on disk.\n */\nexport function installSkill(opts: InstallSkillOptions): InstallSkillResult {\n const { cwd, agent, global, baseDir, skillDirName, lock, dedupePackageName, skipLinkAgents } = opts\n\n writeLock(baseDir, skillDirName, lock)\n\n if (dedupePackageName) {\n const current = readLock(baseDir)\n if (current) {\n for (const stale of findSkillDirsByPackage(current, dedupePackageName, skillDirName)) {\n removeLockEntry(baseDir, stale)\n const staleDir = join(baseDir, stale)\n if (existsSync(staleDir))\n rmSync(staleDir, { recursive: true })\n }\n }\n }\n\n const shared: string | false = global ? false : (getSharedSkillsDir(cwd) ?? false)\n if (shared && !skipLinkAgents)\n linkSkillToAgents(skillDirName, shared, cwd, agent)\n\n if (!global)\n registerProject(cwd)\n\n return { shared }\n}\n\nexport interface EnsureProjectFilesOptions {\n cwd: string\n agent: AgentType\n global: boolean\n /**\n * Pre-computed shared dir from a prior `installSkill` call. Optional;\n * recomputed if omitted. Pass `false` to force per-agent dir.\n */\n shared?: string | false\n}\n\n/**\n * Once-per-session project file maintenance: ensures `.gitignore` has\n * `.skilld` and the agent's instruction file activates skills.\n *\n * Skipped entirely for global installs.\n */\nexport async function ensureProjectFiles(opts: EnsureProjectFilesOptions): Promise<void> {\n const { cwd, agent, global } = opts\n if (global)\n return\n\n const shared = opts.shared ?? getSharedSkillsDir(cwd)\n const skillsDir = shared ? SHARED_SKILLS_DIR : agents[agent].skillsDir\n await ensureGitignore(skillsDir, cwd, false)\n await ensureAgentInstructions(agent, cwd, false)\n}\n\n/**\n * Link shipped (in-package) skills into the per-agent dirs after\n * `handleShippedSkills` populated the shared/base dir.\n */\nexport function linkShippedToAgents(\n shipped: Array<{ skillName: string }>,\n cwd: string,\n agent: AgentType,\n global: boolean,\n): void {\n if (global)\n return\n const shared = getSharedSkillsDir(cwd)\n if (!shared)\n return\n for (const s of shipped)\n linkSkillToAgents(s.skillName, shared, cwd, agent)\n}\n\n/**\n * Check if .gitignore has `.skilld` entry.\n * If missing, prompt to add it. Skipped for global installs.\n */\nexport async function ensureGitignore(skillsDir: string, cwd: string, isGlobal: boolean): Promise<void> {\n if (isGlobal)\n return\n\n const gitignorePath = join(cwd, '.gitignore')\n const pattern = '.skilld'\n\n if (existsSync(gitignorePath)) {\n const content = readFileSync(gitignorePath, 'utf-8')\n if (content.split('\\n').some(line => line.trim() === pattern))\n return\n }\n\n if (!isInteractive()) {\n const entry = `\\n# Skilld references (recreated by \\`skilld install\\`)\\n${pattern}\\n`\n if (existsSync(gitignorePath)) {\n const existing = readFileSync(gitignorePath, 'utf-8')\n const separator = existing.endsWith('\\n') ? '' : '\\n'\n appendFileSync(gitignorePath, `${separator}${entry}`)\n }\n else {\n writeFileSync(gitignorePath, entry)\n }\n return\n }\n\n const relSkillsDir = relative(cwd, skillsDir) || '.'\n p.log.info(\n `\\x1B[1mGit guidance:\\x1B[0m\\n`\n + ` \\x1B[32m✓\\x1B[0m Commit: \\x1B[36m${relSkillsDir}/*/SKILL.md\\x1B[0m\\n`\n + ` \\x1B[32m✓\\x1B[0m Commit: \\x1B[36m${relSkillsDir}/skilld-lock.yaml\\x1B[0m\\n`\n + ` \\x1B[31m✗\\x1B[0m Ignore: \\x1B[36m${pattern}\\x1B[0m \\x1B[90m(recreated by \\`skilld install\\`)\\x1B[0m`,\n )\n\n const add = await p.confirm({\n message: `Add \\`${pattern}\\` to .gitignore?`,\n initialValue: true,\n })\n\n if (p.isCancel(add) || !add)\n return\n\n const entry = `\\n# Skilld references (recreated by \\`skilld install\\`)\\n${pattern}\\n`\n if (existsSync(gitignorePath)) {\n const existing = readFileSync(gitignorePath, 'utf-8')\n const separator = existing.endsWith('\\n') ? '' : '\\n'\n appendFileSync(gitignorePath, `${separator}${entry}`)\n }\n else {\n writeFileSync(gitignorePath, entry)\n }\n\n p.log.success('Updated .gitignore')\n}\n\nexport const SKILLD_MARKER_START = '<!-- skilld -->'\nexport const SKILLD_MARKER_END = '<!-- /skilld -->'\n\nconst DEFAULT_SKILL_HINT = 'Before modifying code, evaluate each installed skill against the current task.\\nFor each skill, determine YES/NO relevance and invoke all YES skills before proceeding.'\n\nfunction getSkillInstructions(agent: AgentType): string {\n const hint = agents[agent].skillActivationHint || DEFAULT_SKILL_HINT\n return `${SKILLD_MARKER_START}\\n${hint}\\n${SKILLD_MARKER_END}`\n}\n\nfunction getMdcSkillInstructions(agent: AgentType): string {\n const hint = agents[agent].skillActivationHint || DEFAULT_SKILL_HINT\n return `---\\ndescription: \"Activates installed skilld skills before code changes\"\\nalwaysApply: true\\n---\\n\\n${hint}`\n}\n\n/**\n * Check if agent instruction file has skilld skill-activation snippet.\n * If missing, prompt to add it. Skipped for global installs or agents without an instructionFile.\n */\nexport async function ensureAgentInstructions(agent: AgentType, cwd: string, isGlobal: boolean): Promise<void> {\n if (isGlobal)\n return\n\n const agentConfig = agents[agent]\n if (!agentConfig.instructionFile)\n return\n\n const filePath = join(cwd, agentConfig.instructionFile)\n const isMdc = agentConfig.instructionFile.endsWith('.mdc')\n\n if (isMdc) {\n if (existsSync(filePath))\n return\n\n const content = `${getMdcSkillInstructions(agent)}\\n`\n\n if (!isInteractive()) {\n mkdirSync(join(filePath, '..'), { recursive: true })\n writeFileSync(filePath, content)\n return\n }\n\n p.note(\n `This tells your agent to check installed skills before making\\n`\n + `code changes. Without it, skills are available but may not\\n`\n + `activate automatically.\\n`\n + `\\n`\n + `\\x1B[90m${getMdcSkillInstructions(agent)}\\x1B[0m`,\n `Create ${agentConfig.instructionFile}`,\n )\n\n const add = await p.confirm({\n message: `Create ${agentConfig.instructionFile} with skill activation instructions?`,\n initialValue: true,\n })\n\n if (p.isCancel(add) || !add)\n return\n\n mkdirSync(join(filePath, '..'), { recursive: true })\n writeFileSync(filePath, content)\n p.log.success(`Created ${agentConfig.instructionFile}`)\n return\n }\n\n if (existsSync(filePath)) {\n const content = readFileSync(filePath, 'utf-8')\n if (content.includes(SKILLD_MARKER_START))\n return\n }\n\n if (!isInteractive()) {\n if (existsSync(filePath)) {\n const existing = readFileSync(filePath, 'utf-8')\n const separator = existing.endsWith('\\n') ? '' : '\\n'\n appendFileSync(filePath, `${separator}\\n${getSkillInstructions(agent)}\\n`)\n }\n else {\n writeFileSync(filePath, `${getSkillInstructions(agent)}\\n`)\n }\n return\n }\n\n const fileExists = existsSync(filePath)\n const action = fileExists ? 'Append to' : 'Create'\n p.note(\n `This tells your agent to check installed skills before making\\n`\n + `code changes. Without it, skills are available but may not\\n`\n + `activate automatically.\\n`\n + `\\n`\n + `\\x1B[90m${getSkillInstructions(agent).replace(/\\n/g, '\\n')}\\x1B[0m`,\n `${action} ${agentConfig.instructionFile}`,\n )\n\n const add = await p.confirm({\n message: `${action} ${agentConfig.instructionFile} with skill activation instructions?`,\n initialValue: true,\n })\n\n if (p.isCancel(add) || !add)\n return\n\n if (existsSync(filePath)) {\n const existing = readFileSync(filePath, 'utf-8')\n const separator = existing.endsWith('\\n') ? '' : '\\n'\n appendFileSync(filePath, `${separator}\\n${getSkillInstructions(agent)}\\n`)\n }\n else {\n writeFileSync(filePath, `${getSkillInstructions(agent)}\\n`)\n }\n\n p.log.success(`Updated ${agentConfig.instructionFile}`)\n}\n"],"mappings":"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"}
@@ -1,12 +1,13 @@
1
- import "./agent.mjs";
2
- import { t as getShippedSkills } from "./prepare.mjs";
1
+ import { d as getSharedSkillsDir, m as skillInternalFile } from "./paths.mjs";
3
2
  import "./cache.mjs";
4
- import { n as getSharedSkillsDir, o as semverGt, s as semverValid } from "./shared.mjs";
5
- import { s as readLocalDependencies } from "./sources.mjs";
3
+ import { t as getShippedSkills } from "./prepare.mjs";
4
+ import { f as readLocalDependencies } from "./sources.mjs";
6
5
  import { a as targets } from "./detect.mjs";
7
- import { a as readLock, i as parseSkillFrontmatter, r as parsePackages } from "./lockfile.mjs";
8
- import { join } from "pathe";
6
+ import "./agent.mjs";
7
+ import { c as readLock, o as parsePackages, s as parseSkillFrontmatter } from "./lockfile.mjs";
8
+ import { n as semverGt, r as semverValid } from "./semver.mjs";
9
9
  import { existsSync, readdirSync } from "node:fs";
10
+ import { join } from "pathe";
10
11
  function* iterateSkills(opts = {}) {
11
12
  const { scope = "all", cwd = process.cwd() } = opts;
12
13
  const agentTypes = opts.agents ?? Object.keys(targets);
@@ -27,7 +28,7 @@ function* iterateSkills(opts = {}) {
27
28
  scope: "local"
28
29
  };
29
30
  else {
30
- const info = parseSkillFrontmatter(join(dir, ".skilld", "_SKILL.md"));
31
+ const info = parseSkillFrontmatter(skillInternalFile(dir));
31
32
  if (info?.generator === "skilld") yield {
32
33
  name,
33
34
  dir,
@@ -55,7 +56,7 @@ function* iterateSkills(opts = {}) {
55
56
  scope: "local"
56
57
  };
57
58
  else {
58
- const info = parseSkillFrontmatter(join(dir, ".skilld", "_SKILL.md"));
59
+ const info = parseSkillFrontmatter(skillInternalFile(dir));
59
60
  if (info?.generator === "skilld") yield {
60
61
  name,
61
62
  dir,
@@ -82,7 +83,7 @@ function* iterateSkills(opts = {}) {
82
83
  scope: "global"
83
84
  };
84
85
  else {
85
- const info = parseSkillFrontmatter(join(dir, ".skilld", "_SKILL.md"));
86
+ const info = parseSkillFrontmatter(skillInternalFile(dir));
86
87
  if (info?.generator === "skilld") yield {
87
88
  name,
88
89
  dir,
@@ -1 +1 @@
1
- {"version":3,"file":"skills.mjs","names":["agents"],"sources":["../../src/core/skills.ts"],"sourcesContent":["import type { AgentType } from '../agent/index.ts'\nimport type { ShippedSkill } from '../cache/storage.ts'\nimport type { SkillInfo } from './lockfile.ts'\nimport { existsSync, readdirSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { agents } from '../agent/index.ts'\nimport { getShippedSkills } from '../cache/storage.ts'\nimport { readLocalDependencies } from '../sources/index.ts'\nimport { parsePackages, parseSkillFrontmatter, readLock } from './lockfile.ts'\nimport { getSharedSkillsDir, semverGt, semverValid } from './shared.ts'\n\nexport interface SkillEntry {\n name: string\n dir: string\n agent: AgentType\n info: SkillInfo | null\n scope: 'local' | 'global'\n /** Original package name from package.json (e.g., @scope/pkg) */\n packageName?: string\n /** Latest version from package.json deps */\n latestVersion?: string\n}\n\nexport interface AvailableShippedSkill {\n /** npm package that ships the skill */\n packageName: string\n skills: ShippedSkill[]\n}\n\nexport interface ProjectState {\n skills: SkillEntry[]\n deps: Map<string, string>\n missing: string[]\n outdated: SkillEntry[]\n synced: SkillEntry[]\n /** Skills in lockfile but not matched to any local dep */\n unmatched: SkillEntry[]\n /** Dependencies that ship skills not yet installed */\n shipped: AvailableShippedSkill[]\n}\n\nexport interface IterateSkillsOptions {\n scope?: 'local' | 'global' | 'all'\n agents?: AgentType[]\n cwd?: string\n}\n\nexport function* iterateSkills(opts: IterateSkillsOptions = {}): Generator<SkillEntry> {\n const { scope = 'all', cwd = process.cwd() } = opts\n const agentTypes = opts.agents ?? (Object.keys(agents) as AgentType[])\n\n // When shared dir exists, read local skills from there (avoid duplicates from agent symlinks)\n const sharedDir = getSharedSkillsDir(cwd)\n let yieldedLocal = false\n\n if (sharedDir && (scope === 'local' || scope === 'all')) {\n yieldedLocal = true\n const lock = readLock(sharedDir)\n const entries = readdirSync(sharedDir).filter(f => !f.startsWith('.') && f !== 'skilld-lock.yaml')\n // Use first detected agent as the representative\n const firstAgent = agentTypes[0] ?? (Object.keys(agents) as AgentType[])[0]!\n for (const name of entries) {\n const dir = join(sharedDir, name)\n if (lock?.skills[name]) {\n yield { name, dir, agent: firstAgent, info: lock.skills[name], scope: 'local' }\n }\n else {\n const info = parseSkillFrontmatter(join(dir, '.skilld', '_SKILL.md'))\n if (info?.generator === 'skilld') {\n yield { name, dir, agent: firstAgent, info, scope: 'local' }\n }\n }\n }\n }\n\n for (const agentType of agentTypes) {\n const agent = agents[agentType]\n\n // Local skills (skip if already yielded from shared dir)\n if (!yieldedLocal && (scope === 'local' || scope === 'all')) {\n const localDir = join(cwd, agent.skillsDir)\n if (existsSync(localDir)) {\n const lock = readLock(localDir)\n const entries = readdirSync(localDir).filter(f => !f.startsWith('.') && f !== 'skilld-lock.yaml')\n for (const name of entries) {\n const dir = join(localDir, name)\n // Only track skills in lockfile OR with generator: \"skilld\"\n if (lock?.skills[name]) {\n yield { name, dir, agent: agentType, info: lock.skills[name], scope: 'local' }\n }\n else {\n const info = parseSkillFrontmatter(join(dir, '.skilld', '_SKILL.md'))\n if (info?.generator === 'skilld') {\n yield { name, dir, agent: agentType, info, scope: 'local' }\n }\n }\n }\n }\n }\n\n // Global skills\n if ((scope === 'global' || scope === 'all') && agent.globalSkillsDir) {\n const globalDir = agent.globalSkillsDir\n if (existsSync(globalDir)) {\n const lock = readLock(globalDir)\n const entries = readdirSync(globalDir).filter(f => !f.startsWith('.') && f !== 'skilld-lock.yaml')\n for (const name of entries) {\n const dir = join(globalDir, name)\n // Only track skills in lockfile OR with generator: \"skilld\"\n if (lock?.skills[name]) {\n yield { name, dir, agent: agentType, info: lock.skills[name], scope: 'global' }\n }\n else {\n const info = parseSkillFrontmatter(join(dir, '.skilld', '_SKILL.md'))\n if (info?.generator === 'skilld') {\n yield { name, dir, agent: agentType, info, scope: 'global' }\n }\n }\n }\n }\n }\n }\n}\n\nexport function isOutdated(skill: SkillEntry, depVersion: string): boolean {\n if (!skill.info?.version)\n return true\n\n const depClean = depVersion.replace(/^[\\^~>=<]+/, '')\n\n // Non-semver versions (e.g. '*' from catalog:/workspace: specifiers) can't be compared\n if (!semverValid(depClean))\n return false\n\n return semverGt(depClean, skill.info.version)\n}\n\nexport async function getProjectState(cwd: string = process.cwd()): Promise<ProjectState> {\n const skills = [...iterateSkills({ scope: 'local', cwd })]\n\n // Get package.json deps\n const localDeps = await readLocalDependencies(cwd).catch(() => [])\n const deps = new Map(localDeps.map(d => [d.name, d.version]))\n\n // Build unified lookup: packageName -> best skill entry\n // When multiple skills claim the same package, prefer the one with the newer version\n const skillByPkgName = new Map<string, SkillEntry>()\n const setBestSkill = (key: string, s: SkillEntry) => {\n const existing = skillByPkgName.get(key)\n if (!existing) {\n skillByPkgName.set(key, s)\n return\n }\n // Prefer the skill with the newer version (more recently synced)\n if (s.info?.version && existing.info?.version && semverValid(s.info.version) && semverValid(existing.info.version) && semverGt(s.info.version, existing.info.version))\n skillByPkgName.set(key, s)\n }\n for (const s of skills) {\n if (s.info?.packageName)\n setBestSkill(s.info.packageName, s)\n for (const pkg of parsePackages(s.info?.packages))\n setBestSkill(pkg.name, s)\n }\n\n // Also build name-based lookups, but defer to pkgName map for conflicts\n const skillByName = new Map(skills.map(s => [s.name, s]))\n\n const missing: string[] = []\n const outdated: SkillEntry[] = []\n const synced: SkillEntry[] = []\n const matchedSkillNames = new Set<string>()\n\n for (const [pkgName, version] of deps) {\n // Normalize package name (e.g., @scope/pkg -> scope-pkg)\n const normalizedName = pkgName.replace(/^@/, '').replace(/\\//g, '-')\n // Prefer packageName-based lookup (handles duplicates correctly), fall back to name-based\n const skill = skillByPkgName.get(pkgName) || skillByName.get(`${normalizedName}-skilld`) || skillByName.get(normalizedName) || skillByName.get(pkgName)\n\n if (!skill) {\n missing.push(pkgName)\n }\n else {\n matchedSkillNames.add(skill.name)\n if (isOutdated(skill, version)) {\n outdated.push({ ...skill, packageName: pkgName, latestVersion: version })\n }\n else {\n synced.push({ ...skill, packageName: pkgName, latestVersion: version })\n }\n }\n }\n\n // Skills in lockfile but not matched to any local dep\n const unmatched = skills.filter(s => !matchedSkillNames.has(s.name))\n\n // Discover dependencies that ship skills not yet installed\n const installedSkillNames = new Set(skills.map(s => s.name))\n const shipped: AvailableShippedSkill[] = []\n for (const pkgName of deps.keys()) {\n const pkgShipped = getShippedSkills(pkgName, cwd)\n const uninstalled = pkgShipped.filter(s => !installedSkillNames.has(s.skillName))\n if (uninstalled.length > 0)\n shipped.push({ packageName: pkgName, skills: uninstalled })\n }\n\n return { skills, deps, missing, outdated, synced, unmatched, shipped }\n}\n\nexport function getSkillsDir(agent: AgentType, scope: 'local' | 'global', cwd: string = process.cwd()): string {\n const agentConfig = agents[agent]\n if (scope === 'global') {\n if (!agentConfig.globalSkillsDir) {\n throw new Error(`Agent ${agent} does not support global skills`)\n }\n return agentConfig.globalSkillsDir\n }\n return getSharedSkillsDir(cwd) || join(cwd, agentConfig.skillsDir)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"skills.mjs","names":["agents"],"sources":["../../src/core/skills.ts"],"sourcesContent":["import type { AgentType } from '../agent/index.ts'\nimport type { ShippedSkill } from '../cache/storage.ts'\nimport type { SkillInfo } from './lockfile.ts'\nimport { existsSync, readdirSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { agents } from '../agent/index.ts'\nimport { getShippedSkills } from '../cache/storage.ts'\nimport { readLocalDependencies } from '../sources/index.ts'\nimport { parsePackages, parseSkillFrontmatter, readLock } from './lockfile.ts'\nimport { getSharedSkillsDir, LOCK_FILENAME, skillInternalFile } from './paths.ts'\nimport { semverGt, semverValid } from './semver.ts'\n\nexport interface SkillEntry {\n name: string\n dir: string\n agent: AgentType\n info: SkillInfo | null\n scope: 'local' | 'global'\n /** Original package name from package.json (e.g., @scope/pkg) */\n packageName?: string\n /** Latest version from package.json deps */\n latestVersion?: string\n}\n\nexport interface AvailableShippedSkill {\n /** npm package that ships the skill */\n packageName: string\n skills: ShippedSkill[]\n}\n\nexport interface ProjectState {\n skills: SkillEntry[]\n deps: Map<string, string>\n missing: string[]\n outdated: SkillEntry[]\n synced: SkillEntry[]\n /** Skills in lockfile but not matched to any local dep */\n unmatched: SkillEntry[]\n /** Dependencies that ship skills not yet installed */\n shipped: AvailableShippedSkill[]\n}\n\nexport interface IterateSkillsOptions {\n scope?: 'local' | 'global' | 'all'\n agents?: AgentType[]\n cwd?: string\n}\n\nexport function* iterateSkills(opts: IterateSkillsOptions = {}): Generator<SkillEntry> {\n const { scope = 'all', cwd = process.cwd() } = opts\n const agentTypes = opts.agents ?? (Object.keys(agents) as AgentType[])\n\n // When shared dir exists, read local skills from there (avoid duplicates from agent symlinks)\n const sharedDir = getSharedSkillsDir(cwd)\n let yieldedLocal = false\n\n if (sharedDir && (scope === 'local' || scope === 'all')) {\n yieldedLocal = true\n const lock = readLock(sharedDir)\n const entries = readdirSync(sharedDir).filter(f => !f.startsWith('.') && f !== LOCK_FILENAME)\n // Use first detected agent as the representative\n const firstAgent = agentTypes[0] ?? (Object.keys(agents) as AgentType[])[0]!\n for (const name of entries) {\n const dir = join(sharedDir, name)\n if (lock?.skills[name]) {\n yield { name, dir, agent: firstAgent, info: lock.skills[name], scope: 'local' }\n }\n else {\n const info = parseSkillFrontmatter(skillInternalFile(dir))\n if (info?.generator === 'skilld') {\n yield { name, dir, agent: firstAgent, info, scope: 'local' }\n }\n }\n }\n }\n\n for (const agentType of agentTypes) {\n const agent = agents[agentType]\n\n // Local skills (skip if already yielded from shared dir)\n if (!yieldedLocal && (scope === 'local' || scope === 'all')) {\n const localDir = join(cwd, agent.skillsDir)\n if (existsSync(localDir)) {\n const lock = readLock(localDir)\n const entries = readdirSync(localDir).filter(f => !f.startsWith('.') && f !== LOCK_FILENAME)\n for (const name of entries) {\n const dir = join(localDir, name)\n // Only track skills in lockfile OR with generator: \"skilld\"\n if (lock?.skills[name]) {\n yield { name, dir, agent: agentType, info: lock.skills[name], scope: 'local' }\n }\n else {\n const info = parseSkillFrontmatter(skillInternalFile(dir))\n if (info?.generator === 'skilld') {\n yield { name, dir, agent: agentType, info, scope: 'local' }\n }\n }\n }\n }\n }\n\n // Global skills\n if ((scope === 'global' || scope === 'all') && agent.globalSkillsDir) {\n const globalDir = agent.globalSkillsDir\n if (existsSync(globalDir)) {\n const lock = readLock(globalDir)\n const entries = readdirSync(globalDir).filter(f => !f.startsWith('.') && f !== LOCK_FILENAME)\n for (const name of entries) {\n const dir = join(globalDir, name)\n // Only track skills in lockfile OR with generator: \"skilld\"\n if (lock?.skills[name]) {\n yield { name, dir, agent: agentType, info: lock.skills[name], scope: 'global' }\n }\n else {\n const info = parseSkillFrontmatter(skillInternalFile(dir))\n if (info?.generator === 'skilld') {\n yield { name, dir, agent: agentType, info, scope: 'global' }\n }\n }\n }\n }\n }\n }\n}\n\nexport function isOutdated(skill: SkillEntry, depVersion: string): boolean {\n if (!skill.info?.version)\n return true\n\n const depClean = depVersion.replace(/^[\\^~>=<]+/, '')\n\n // Non-semver versions (e.g. '*' from catalog:/workspace: specifiers) can't be compared\n if (!semverValid(depClean))\n return false\n\n return semverGt(depClean, skill.info.version)\n}\n\nexport async function getProjectState(cwd: string = process.cwd()): Promise<ProjectState> {\n const skills = [...iterateSkills({ scope: 'local', cwd })]\n\n // Get package.json deps\n const localDeps = await readLocalDependencies(cwd).catch(() => [])\n const deps = new Map(localDeps.map(d => [d.name, d.version]))\n\n // Build unified lookup: packageName -> best skill entry\n // When multiple skills claim the same package, prefer the one with the newer version\n const skillByPkgName = new Map<string, SkillEntry>()\n const setBestSkill = (key: string, s: SkillEntry) => {\n const existing = skillByPkgName.get(key)\n if (!existing) {\n skillByPkgName.set(key, s)\n return\n }\n // Prefer the skill with the newer version (more recently synced)\n if (s.info?.version && existing.info?.version && semverValid(s.info.version) && semverValid(existing.info.version) && semverGt(s.info.version, existing.info.version))\n skillByPkgName.set(key, s)\n }\n for (const s of skills) {\n if (s.info?.packageName)\n setBestSkill(s.info.packageName, s)\n for (const pkg of parsePackages(s.info?.packages))\n setBestSkill(pkg.name, s)\n }\n\n // Also build name-based lookups, but defer to pkgName map for conflicts\n const skillByName = new Map(skills.map(s => [s.name, s]))\n\n const missing: string[] = []\n const outdated: SkillEntry[] = []\n const synced: SkillEntry[] = []\n const matchedSkillNames = new Set<string>()\n\n for (const [pkgName, version] of deps) {\n // Normalize package name (e.g., @scope/pkg -> scope-pkg)\n const normalizedName = pkgName.replace(/^@/, '').replace(/\\//g, '-')\n // Prefer packageName-based lookup (handles duplicates correctly), fall back to name-based\n const skill = skillByPkgName.get(pkgName) || skillByName.get(`${normalizedName}-skilld`) || skillByName.get(normalizedName) || skillByName.get(pkgName)\n\n if (!skill) {\n missing.push(pkgName)\n }\n else {\n matchedSkillNames.add(skill.name)\n if (isOutdated(skill, version)) {\n outdated.push({ ...skill, packageName: pkgName, latestVersion: version })\n }\n else {\n synced.push({ ...skill, packageName: pkgName, latestVersion: version })\n }\n }\n }\n\n // Skills in lockfile but not matched to any local dep\n const unmatched = skills.filter(s => !matchedSkillNames.has(s.name))\n\n // Discover dependencies that ship skills not yet installed\n const installedSkillNames = new Set(skills.map(s => s.name))\n const shipped: AvailableShippedSkill[] = []\n for (const pkgName of deps.keys()) {\n const pkgShipped = getShippedSkills(pkgName, cwd)\n const uninstalled = pkgShipped.filter(s => !installedSkillNames.has(s.skillName))\n if (uninstalled.length > 0)\n shipped.push({ packageName: pkgName, skills: uninstalled })\n }\n\n return { skills, deps, missing, outdated, synced, unmatched, shipped }\n}\n\nexport function getSkillsDir(agent: AgentType, scope: 'local' | 'global', cwd: string = process.cwd()): string {\n const agentConfig = agents[agent]\n if (scope === 'global') {\n if (!agentConfig.globalSkillsDir) {\n throw new Error(`Agent ${agent} does not support global skills`)\n }\n return agentConfig.globalSkillsDir\n }\n return getSharedSkillsDir(cwd) || join(cwd, agentConfig.skillsDir)\n}\n"],"mappings":"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"}