skilld 1.7.4 → 2.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.
- package/dist/_chunks/add.mjs +66 -0
- package/dist/_chunks/add.mjs.map +1 -0
- package/dist/_chunks/agent-prompt.mjs +88 -0
- package/dist/_chunks/agent-prompt.mjs.map +1 -0
- package/dist/_chunks/agent.mjs +81 -57
- package/dist/_chunks/agent.mjs.map +1 -1
- package/dist/_chunks/args.mjs +42 -0
- package/dist/_chunks/args.mjs.map +1 -0
- package/dist/_chunks/assemble.mjs +10 -7
- package/dist/_chunks/assemble.mjs.map +1 -1
- package/dist/_chunks/author.mjs +33 -17
- package/dist/_chunks/author.mjs.map +1 -1
- package/dist/_chunks/cache.mjs +143 -183
- package/dist/_chunks/cache.mjs.map +1 -1
- package/dist/_chunks/cache2.mjs +7 -6
- package/dist/_chunks/cache2.mjs.map +1 -1
- package/dist/_chunks/client.mjs +117 -0
- package/dist/_chunks/client.mjs.map +1 -0
- package/dist/_chunks/core.mjs +5 -5
- package/dist/_chunks/detect.mjs +53 -43
- package/dist/_chunks/detect.mjs.map +1 -1
- package/dist/_chunks/eject.mjs +69 -0
- package/dist/_chunks/eject.mjs.map +1 -0
- package/dist/_chunks/embedding-cache2.mjs +1 -1
- package/dist/_chunks/env.mjs +19 -0
- package/dist/_chunks/env.mjs.map +1 -0
- package/dist/_chunks/install-many.mjs +376 -0
- package/dist/_chunks/install-many.mjs.map +1 -0
- package/dist/_chunks/install.mjs +81 -326
- package/dist/_chunks/install.mjs.map +1 -1
- package/dist/_chunks/intro.mjs +63 -0
- package/dist/_chunks/intro.mjs.map +1 -0
- package/dist/_chunks/list.mjs +2 -2
- package/dist/_chunks/list.mjs.map +1 -1
- package/dist/_chunks/lockfile.mjs +3 -2
- package/dist/_chunks/lockfile.mjs.map +1 -1
- package/dist/_chunks/login.mjs +233 -0
- package/dist/_chunks/login.mjs.map +1 -0
- package/dist/_chunks/logout.mjs +27 -0
- package/dist/_chunks/logout.mjs.map +1 -0
- package/dist/_chunks/map.mjs +11 -0
- package/dist/_chunks/map.mjs.map +1 -0
- package/dist/_chunks/markdown.mjs +79 -54
- package/dist/_chunks/markdown.mjs.map +1 -1
- package/dist/_chunks/menu.mjs +33 -0
- package/dist/_chunks/menu.mjs.map +1 -0
- package/dist/_chunks/model-picker.mjs +61 -0
- package/dist/_chunks/model-picker.mjs.map +1 -0
- package/dist/_chunks/monorepo.mjs +4 -2
- package/dist/_chunks/monorepo.mjs.map +1 -1
- package/dist/_chunks/package-json.mjs.map +1 -1
- package/dist/_chunks/paths.mjs +3 -5
- package/dist/_chunks/paths.mjs.map +1 -1
- package/dist/_chunks/{sync-pipeline.mjs → pipeline.mjs} +346 -313
- package/dist/_chunks/pipeline.mjs.map +1 -0
- package/dist/_chunks/pool2.mjs +1 -1
- package/dist/_chunks/portable.mjs +151 -0
- package/dist/_chunks/portable.mjs.map +1 -0
- package/dist/_chunks/prepare-hook.mjs +2 -0
- package/dist/_chunks/prepare-hook2.mjs +61 -0
- package/dist/_chunks/prepare-hook2.mjs.map +1 -0
- package/dist/_chunks/prepare.mjs +47 -3
- package/dist/_chunks/prepare.mjs.map +1 -1
- package/dist/_chunks/prepare2.mjs +7 -6
- package/dist/_chunks/prepare2.mjs.map +1 -1
- package/dist/_chunks/prompts.mjs +484 -74
- package/dist/_chunks/prompts.mjs.map +1 -1
- package/dist/_chunks/pull.mjs +219 -0
- package/dist/_chunks/pull.mjs.map +1 -0
- package/dist/_chunks/regex.mjs +19 -0
- package/dist/_chunks/regex.mjs.map +1 -0
- package/dist/_chunks/retriv.mjs +2 -171
- package/dist/_chunks/retriv2.mjs +159 -0
- package/dist/_chunks/retriv2.mjs.map +1 -0
- package/dist/_chunks/sanitize.mjs +12 -9
- package/dist/_chunks/sanitize.mjs.map +1 -1
- package/dist/_chunks/search-helpers.mjs +8 -6
- package/dist/_chunks/search-helpers.mjs.map +1 -1
- package/dist/_chunks/search-interactive.mjs +23 -20
- package/dist/_chunks/search-interactive.mjs.map +1 -1
- package/dist/_chunks/search.mjs +3 -3
- package/dist/_chunks/search.mjs.map +1 -1
- package/dist/_chunks/semver.mjs +2755 -1
- package/dist/_chunks/semver.mjs.map +1 -1
- package/dist/_chunks/skill-installer2.mjs +10 -11
- package/dist/_chunks/skill-installer2.mjs.map +1 -1
- package/dist/_chunks/skills.mjs +6 -7
- package/dist/_chunks/skills.mjs.map +1 -1
- package/dist/_chunks/store.mjs +107 -0
- package/dist/_chunks/store.mjs.map +1 -0
- package/dist/_chunks/sync.mjs +411 -910
- package/dist/_chunks/sync.mjs.map +1 -1
- package/dist/_chunks/sync2.mjs +2 -5
- package/dist/_chunks/telemetry.mjs +26 -0
- package/dist/_chunks/telemetry.mjs.map +1 -0
- package/dist/_chunks/uninstall.mjs +12 -9
- package/dist/_chunks/uninstall.mjs.map +1 -1
- package/dist/_chunks/update.mjs +171 -0
- package/dist/_chunks/update.mjs.map +1 -0
- package/dist/_chunks/upload.mjs +3 -3
- package/dist/_chunks/validate.mjs +1 -1
- package/dist/_chunks/version.mjs +16 -17
- package/dist/_chunks/version.mjs.map +1 -1
- package/dist/_chunks/whoami.mjs +21 -0
- package/dist/_chunks/whoami.mjs.map +1 -0
- package/dist/_chunks/wizard.mjs +2 -190
- package/dist/_chunks/wizard2.mjs +200 -0
- package/dist/_chunks/wizard2.mjs.map +1 -0
- package/dist/cli.mjs +72 -53
- package/dist/cli.mjs.map +1 -1
- package/dist/prepare.mjs +4 -3
- package/dist/prepare.mjs.map +1 -1
- package/dist/retriv/worker.d.mts +5 -1
- package/dist/retriv/worker.d.mts.map +1 -1
- package/dist/retriv/worker.mjs +1 -1
- package/package.json +19 -28
- package/dist/_chunks/author-group.mjs +0 -17
- package/dist/_chunks/author-group.mjs.map +0 -1
- package/dist/_chunks/cli-helpers.mjs +0 -335
- package/dist/_chunks/cli-helpers.mjs.map +0 -1
- package/dist/_chunks/cli-helpers2.mjs +0 -2
- package/dist/_chunks/index.d.mts +0 -344
- package/dist/_chunks/index.d.mts.map +0 -1
- package/dist/_chunks/index2.d.mts +0 -279
- package/dist/_chunks/index2.d.mts.map +0 -1
- package/dist/_chunks/index3.d.mts +0 -44
- package/dist/_chunks/index3.d.mts.map +0 -1
- package/dist/_chunks/index4.d.mts +0 -553
- package/dist/_chunks/index4.d.mts.map +0 -1
- package/dist/_chunks/package-registry.mjs +0 -465
- package/dist/_chunks/package-registry.mjs.map +0 -1
- package/dist/_chunks/retriv.mjs.map +0 -1
- package/dist/_chunks/setup.mjs +0 -17
- package/dist/_chunks/setup.mjs.map +0 -1
- package/dist/_chunks/sources.mjs +0 -2654
- package/dist/_chunks/sources.mjs.map +0 -1
- package/dist/_chunks/sync-pipeline.mjs.map +0 -1
- package/dist/_chunks/sync-registry.mjs +0 -65
- package/dist/_chunks/sync-registry.mjs.map +0 -1
- package/dist/_chunks/types.d.mts +0 -76
- package/dist/_chunks/types.d.mts.map +0 -1
- package/dist/_chunks/types2.d.mts +0 -88
- package/dist/_chunks/types2.d.mts.map +0 -1
- package/dist/_chunks/wizard.mjs.map +0 -1
- package/dist/agent/index.d.mts +0 -2
- package/dist/agent/index.mjs +0 -4
- package/dist/cache/index.d.mts +0 -2
- package/dist/cache/index.mjs +0 -5
- package/dist/index.d.mts +0 -6
- package/dist/index.mjs +0 -6
- package/dist/retriv/index.d.mts +0 -3
- package/dist/retriv/index.mjs +0 -2
- package/dist/sources/index.d.mts +0 -3
- package/dist/sources/index.mjs +0 -3
- package/dist/types.d.mts +0 -4
- package/dist/types.mjs +0 -1
|
@@ -1 +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
|
+
{"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 { styleText } from 'node:util'\nimport * as p from '@clack/prompts'\nimport { join, relative } from 'pathe'\nimport { isInteractive } from '../cli/env.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 { getShippedSkills, linkShippedSkill } from '../core/prepare.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 `${styleText('bold', 'Git guidance:')}\\n`\n + ` ${styleText('green', '✓')} Commit: ${styleText('cyan', `${relSkillsDir}/*/SKILL.md`)}\\n`\n + ` ${styleText('green', '✓')} Commit: ${styleText('cyan', `${relSkillsDir}/skilld-lock.yaml`)}\\n`\n + ` ${styleText('red', '✗')} Ignore: ${styleText('cyan', pattern)} ${styleText('gray', '(recreated by `skilld install`)')}`,\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 styleText('gray', getMdcSkillInstructions(agent))}`,\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 styleText('gray', getSkillInstructions(agent).replace(/\\n/g, '\\n'))}`,\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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"}
|
package/dist/_chunks/skills.mjs
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { d as getSharedSkillsDir, m as skillInternalFile } from "./paths.mjs";
|
|
2
|
-
import "./cache.mjs";
|
|
3
|
-
import { t as getShippedSkills } from "./prepare.mjs";
|
|
4
|
-
import { f as readLocalDependencies } from "./sources.mjs";
|
|
5
1
|
import { a as targets } from "./detect.mjs";
|
|
6
2
|
import "./agent.mjs";
|
|
3
|
+
import { f as getSharedSkillsDir, h as skillInternalFile } from "./paths.mjs";
|
|
4
|
+
import { c as NPM_SCOPE_PREFIX_RE, h as VERSION_RANGE_PREFIX_RE } from "./regex.mjs";
|
|
5
|
+
import { n as getShippedSkills } from "./prepare.mjs";
|
|
6
|
+
import { h as readLocalDependencies, n as semverGt, r as semverValid } from "./semver.mjs";
|
|
7
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
8
|
import { existsSync, readdirSync } from "node:fs";
|
|
10
9
|
import { join } from "pathe";
|
|
11
10
|
function* iterateSkills(opts = {}) {
|
|
@@ -99,7 +98,7 @@ function* iterateSkills(opts = {}) {
|
|
|
99
98
|
}
|
|
100
99
|
function isOutdated(skill, depVersion) {
|
|
101
100
|
if (!skill.info?.version) return true;
|
|
102
|
-
const depClean = depVersion.replace(
|
|
101
|
+
const depClean = depVersion.replace(VERSION_RANGE_PREFIX_RE, "");
|
|
103
102
|
if (!semverValid(depClean)) return false;
|
|
104
103
|
return semverGt(depClean, skill.info.version);
|
|
105
104
|
}
|
|
@@ -129,7 +128,7 @@ async function getProjectState(cwd = process.cwd()) {
|
|
|
129
128
|
const synced = [];
|
|
130
129
|
const matchedSkillNames = /* @__PURE__ */ new Set();
|
|
131
130
|
for (const [pkgName, version] of deps) {
|
|
132
|
-
const normalizedName = pkgName.replace(
|
|
131
|
+
const normalizedName = pkgName.replace(NPM_SCOPE_PREFIX_RE, "").replace(/\//g, "-");
|
|
133
132
|
const skill = skillByPkgName.get(pkgName) || skillByName.get(`${normalizedName}-skilld`) || skillByName.get(normalizedName) || skillByName.get(pkgName);
|
|
134
133
|
if (!skill) missing.push(pkgName);
|
|
135
134
|
else {
|
|
@@ -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, 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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"}
|
|
1
|
+
{"version":3,"file":"skills.mjs","names":["agents"],"sources":["../../src/core/skills.ts"],"sourcesContent":["import type { AgentType } from '../agent/index.ts'\nimport type { SkillInfo } from './lockfile.ts'\nimport type { ShippedSkill } from './prepare.ts'\nimport { existsSync, readdirSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { agents } from '../agent/index.ts'\nimport { readLocalDependencies } from '../sources/index.ts'\nimport { parsePackages, parseSkillFrontmatter, readLock } from './lockfile.ts'\nimport { getSharedSkillsDir, LOCK_FILENAME, skillInternalFile } from './paths.ts'\nimport { getShippedSkills } from './prepare.ts'\nimport { NPM_SCOPE_PREFIX_RE, VERSION_RANGE_PREFIX_RE } from './regex.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(VERSION_RANGE_PREFIX_RE, '')\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(NPM_SCOPE_PREFIX_RE, '').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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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { n as CACHE_DIR, t as AUTH_PATH } from "./paths.mjs";
|
|
2
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname } from "pathe";
|
|
4
|
+
const KEYRING_SERVICE = "skilld";
|
|
5
|
+
const DEFAULT_HOST = "https://skilld.dev";
|
|
6
|
+
let keyringPromise = null;
|
|
7
|
+
async function loadKeyring() {
|
|
8
|
+
if (!keyringPromise) keyringPromise = import("@napi-rs/keyring").then((m) => m.Entry).catch(() => null);
|
|
9
|
+
return keyringPromise;
|
|
10
|
+
}
|
|
11
|
+
function readMarker() {
|
|
12
|
+
if (!existsSync(AUTH_PATH)) return null;
|
|
13
|
+
const raw = readFileSync(AUTH_PATH, "utf8").trim();
|
|
14
|
+
if (!raw) return null;
|
|
15
|
+
return JSON.parse(raw);
|
|
16
|
+
}
|
|
17
|
+
function writeMarker(marker) {
|
|
18
|
+
if (!existsSync(CACHE_DIR)) mkdirSync(dirname(AUTH_PATH), { recursive: true });
|
|
19
|
+
writeFileSync(AUTH_PATH, `${JSON.stringify(marker, null, 2)}\n`, { mode: 384 });
|
|
20
|
+
chmodSync(AUTH_PATH, 384);
|
|
21
|
+
}
|
|
22
|
+
async function saveSession(session) {
|
|
23
|
+
const Keyring = await loadKeyring();
|
|
24
|
+
const host = session.host ?? DEFAULT_HOST;
|
|
25
|
+
if (Keyring) {
|
|
26
|
+
new Keyring(KEYRING_SERVICE, `access:${session.login}`).setPassword(session.tokens.accessToken);
|
|
27
|
+
if (session.tokens.refreshToken) new Keyring(KEYRING_SERVICE, `refresh:${session.login}`).setPassword(session.tokens.refreshToken);
|
|
28
|
+
writeMarker({
|
|
29
|
+
scheme: "keychain",
|
|
30
|
+
login: session.login,
|
|
31
|
+
expiresAt: session.expiresAt,
|
|
32
|
+
host
|
|
33
|
+
});
|
|
34
|
+
return "keychain";
|
|
35
|
+
}
|
|
36
|
+
writeMarker({
|
|
37
|
+
scheme: "file",
|
|
38
|
+
login: session.login,
|
|
39
|
+
expiresAt: session.expiresAt,
|
|
40
|
+
host,
|
|
41
|
+
tokens: session.tokens
|
|
42
|
+
});
|
|
43
|
+
return "file";
|
|
44
|
+
}
|
|
45
|
+
async function loadSession() {
|
|
46
|
+
const envToken = process.env.SKILLD_TOKEN;
|
|
47
|
+
if (envToken) return {
|
|
48
|
+
scheme: "env",
|
|
49
|
+
accessToken: envToken,
|
|
50
|
+
login: process.env.SKILLD_LOGIN ?? "ci",
|
|
51
|
+
host: DEFAULT_HOST
|
|
52
|
+
};
|
|
53
|
+
const marker = readMarker();
|
|
54
|
+
if (!marker) return null;
|
|
55
|
+
if (marker.scheme === "file") {
|
|
56
|
+
if (!marker.tokens?.accessToken) return null;
|
|
57
|
+
return {
|
|
58
|
+
scheme: "file",
|
|
59
|
+
accessToken: marker.tokens.accessToken,
|
|
60
|
+
refreshToken: marker.tokens.refreshToken,
|
|
61
|
+
login: marker.login,
|
|
62
|
+
expiresAt: marker.expiresAt,
|
|
63
|
+
host: marker.host
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const Keyring = await loadKeyring();
|
|
67
|
+
if (!Keyring) return null;
|
|
68
|
+
const accessToken = new Keyring(KEYRING_SERVICE, `access:${marker.login}`).getPassword();
|
|
69
|
+
if (!accessToken) return null;
|
|
70
|
+
return {
|
|
71
|
+
scheme: "keychain",
|
|
72
|
+
accessToken,
|
|
73
|
+
refreshToken: new Keyring(KEYRING_SERVICE, `refresh:${marker.login}`).getPassword() ?? void 0,
|
|
74
|
+
login: marker.login,
|
|
75
|
+
expiresAt: marker.expiresAt,
|
|
76
|
+
host: marker.host
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async function clearSession() {
|
|
80
|
+
const marker = readMarker();
|
|
81
|
+
if (marker) {
|
|
82
|
+
const Keyring = await loadKeyring();
|
|
83
|
+
if (Keyring && marker.scheme === "keychain") {
|
|
84
|
+
new Keyring(KEYRING_SERVICE, `access:${marker.login}`).deletePassword();
|
|
85
|
+
new Keyring(KEYRING_SERVICE, `refresh:${marker.login}`).deletePassword();
|
|
86
|
+
}
|
|
87
|
+
rmSync(AUTH_PATH, { force: true });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function updateMarker(patch) {
|
|
91
|
+
const current = readMarker();
|
|
92
|
+
if (!current) return;
|
|
93
|
+
writeMarker({
|
|
94
|
+
...current,
|
|
95
|
+
...patch
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function peekMarker() {
|
|
99
|
+
try {
|
|
100
|
+
return readMarker();
|
|
101
|
+
} catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export { updateMarker as a, saveSession as i, loadSession as n, peekMarker as r, clearSession as t };
|
|
106
|
+
|
|
107
|
+
//# sourceMappingURL=store.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.mjs","names":[],"sources":["../../src/auth/store.ts"],"sourcesContent":["/**\n * Auth credential store. Prefers OS keychain via the optional `@napi-rs/keyring`\n * dependency; falls back to a 0600 JSON file at `~/.skilld/auth.json`.\n *\n * `SKILLD_TOKEN` env var trumps everything: hard expiry, no refresh, no marker\n * file. Use it in CI when keychain access isn't available.\n */\n\nimport type { AuthSession } from '../registry/client.ts'\nimport { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport { dirname } from 'pathe'\nimport { AUTH_PATH, CACHE_DIR } from '../core/paths.ts'\n\nconst KEYRING_SERVICE = 'skilld'\nconst DEFAULT_HOST = 'https://skilld.dev'\n\nexport type StorageScheme = 'keychain' | 'file' | 'env'\n\nexport interface StoredTokens {\n accessToken: string\n refreshToken?: string\n}\n\nexport interface AuthMarker {\n scheme: StorageScheme\n login: string\n expiresAt?: number\n host: string\n /** Present only when scheme = 'file' */\n tokens?: StoredTokens\n /** ISO timestamp of the last in-terminal digest the user saw. */\n /** Unix seconds, matches the server's `windowEnd` in the digest envelope. */\n lastDigestAt?: number\n}\n\nexport interface StoredSession extends AuthSession {\n scheme: StorageScheme\n}\n\ninterface KeyringEntry {\n new(service: string, account: string): {\n getPassword: () => string | null\n setPassword: (value: string) => void\n deletePassword: () => boolean\n }\n}\n\nlet keyringPromise: Promise<KeyringEntry | null> | null = null\n\nasync function loadKeyring(): Promise<KeyringEntry | null> {\n if (!keyringPromise) {\n keyringPromise = (import('@napi-rs/keyring' as any) as Promise<{ Entry: KeyringEntry }>)\n .then(m => m.Entry)\n .catch(() => null)\n }\n return keyringPromise\n}\n\nfunction readMarker(): AuthMarker | null {\n if (!existsSync(AUTH_PATH))\n return null\n const raw = readFileSync(AUTH_PATH, 'utf8').trim()\n if (!raw)\n return null\n return JSON.parse(raw) as AuthMarker\n}\n\nfunction writeMarker(marker: AuthMarker): void {\n if (!existsSync(CACHE_DIR))\n mkdirSync(dirname(AUTH_PATH), { recursive: true })\n writeFileSync(AUTH_PATH, `${JSON.stringify(marker, null, 2)}\\n`, { mode: 0o600 })\n chmodSync(AUTH_PATH, 0o600)\n}\n\nexport async function saveSession(session: AuthSession & { tokens: StoredTokens }): Promise<StorageScheme> {\n const Keyring = await loadKeyring()\n const host = session.host ?? DEFAULT_HOST\n\n if (Keyring) {\n new Keyring(KEYRING_SERVICE, `access:${session.login}`).setPassword(session.tokens.accessToken)\n if (session.tokens.refreshToken)\n new Keyring(KEYRING_SERVICE, `refresh:${session.login}`).setPassword(session.tokens.refreshToken)\n\n writeMarker({\n scheme: 'keychain',\n login: session.login,\n expiresAt: session.expiresAt,\n host,\n })\n return 'keychain'\n }\n\n writeMarker({\n scheme: 'file',\n login: session.login,\n expiresAt: session.expiresAt,\n host,\n tokens: session.tokens,\n })\n return 'file'\n}\n\nexport async function loadSession(): Promise<StoredSession | null> {\n const envToken = process.env.SKILLD_TOKEN\n if (envToken) {\n return {\n scheme: 'env',\n accessToken: envToken,\n login: process.env.SKILLD_LOGIN ?? 'ci',\n host: DEFAULT_HOST,\n }\n }\n\n const marker = readMarker()\n if (!marker)\n return null\n\n if (marker.scheme === 'file') {\n if (!marker.tokens?.accessToken)\n return null\n return {\n scheme: 'file',\n accessToken: marker.tokens.accessToken,\n refreshToken: marker.tokens.refreshToken,\n login: marker.login,\n expiresAt: marker.expiresAt,\n host: marker.host,\n }\n }\n\n const Keyring = await loadKeyring()\n if (!Keyring)\n return null\n\n const accessToken = new Keyring(KEYRING_SERVICE, `access:${marker.login}`).getPassword()\n if (!accessToken)\n return null\n const refreshToken = new Keyring(KEYRING_SERVICE, `refresh:${marker.login}`).getPassword() ?? undefined\n\n return {\n scheme: 'keychain',\n accessToken,\n refreshToken,\n login: marker.login,\n expiresAt: marker.expiresAt,\n host: marker.host,\n }\n}\n\nexport async function clearSession(): Promise<void> {\n const marker = readMarker()\n if (marker) {\n const Keyring = await loadKeyring()\n if (Keyring && marker.scheme === 'keychain') {\n new Keyring(KEYRING_SERVICE, `access:${marker.login}`).deletePassword()\n new Keyring(KEYRING_SERVICE, `refresh:${marker.login}`).deletePassword()\n }\n rmSync(AUTH_PATH, { force: true })\n }\n}\n\nexport function updateMarker(patch: Partial<AuthMarker>): void {\n const current = readMarker()\n if (!current)\n return\n writeMarker({ ...current, ...patch })\n}\n\nexport function peekMarker(): AuthMarker | null {\n try {\n return readMarker()\n }\n catch {\n return null\n }\n}\n"],"mappings":";;;AAaA,MAAM,kBAAkB;AACxB,MAAM,eAAe;AAiCrB,IAAI,iBAAsD;AAE1D,eAAe,cAA4C;CACzD,IAAI,CAAC,gBACH,iBAAkB,OAAO,oBACtB,MAAK,MAAK,EAAE,MAAM,CAClB,YAAY,KAAK;CAEtB,OAAO;;AAGT,SAAS,aAAgC;CACvC,IAAI,CAAC,WAAW,UAAU,EACxB,OAAO;CACT,MAAM,MAAM,aAAa,WAAW,OAAO,CAAC,MAAM;CAClD,IAAI,CAAC,KACH,OAAO;CACT,OAAO,KAAK,MAAM,IAAI;;AAGxB,SAAS,YAAY,QAA0B;CAC7C,IAAI,CAAC,WAAW,UAAU,EACxB,UAAU,QAAQ,UAAU,EAAE,EAAE,WAAW,MAAM,CAAC;CACpD,cAAc,WAAW,GAAG,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAO,CAAC;CACjF,UAAU,WAAW,IAAM;;AAG7B,eAAsB,YAAY,SAAyE;CACzG,MAAM,UAAU,MAAM,aAAa;CACnC,MAAM,OAAO,QAAQ,QAAQ;CAE7B,IAAI,SAAS;EACX,IAAI,QAAQ,iBAAiB,UAAU,QAAQ,QAAQ,CAAC,YAAY,QAAQ,OAAO,YAAY;EAC/F,IAAI,QAAQ,OAAO,cACjB,IAAI,QAAQ,iBAAiB,WAAW,QAAQ,QAAQ,CAAC,YAAY,QAAQ,OAAO,aAAa;EAEnG,YAAY;GACV,QAAQ;GACR,OAAO,QAAQ;GACf,WAAW,QAAQ;GACnB;GACD,CAAC;EACF,OAAO;;CAGT,YAAY;EACV,QAAQ;EACR,OAAO,QAAQ;EACf,WAAW,QAAQ;EACnB;EACA,QAAQ,QAAQ;EACjB,CAAC;CACF,OAAO;;AAGT,eAAsB,cAA6C;CACjE,MAAM,WAAW,QAAQ,IAAI;CAC7B,IAAI,UACF,OAAO;EACL,QAAQ;EACR,aAAa;EACb,OAAO,QAAQ,IAAI,gBAAgB;EACnC,MAAM;EACP;CAGH,MAAM,SAAS,YAAY;CAC3B,IAAI,CAAC,QACH,OAAO;CAET,IAAI,OAAO,WAAW,QAAQ;EAC5B,IAAI,CAAC,OAAO,QAAQ,aAClB,OAAO;EACT,OAAO;GACL,QAAQ;GACR,aAAa,OAAO,OAAO;GAC3B,cAAc,OAAO,OAAO;GAC5B,OAAO,OAAO;GACd,WAAW,OAAO;GAClB,MAAM,OAAO;GACd;;CAGH,MAAM,UAAU,MAAM,aAAa;CACnC,IAAI,CAAC,SACH,OAAO;CAET,MAAM,cAAc,IAAI,QAAQ,iBAAiB,UAAU,OAAO,QAAQ,CAAC,aAAa;CACxF,IAAI,CAAC,aACH,OAAO;CAGT,OAAO;EACL,QAAQ;EACR;EACA,cALmB,IAAI,QAAQ,iBAAiB,WAAW,OAAO,QAAQ,CAAC,aAAa,IAAI,KAAA;EAM5F,OAAO,OAAO;EACd,WAAW,OAAO;EAClB,MAAM,OAAO;EACd;;AAGH,eAAsB,eAA8B;CAClD,MAAM,SAAS,YAAY;CAC3B,IAAI,QAAQ;EACV,MAAM,UAAU,MAAM,aAAa;EACnC,IAAI,WAAW,OAAO,WAAW,YAAY;GAC3C,IAAI,QAAQ,iBAAiB,UAAU,OAAO,QAAQ,CAAC,gBAAgB;GACvE,IAAI,QAAQ,iBAAiB,WAAW,OAAO,QAAQ,CAAC,gBAAgB;;EAE1E,OAAO,WAAW,EAAE,OAAO,MAAM,CAAC;;;AAItC,SAAgB,aAAa,OAAkC;CAC7D,MAAM,UAAU,YAAY;CAC5B,IAAI,CAAC,SACH;CACF,YAAY;EAAE,GAAG;EAAS,GAAG;EAAO,CAAC;;AAGvC,SAAgB,aAAgC;CAC9C,IAAI;EACF,OAAO,YAAY;SAEf;EACJ,OAAO"}
|