claude-skills-cli 0.0.21 → 0.0.22

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 (29) hide show
  1. package/dist/{add-hook.cmd-B6iZtoPi.js → add-hook.cmd-JWw5UqA3.js} +3 -3
  2. package/dist/{add-hook.cmd-B6iZtoPi.js.map → add-hook.cmd-JWw5UqA3.js.map} +1 -1
  3. package/dist/{frontmatter-validator-DO686mla.js → dependency-validator-CQJ1hCoU.js} +159 -3
  4. package/dist/dependency-validator-CQJ1hCoU.js.map +1 -0
  5. package/dist/{doctor.cmd-CkNw6ine.js → doctor.cmd-DJpHLDCV.js} +42 -19
  6. package/dist/doctor.cmd-DJpHLDCV.js.map +1 -0
  7. package/dist/index.js +7 -7
  8. package/dist/{init.cmd-BoeuCgQP.js → init.cmd-BdqImX8b.js} +3 -3
  9. package/dist/{init.cmd-BoeuCgQP.js.map → init.cmd-BdqImX8b.js.map} +1 -1
  10. package/dist/{install.cmd-CH7yZ92g.js → install.cmd-BaP8k9d2.js} +2 -2
  11. package/dist/{install.cmd-CH7yZ92g.js.map → install.cmd-BaP8k9d2.js.map} +1 -1
  12. package/dist/{output-Dz8fk6Gu.js → output-DiffPD2u.js} +4 -2
  13. package/dist/output-DiffPD2u.js.map +1 -0
  14. package/dist/{package.cmd-CwGRHdEq.js → package.cmd-BYhkheya.js} +3 -3
  15. package/dist/{package.cmd-CwGRHdEq.js.map → package.cmd-BYhkheya.js.map} +1 -1
  16. package/dist/{stats.cmd-D1ujNiDO.js → stats.cmd-Dd46qjoV.js} +37 -4
  17. package/dist/stats.cmd-Dd46qjoV.js.map +1 -0
  18. package/dist/{templates-BQTgkXfH.js → templates-fyteNbD0.js} +5 -1
  19. package/dist/templates-fyteNbD0.js.map +1 -0
  20. package/dist/{validate.cmd-CDUJDKGs.js → validate.cmd-BxF4HNsu.js} +3 -3
  21. package/dist/{validate.cmd-CDUJDKGs.js.map → validate.cmd-BxF4HNsu.js.map} +1 -1
  22. package/dist/{validator-DV5zeeel.js → validator-Dp5x-OjP.js} +11 -3
  23. package/dist/{validator-DV5zeeel.js.map → validator-Dp5x-OjP.js.map} +1 -1
  24. package/package.json +1 -1
  25. package/dist/doctor.cmd-CkNw6ine.js.map +0 -1
  26. package/dist/frontmatter-validator-DO686mla.js.map +0 -1
  27. package/dist/output-Dz8fk6Gu.js.map +0 -1
  28. package/dist/stats.cmd-D1ujNiDO.js.map +0 -1
  29. package/dist/templates-BQTgkXfH.js.map +0 -1
@@ -1,6 +1,6 @@
1
- import { a as SIMPLE_HOOK_TEMPLATE, n as LLM_EVAL_HOOK_TEMPLATE, t as FORCED_EVAL_HOOK_TEMPLATE } from "./templates-BQTgkXfH.js";
1
+ import { a as SIMPLE_HOOK_TEMPLATE, n as LLM_EVAL_HOOK_TEMPLATE, t as FORCED_EVAL_HOOK_TEMPLATE } from "./templates-fyteNbD0.js";
2
2
  import { r as make_executable, t as ensure_dir } from "./fs-CuGv3Ob2.js";
3
- import { c as warning, n as error, o as success, r as info } from "./output-Dz8fk6Gu.js";
3
+ import { c as warning, n as error, o as success, r as info } from "./output-DiffPD2u.js";
4
4
  import { defineCommand } from "citty";
5
5
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
6
6
  import { join } from "node:path";
@@ -190,4 +190,4 @@ var add_hook_cmd_default = defineCommand({
190
190
  //#endregion
191
191
  export { add_hook_cmd_default as default };
192
192
 
193
- //# sourceMappingURL=add-hook.cmd-B6iZtoPi.js.map
193
+ //# sourceMappingURL=add-hook.cmd-JWw5UqA3.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"add-hook.cmd-B6iZtoPi.js","names":[],"sources":["../src/commands/add-hook.ts","../src/commands/add-hook.cmd.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport {\n\tFORCED_EVAL_HOOK_TEMPLATE,\n\tLLM_EVAL_HOOK_TEMPLATE,\n\tSIMPLE_HOOK_TEMPLATE,\n} from '../core/templates.js';\nimport type { AddHookOptions } from '../types.js';\nimport { ensure_dir, make_executable } from '../utils/fs.js';\nimport { error, info, success, warning } from '../utils/output.js';\n\ninterface HookHandler {\n\ttype: string;\n\tcommand?: string;\n\tprompt?: string;\n\ttimeout?: number;\n}\n\ninterface SettingsJson {\n\thooks?: {\n\t\tUserPromptSubmit?: Array<{\n\t\t\thooks: Array<HookHandler>;\n\t\t}>;\n\t\t[key: string]: unknown;\n\t};\n\tdisableAllHooks?: boolean;\n\t[key: string]: unknown;\n}\n\nconst HOOK_TYPES = {\n\t'simple-inline': {\n\t\tname: 'Simple Inline',\n\t\tsuccess_rate: '20%',\n\t\tdescription: 'Echo command in settings.json',\n\t\tcommand:\n\t\t\t\"echo 'INSTRUCTION: If the prompt matches any available skill keywords, use Skill(skill-name) to activate it.'\",\n\t\tscript: null,\n\t},\n\t'simple-script': {\n\t\tname: 'Simple Script',\n\t\tsuccess_rate: '20%',\n\t\tdescription: 'Script file with basic instruction',\n\t\tcommand: null,\n\t\tscript: 'skill-activation-simple.sh',\n\t\ttemplate: SIMPLE_HOOK_TEMPLATE,\n\t},\n\t'forced-eval': {\n\t\tname: 'Forced Evaluation',\n\t\tsuccess_rate: '84%',\n\t\tdescription: 'Mandatory 3-step evaluation process',\n\t\tcommand: null,\n\t\tscript: 'skill-activation-forced-eval.sh',\n\t\ttemplate: FORCED_EVAL_HOOK_TEMPLATE,\n\t},\n\t'llm-eval': {\n\t\tname: 'LLM Evaluation',\n\t\tsuccess_rate: '80%',\n\t\tdescription:\n\t\t\t'Claude API pre-evaluation (requires ANTHROPIC_API_KEY)',\n\t\tcommand: null,\n\t\tscript: 'skill-activation-llm-eval.sh',\n\t\ttemplate: LLM_EVAL_HOOK_TEMPLATE,\n\t},\n} as const;\n\ntype HookType = keyof typeof HOOK_TYPES;\n\nexport function add_hook_command(options: AddHookOptions = {}): void {\n\t// Default to forced-eval for best performance\n\tconst hook_type: HookType = (options.type ||\n\t\t'forced-eval') as HookType;\n\n\tif (!HOOK_TYPES[hook_type]) {\n\t\terror(`Invalid hook type: ${hook_type}`);\n\t\tinfo(\n\t\t\t'Valid types: simple-inline, simple-script, forced-eval, llm-eval',\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\tconst hook_config = HOOK_TYPES[hook_type];\n\n\t// Determine which settings file to use\n\tlet settings_path: string;\n\tlet hooks_dir: string;\n\tlet scope: string;\n\n\tif (options.local) {\n\t\t// Project-specific local (gitignored)\n\t\tsettings_path = join('.claude', 'settings.local.json');\n\t\thooks_dir = join('.claude', 'hooks');\n\t\tscope = 'project-local';\n\t} else if (options.project) {\n\t\t// Project-specific shared (committed)\n\t\tsettings_path = join('.claude', 'settings.json');\n\t\thooks_dir = join('.claude', 'hooks');\n\t\tscope = 'project';\n\t} else {\n\t\t// Global (default)\n\t\tsettings_path = join(homedir(), '.claude', 'settings.json');\n\t\thooks_dir = join(homedir(), '.claude', 'hooks');\n\t\tscope = 'global';\n\t}\n\n\tlet settings: SettingsJson = {};\n\n\t// Check if settings.json exists and load it\n\tif (existsSync(settings_path)) {\n\t\ttry {\n\t\t\tconst content = readFileSync(settings_path, 'utf-8');\n\t\t\tsettings = JSON.parse(content);\n\n\t\t\t// Warn if all hooks are disabled\n\t\t\tif (settings.disableAllHooks) {\n\t\t\t\twarning(\n\t\t\t\t\t'disableAllHooks is set to true in settings — hooks will not run',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Check if UserPromptSubmit hook already exists\n\t\t\tif (\n\t\t\t\tsettings.hooks?.UserPromptSubmit &&\n\t\t\t\tArray.isArray(settings.hooks.UserPromptSubmit) &&\n\t\t\t\tsettings.hooks.UserPromptSubmit.length > 0\n\t\t\t) {\n\t\t\t\t// Get the first (and should be only) UserPromptSubmit object\n\t\t\t\tconst userPromptSubmit = settings.hooks.UserPromptSubmit[0];\n\n\t\t\t\t// Find existing skill activation hook (check for various patterns)\n\t\t\t\tconst existing_hook = userPromptSubmit.hooks?.find(\n\t\t\t\t\t(h) =>\n\t\t\t\t\t\th.type === 'command' &&\n\t\t\t\t\t\th.command &&\n\t\t\t\t\t\t(h.command.includes('skill-activation') ||\n\t\t\t\t\t\t\th.command.includes('skill-forced-eval-hook') ||\n\t\t\t\t\t\t\th.command.includes('skill-llm-eval-hook') ||\n\t\t\t\t\t\t\th.command.includes('skill-simple-instruction-hook') ||\n\t\t\t\t\t\t\th.command.includes(\n\t\t\t\t\t\t\t\t'If the prompt matches any available skill keywords',\n\t\t\t\t\t\t\t)),\n\t\t\t\t);\n\n\t\t\t\tif (existing_hook) {\n\t\t\t\t\twarning(\n\t\t\t\t\t\t`Skill activation hook already exists in ${scope} settings`,\n\t\t\t\t\t);\n\t\t\t\t\tinfo(\n\t\t\t\t\t\t`Current hook: ${existing_hook.command || existing_hook.prompt || 'unknown'}`,\n\t\t\t\t\t);\n\t\t\t\t\tconsole.log('');\n\n\t\t\t\t\tif (options.force) {\n\t\t\t\t\t\tinfo('--force flag provided, replacing existing hook...');\n\t\t\t\t\t\t// Remove the existing hook\n\t\t\t\t\t\tuserPromptSubmit.hooks = userPromptSubmit.hooks?.filter(\n\t\t\t\t\t\t\t(h) => h !== existing_hook,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tinfo('No changes made.');\n\t\t\t\t\t\tinfo(\n\t\t\t\t\t\t\t'To replace, run with --force flag or manually remove the existing hook.',\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\terror(`Failed to parse ${settings_path}: ${String(err)}`);\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\t// Determine the hook handler to use\n\tlet hook_handler: HookHandler;\n\n\tif (hook_config.script) {\n\t\t// Script-based hook: create the script file\n\t\tconst script_path = join(hooks_dir, hook_config.script);\n\n\t\tinfo(`Creating ${hook_config.name} hook script...`);\n\n\t\ttry {\n\t\t\tensure_dir(hooks_dir);\n\n\t\t\t// Write the script file\n\t\t\tif (hook_config.template) {\n\t\t\t\twriteFileSync(script_path, hook_config.template(), 'utf-8');\n\t\t\t}\n\n\t\t\t// Make it executable\n\t\t\tmake_executable(script_path);\n\n\t\t\tsuccess(`Script created: ${script_path}`);\n\t\t} catch (err) {\n\t\t\terror(`Failed to create hook script: ${String(err)}`);\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Use relative path for project hooks, absolute for global\n\t\tconst command =\n\t\t\tscope === 'global'\n\t\t\t\t? script_path\n\t\t\t\t: `.claude/hooks/${hook_config.script}`;\n\t\thook_handler = { type: 'command', command };\n\t} else {\n\t\t// Inline command\n\t\thook_handler = { type: 'command', command: hook_config.command! };\n\t}\n\n\t// Update or create settings.json\n\tif (existsSync(settings_path)) {\n\t\t// Add to existing settings\n\t\tconst userPromptSubmit = settings.hooks?.UserPromptSubmit?.[0];\n\n\t\tif (userPromptSubmit) {\n\t\t\t// Add to existing hooks array\n\t\t\tif (!userPromptSubmit.hooks) {\n\t\t\t\tuserPromptSubmit.hooks = [];\n\t\t\t}\n\t\t\tuserPromptSubmit.hooks.push(hook_handler);\n\n\t\t\tinfo(\n\t\t\t\t`Adding ${hook_config.name} hook to existing ${scope} settings...`,\n\t\t\t);\n\t\t} else {\n\t\t\t// Create UserPromptSubmit section\n\t\t\tsettings.hooks = settings.hooks || {};\n\t\t\tsettings.hooks.UserPromptSubmit = [\n\t\t\t\t{\n\t\t\t\t\thooks: [hook_handler],\n\t\t\t\t},\n\t\t\t];\n\n\t\t\tinfo(`Adding ${hook_config.name} hook to ${scope} settings...`);\n\t\t}\n\t} else {\n\t\t// Create new settings.json\n\t\tinfo(\n\t\t\t`Creating ${scope} settings with ${hook_config.name} hook...`,\n\t\t);\n\t\tsettings = {\n\t\t\thooks: {\n\t\t\t\tUserPromptSubmit: [\n\t\t\t\t\t{\n\t\t\t\t\t\thooks: [hook_handler],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t}\n\n\t// Write settings.json\n\ttry {\n\t\tensure_dir(\n\t\t\tscope === 'global' ? join(homedir(), '.claude') : '.claude',\n\t\t);\n\t\twriteFileSync(\n\t\t\tsettings_path,\n\t\t\tJSON.stringify(settings, null, 2),\n\t\t\t'utf-8',\n\t\t);\n\t\tsuccess(\n\t\t\t`${hook_config.name} hook added successfully! (${scope})`,\n\t\t);\n\t\tconsole.log('');\n\t\tinfo(`Settings: ${settings_path}`);\n\t\tif (hook_config.script) {\n\t\t\tinfo(`Script: ${join(hooks_dir, hook_config.script)}`);\n\t\t}\n\t\tconsole.log('');\n\t\tinfo(`Hook Type: ${hook_config.name}`);\n\t\tinfo(`Success Rate: ${hook_config.success_rate}`);\n\t\tinfo(`Description: ${hook_config.description}`);\n\t\tconsole.log('');\n\n\t\tif (hook_type === 'llm-eval') {\n\t\t\twarning(\n\t\t\t\t'LLM eval hook requires ANTHROPIC_API_KEY environment variable',\n\t\t\t);\n\t\t\tinfo('Set with: export ANTHROPIC_API_KEY=your-key-here');\n\t\t\tinfo('Falls back to simple instruction if API key not found');\n\t\t\tconsole.log('');\n\t\t}\n\n\t\twarning(\n\t\t\t'Restart Claude Code for hooks to take effect (hooks are captured at startup)',\n\t\t);\n\t\tconsole.log('');\n\t\tinfo('Next steps:');\n\t\tconsole.log(\n\t\t\t' 1. Create skills with: claude-skills-cli init --name <name>',\n\t\t);\n\t\tconsole.log(\n\t\t\t' 2. Validate with: claude-skills-cli validate <path>',\n\t\t);\n\t} catch (err) {\n\t\terror(`Failed to write ${settings_path}: ${String(err)}`);\n\t\tprocess.exit(1);\n\t}\n}\n","import { defineCommand } from 'citty';\nimport { add_hook_command } from './add-hook.js';\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'add-hook',\n\t\tdescription: 'Add skill activation hook to .claude/settings.json',\n\t},\n\targs: {\n\t\tlocal: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Install in project .claude/settings.local.json',\n\t\t},\n\t\tproject: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Install in project .claude/settings.json',\n\t\t},\n\t\ttype: {\n\t\t\ttype: 'string',\n\t\t\tdescription:\n\t\t\t\t'Hook type: simple-inline|simple-script|forced-eval|llm-eval',\n\t\t},\n\t\tforce: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Replace existing hook without prompting',\n\t\t},\n\t},\n\trun({ args }) {\n\t\tadd_hook_command({\n\t\t\tlocal: args.local,\n\t\t\tproject: args.project,\n\t\t\ttype: args.type as\n\t\t\t\t| 'simple-inline'\n\t\t\t\t| 'simple-script'\n\t\t\t\t| 'forced-eval'\n\t\t\t\t| 'llm-eval'\n\t\t\t\t| undefined,\n\t\t\tforce: args.force,\n\t\t});\n\t},\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"add-hook.cmd-JWw5UqA3.js","names":[],"sources":["../src/commands/add-hook.ts","../src/commands/add-hook.cmd.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport {\n\tFORCED_EVAL_HOOK_TEMPLATE,\n\tLLM_EVAL_HOOK_TEMPLATE,\n\tSIMPLE_HOOK_TEMPLATE,\n} from '../core/templates.js';\nimport type { AddHookOptions } from '../types.js';\nimport { ensure_dir, make_executable } from '../utils/fs.js';\nimport { error, info, success, warning } from '../utils/output.js';\n\ninterface HookHandler {\n\ttype: string;\n\tcommand?: string;\n\tprompt?: string;\n\ttimeout?: number;\n}\n\ninterface SettingsJson {\n\thooks?: {\n\t\tUserPromptSubmit?: Array<{\n\t\t\thooks: Array<HookHandler>;\n\t\t}>;\n\t\t[key: string]: unknown;\n\t};\n\tdisableAllHooks?: boolean;\n\t[key: string]: unknown;\n}\n\nconst HOOK_TYPES = {\n\t'simple-inline': {\n\t\tname: 'Simple Inline',\n\t\tsuccess_rate: '20%',\n\t\tdescription: 'Echo command in settings.json',\n\t\tcommand:\n\t\t\t\"echo 'INSTRUCTION: If the prompt matches any available skill keywords, use Skill(skill-name) to activate it.'\",\n\t\tscript: null,\n\t},\n\t'simple-script': {\n\t\tname: 'Simple Script',\n\t\tsuccess_rate: '20%',\n\t\tdescription: 'Script file with basic instruction',\n\t\tcommand: null,\n\t\tscript: 'skill-activation-simple.sh',\n\t\ttemplate: SIMPLE_HOOK_TEMPLATE,\n\t},\n\t'forced-eval': {\n\t\tname: 'Forced Evaluation',\n\t\tsuccess_rate: '84%',\n\t\tdescription: 'Mandatory 3-step evaluation process',\n\t\tcommand: null,\n\t\tscript: 'skill-activation-forced-eval.sh',\n\t\ttemplate: FORCED_EVAL_HOOK_TEMPLATE,\n\t},\n\t'llm-eval': {\n\t\tname: 'LLM Evaluation',\n\t\tsuccess_rate: '80%',\n\t\tdescription:\n\t\t\t'Claude API pre-evaluation (requires ANTHROPIC_API_KEY)',\n\t\tcommand: null,\n\t\tscript: 'skill-activation-llm-eval.sh',\n\t\ttemplate: LLM_EVAL_HOOK_TEMPLATE,\n\t},\n} as const;\n\ntype HookType = keyof typeof HOOK_TYPES;\n\nexport function add_hook_command(options: AddHookOptions = {}): void {\n\t// Default to forced-eval for best performance\n\tconst hook_type: HookType = (options.type ||\n\t\t'forced-eval') as HookType;\n\n\tif (!HOOK_TYPES[hook_type]) {\n\t\terror(`Invalid hook type: ${hook_type}`);\n\t\tinfo(\n\t\t\t'Valid types: simple-inline, simple-script, forced-eval, llm-eval',\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\tconst hook_config = HOOK_TYPES[hook_type];\n\n\t// Determine which settings file to use\n\tlet settings_path: string;\n\tlet hooks_dir: string;\n\tlet scope: string;\n\n\tif (options.local) {\n\t\t// Project-specific local (gitignored)\n\t\tsettings_path = join('.claude', 'settings.local.json');\n\t\thooks_dir = join('.claude', 'hooks');\n\t\tscope = 'project-local';\n\t} else if (options.project) {\n\t\t// Project-specific shared (committed)\n\t\tsettings_path = join('.claude', 'settings.json');\n\t\thooks_dir = join('.claude', 'hooks');\n\t\tscope = 'project';\n\t} else {\n\t\t// Global (default)\n\t\tsettings_path = join(homedir(), '.claude', 'settings.json');\n\t\thooks_dir = join(homedir(), '.claude', 'hooks');\n\t\tscope = 'global';\n\t}\n\n\tlet settings: SettingsJson = {};\n\n\t// Check if settings.json exists and load it\n\tif (existsSync(settings_path)) {\n\t\ttry {\n\t\t\tconst content = readFileSync(settings_path, 'utf-8');\n\t\t\tsettings = JSON.parse(content);\n\n\t\t\t// Warn if all hooks are disabled\n\t\t\tif (settings.disableAllHooks) {\n\t\t\t\twarning(\n\t\t\t\t\t'disableAllHooks is set to true in settings — hooks will not run',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Check if UserPromptSubmit hook already exists\n\t\t\tif (\n\t\t\t\tsettings.hooks?.UserPromptSubmit &&\n\t\t\t\tArray.isArray(settings.hooks.UserPromptSubmit) &&\n\t\t\t\tsettings.hooks.UserPromptSubmit.length > 0\n\t\t\t) {\n\t\t\t\t// Get the first (and should be only) UserPromptSubmit object\n\t\t\t\tconst userPromptSubmit = settings.hooks.UserPromptSubmit[0];\n\n\t\t\t\t// Find existing skill activation hook (check for various patterns)\n\t\t\t\tconst existing_hook = userPromptSubmit.hooks?.find(\n\t\t\t\t\t(h) =>\n\t\t\t\t\t\th.type === 'command' &&\n\t\t\t\t\t\th.command &&\n\t\t\t\t\t\t(h.command.includes('skill-activation') ||\n\t\t\t\t\t\t\th.command.includes('skill-forced-eval-hook') ||\n\t\t\t\t\t\t\th.command.includes('skill-llm-eval-hook') ||\n\t\t\t\t\t\t\th.command.includes('skill-simple-instruction-hook') ||\n\t\t\t\t\t\t\th.command.includes(\n\t\t\t\t\t\t\t\t'If the prompt matches any available skill keywords',\n\t\t\t\t\t\t\t)),\n\t\t\t\t);\n\n\t\t\t\tif (existing_hook) {\n\t\t\t\t\twarning(\n\t\t\t\t\t\t`Skill activation hook already exists in ${scope} settings`,\n\t\t\t\t\t);\n\t\t\t\t\tinfo(\n\t\t\t\t\t\t`Current hook: ${existing_hook.command || existing_hook.prompt || 'unknown'}`,\n\t\t\t\t\t);\n\t\t\t\t\tconsole.log('');\n\n\t\t\t\t\tif (options.force) {\n\t\t\t\t\t\tinfo('--force flag provided, replacing existing hook...');\n\t\t\t\t\t\t// Remove the existing hook\n\t\t\t\t\t\tuserPromptSubmit.hooks = userPromptSubmit.hooks?.filter(\n\t\t\t\t\t\t\t(h) => h !== existing_hook,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tinfo('No changes made.');\n\t\t\t\t\t\tinfo(\n\t\t\t\t\t\t\t'To replace, run with --force flag or manually remove the existing hook.',\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\terror(`Failed to parse ${settings_path}: ${String(err)}`);\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\t// Determine the hook handler to use\n\tlet hook_handler: HookHandler;\n\n\tif (hook_config.script) {\n\t\t// Script-based hook: create the script file\n\t\tconst script_path = join(hooks_dir, hook_config.script);\n\n\t\tinfo(`Creating ${hook_config.name} hook script...`);\n\n\t\ttry {\n\t\t\tensure_dir(hooks_dir);\n\n\t\t\t// Write the script file\n\t\t\tif (hook_config.template) {\n\t\t\t\twriteFileSync(script_path, hook_config.template(), 'utf-8');\n\t\t\t}\n\n\t\t\t// Make it executable\n\t\t\tmake_executable(script_path);\n\n\t\t\tsuccess(`Script created: ${script_path}`);\n\t\t} catch (err) {\n\t\t\terror(`Failed to create hook script: ${String(err)}`);\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\t// Use relative path for project hooks, absolute for global\n\t\tconst command =\n\t\t\tscope === 'global'\n\t\t\t\t? script_path\n\t\t\t\t: `.claude/hooks/${hook_config.script}`;\n\t\thook_handler = { type: 'command', command };\n\t} else {\n\t\t// Inline command\n\t\thook_handler = { type: 'command', command: hook_config.command! };\n\t}\n\n\t// Update or create settings.json\n\tif (existsSync(settings_path)) {\n\t\t// Add to existing settings\n\t\tconst userPromptSubmit = settings.hooks?.UserPromptSubmit?.[0];\n\n\t\tif (userPromptSubmit) {\n\t\t\t// Add to existing hooks array\n\t\t\tif (!userPromptSubmit.hooks) {\n\t\t\t\tuserPromptSubmit.hooks = [];\n\t\t\t}\n\t\t\tuserPromptSubmit.hooks.push(hook_handler);\n\n\t\t\tinfo(\n\t\t\t\t`Adding ${hook_config.name} hook to existing ${scope} settings...`,\n\t\t\t);\n\t\t} else {\n\t\t\t// Create UserPromptSubmit section\n\t\t\tsettings.hooks = settings.hooks || {};\n\t\t\tsettings.hooks.UserPromptSubmit = [\n\t\t\t\t{\n\t\t\t\t\thooks: [hook_handler],\n\t\t\t\t},\n\t\t\t];\n\n\t\t\tinfo(`Adding ${hook_config.name} hook to ${scope} settings...`);\n\t\t}\n\t} else {\n\t\t// Create new settings.json\n\t\tinfo(\n\t\t\t`Creating ${scope} settings with ${hook_config.name} hook...`,\n\t\t);\n\t\tsettings = {\n\t\t\thooks: {\n\t\t\t\tUserPromptSubmit: [\n\t\t\t\t\t{\n\t\t\t\t\t\thooks: [hook_handler],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t}\n\n\t// Write settings.json\n\ttry {\n\t\tensure_dir(\n\t\t\tscope === 'global' ? join(homedir(), '.claude') : '.claude',\n\t\t);\n\t\twriteFileSync(\n\t\t\tsettings_path,\n\t\t\tJSON.stringify(settings, null, 2),\n\t\t\t'utf-8',\n\t\t);\n\t\tsuccess(\n\t\t\t`${hook_config.name} hook added successfully! (${scope})`,\n\t\t);\n\t\tconsole.log('');\n\t\tinfo(`Settings: ${settings_path}`);\n\t\tif (hook_config.script) {\n\t\t\tinfo(`Script: ${join(hooks_dir, hook_config.script)}`);\n\t\t}\n\t\tconsole.log('');\n\t\tinfo(`Hook Type: ${hook_config.name}`);\n\t\tinfo(`Success Rate: ${hook_config.success_rate}`);\n\t\tinfo(`Description: ${hook_config.description}`);\n\t\tconsole.log('');\n\n\t\tif (hook_type === 'llm-eval') {\n\t\t\twarning(\n\t\t\t\t'LLM eval hook requires ANTHROPIC_API_KEY environment variable',\n\t\t\t);\n\t\t\tinfo('Set with: export ANTHROPIC_API_KEY=your-key-here');\n\t\t\tinfo('Falls back to simple instruction if API key not found');\n\t\t\tconsole.log('');\n\t\t}\n\n\t\twarning(\n\t\t\t'Restart Claude Code for hooks to take effect (hooks are captured at startup)',\n\t\t);\n\t\tconsole.log('');\n\t\tinfo('Next steps:');\n\t\tconsole.log(\n\t\t\t' 1. Create skills with: claude-skills-cli init --name <name>',\n\t\t);\n\t\tconsole.log(\n\t\t\t' 2. Validate with: claude-skills-cli validate <path>',\n\t\t);\n\t} catch (err) {\n\t\terror(`Failed to write ${settings_path}: ${String(err)}`);\n\t\tprocess.exit(1);\n\t}\n}\n","import { defineCommand } from 'citty';\nimport { add_hook_command } from './add-hook.js';\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'add-hook',\n\t\tdescription: 'Add skill activation hook to .claude/settings.json',\n\t},\n\targs: {\n\t\tlocal: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Install in project .claude/settings.local.json',\n\t\t},\n\t\tproject: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Install in project .claude/settings.json',\n\t\t},\n\t\ttype: {\n\t\t\ttype: 'string',\n\t\t\tdescription:\n\t\t\t\t'Hook type: simple-inline|simple-script|forced-eval|llm-eval',\n\t\t},\n\t\tforce: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Replace existing hook without prompting',\n\t\t},\n\t},\n\trun({ args }) {\n\t\tadd_hook_command({\n\t\t\tlocal: args.local,\n\t\t\tproject: args.project,\n\t\t\ttype: args.type as\n\t\t\t\t| 'simple-inline'\n\t\t\t\t| 'simple-script'\n\t\t\t\t| 'forced-eval'\n\t\t\t\t| 'llm-eval'\n\t\t\t\t| undefined,\n\t\t\tforce: args.force,\n\t\t});\n\t},\n});\n"],"mappings":"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"}
@@ -1,3 +1,8 @@
1
+ import { u as SEMVER_REGEX } from "./output-DiffPD2u.js";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { homedir } from "node:os";
5
+ import { execSync } from "node:child_process";
1
6
  //#region src/validators/frontmatter-validator.ts
2
7
  /**
3
8
  * YAML frontmatter validation for SKILL.md
@@ -60,12 +65,42 @@ function extract_frontmatter(content) {
60
65
  };
61
66
  }
62
67
  /**
68
+ * Extract array field values from raw YAML frontmatter
69
+ * Handles both inline [a, b] and YAML list (- item) formats
70
+ */
71
+ function extract_array_field(frontmatter, field) {
72
+ const lines = frontmatter.split("\n");
73
+ let field_index = -1;
74
+ for (let i = 0; i < lines.length; i++) if (lines[i].match(new RegExp(`^${field}:`))) {
75
+ field_index = i;
76
+ break;
77
+ }
78
+ if (field_index === -1) return null;
79
+ const value = lines[field_index].replace(new RegExp(`^${field}:\\s*`), "").trim();
80
+ if (value.startsWith("[")) {
81
+ const inner = value.slice(1, value.lastIndexOf("]"));
82
+ if (!inner.trim()) return [];
83
+ return inner.split(",").map((s) => s.trim().replace(/^["']|["']$/g, ""));
84
+ }
85
+ if (!value) {
86
+ const items = [];
87
+ for (let i = field_index + 1; i < lines.length; i++) {
88
+ const item_match = lines[i].match(/^\s+-\s+(.+)/);
89
+ if (item_match) items.push(item_match[1].trim().replace(/^["']|["']$/g, ""));
90
+ else if (lines[i].match(/^[a-z]/)) break;
91
+ }
92
+ return items;
93
+ }
94
+ return null;
95
+ }
96
+ /**
63
97
  * Known frontmatter fields per Anthropic spec
64
98
  * https://code.claude.com/docs/en/skills#frontmatter-reference
65
99
  */
66
100
  const KNOWN_FRONTMATTER_FIELDS = new Set([
67
101
  "name",
68
102
  "description",
103
+ "version",
69
104
  "argument-hint",
70
105
  "disable-model-invocation",
71
106
  "user-invocable",
@@ -76,7 +111,18 @@ const KNOWN_FRONTMATTER_FIELDS = new Set([
76
111
  "agent",
77
112
  "hooks",
78
113
  "paths",
79
- "shell"
114
+ "shell",
115
+ "depends-on-skills",
116
+ "depends-on-mcp",
117
+ "depends-on-packages"
118
+ ]);
119
+ /**
120
+ * Fields that expect array values
121
+ */
122
+ const ARRAY_FIELDS = new Set([
123
+ "depends-on-skills",
124
+ "depends-on-mcp",
125
+ "depends-on-packages"
80
126
  ]);
81
127
  /**
82
128
  * Fields with constrained values
@@ -142,6 +188,13 @@ function validate_frontmatter_structure(content) {
142
188
  if (!KNOWN_FRONTMATTER_FIELDS.has(field)) validation.unknown_fields.push(field);
143
189
  const allowed = FIELD_VALUES[field];
144
190
  if (allowed && value && !allowed.includes(value)) validation.field_value_warnings.push(`'${field}' has value '${value}' (expected: ${allowed.join(", ")})`);
191
+ if (field === "version" && value) {
192
+ if (value.startsWith("v")) validation.field_value_warnings.push(`'version' should not start with 'v' prefix — use '${value.slice(1)}' instead`);
193
+ else if (!SEMVER_REGEX.test(value)) validation.field_value_warnings.push(`'version' must be valid semver (e.g. 1.0.0) — got '${value}'`);
194
+ }
195
+ if (ARRAY_FIELDS.has(field) && value) {
196
+ if (!value.startsWith("[")) validation.field_value_warnings.push(`'${field}' should be a YAML list (e.g. [item1, item2] or - item) — got plain string '${value}'`);
197
+ }
145
198
  }
146
199
  return validation;
147
200
  }
@@ -221,6 +274,109 @@ function validate_hard_limits(name, description) {
221
274
  return limits;
222
275
  }
223
276
  //#endregion
224
- export { validate_name_format as a, validate_hard_limits as i, is_description_multiline as n, validate_frontmatter_structure as r, extract_frontmatter as t };
277
+ //#region src/validators/dependency-validator.ts
278
+ /**
279
+ * Dependency validation for skill frontmatter
280
+ * Checks depends-on-skills, depends-on-mcp, depends-on-packages
281
+ */
282
+ /**
283
+ * Validate all dependency declarations in frontmatter
284
+ */
285
+ function validate_dependencies(frontmatter_raw) {
286
+ const errors = [];
287
+ const warnings = [];
288
+ const validation = {
289
+ depends_on_skills: [],
290
+ depends_on_mcp: [],
291
+ depends_on_packages: []
292
+ };
293
+ const skills = extract_array_field(frontmatter_raw, "depends-on-skills");
294
+ if (skills !== null) {
295
+ validation.depends_on_skills = check_skill_dependencies(skills);
296
+ for (const dep of validation.depends_on_skills) if (!dep.found) warnings.push(`Skill dependency '${dep.name}' not found in ~/.claude/skills/ or .claude/skills/`);
297
+ }
298
+ const mcp = extract_array_field(frontmatter_raw, "depends-on-mcp");
299
+ if (mcp !== null) {
300
+ validation.depends_on_mcp = check_mcp_dependencies(mcp);
301
+ for (const dep of validation.depends_on_mcp) if (!dep.found) warnings.push(`MCP server dependency '${dep.name}' not found in Claude settings`);
302
+ }
303
+ const packages = extract_array_field(frontmatter_raw, "depends-on-packages");
304
+ if (packages !== null) {
305
+ validation.depends_on_packages = check_package_dependencies(packages);
306
+ for (const dep of validation.depends_on_packages) if (!dep.found) warnings.push(`Package dependency '${dep.name}' not found`);
307
+ }
308
+ return {
309
+ errors,
310
+ warnings,
311
+ validation
312
+ };
313
+ }
314
+ /**
315
+ * Check if skill dependencies exist on disk
316
+ */
317
+ function check_skill_dependencies(names) {
318
+ const search_paths = [join(homedir(), ".claude", "skills"), join(".claude", "skills")];
319
+ return names.map((name) => {
320
+ for (const base of search_paths) if (existsSync(join(base, name, "SKILL.md"))) return {
321
+ name,
322
+ found: true,
323
+ path: join(base, name)
324
+ };
325
+ return {
326
+ name,
327
+ found: false,
328
+ path: null
329
+ };
330
+ });
331
+ }
332
+ /**
333
+ * Check if MCP server dependencies are configured in Claude settings
334
+ */
335
+ function check_mcp_dependencies(names) {
336
+ const configured_servers = /* @__PURE__ */ new Set();
337
+ const settings_paths = [
338
+ join(homedir(), ".claude", "settings.json"),
339
+ join(".claude", "settings.json"),
340
+ join(".claude", "settings.local.json")
341
+ ];
342
+ for (const settings_path of settings_paths) try {
343
+ if (!existsSync(settings_path)) continue;
344
+ const content = readFileSync(settings_path, "utf-8");
345
+ const settings = JSON.parse(content);
346
+ if (settings.mcpServers) for (const key of Object.keys(settings.mcpServers)) configured_servers.add(key);
347
+ } catch {}
348
+ return names.map((name) => ({
349
+ name,
350
+ found: configured_servers.has(name)
351
+ }));
352
+ }
353
+ /**
354
+ * Check if package dependencies are available
355
+ * Tries node_modules and system PATH
356
+ */
357
+ function check_package_dependencies(names) {
358
+ return names.map((name) => {
359
+ if (existsSync(join("node_modules", name))) return {
360
+ name,
361
+ found: true,
362
+ type: "node"
363
+ };
364
+ try {
365
+ execSync(`which ${name}`, { stdio: "pipe" });
366
+ return {
367
+ name,
368
+ found: true,
369
+ type: "system"
370
+ };
371
+ } catch {}
372
+ return {
373
+ name,
374
+ found: false,
375
+ type: "unknown"
376
+ };
377
+ });
378
+ }
379
+ //#endregion
380
+ export { has_yaml_frontmatter as a, validate_hard_limits as c, extract_frontmatter as i, validate_name_format as l, validate_dependencies as n, is_description_multiline as o, extract_array_field as r, validate_frontmatter_structure as s, check_package_dependencies as t };
225
381
 
226
- //# sourceMappingURL=frontmatter-validator-DO686mla.js.map
382
+ //# sourceMappingURL=dependency-validator-CQJ1hCoU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependency-validator-CQJ1hCoU.js","names":[],"sources":["../src/validators/frontmatter-validator.ts","../src/validators/dependency-validator.ts"],"sourcesContent":["/**\n * YAML frontmatter validation for SKILL.md\n */\n\nimport {\n\tDESCRIPTION_MAX_LENGTH,\n\tNAME_MAX_LENGTH,\n\tSEMVER_REGEX,\n} from '../constants.js';\nimport type {\n\tHardLimitValidation,\n\tNameFormatValidation,\n\tYAMLValidation,\n} from '../types.js';\n\nexport interface FrontmatterData {\n\tname: string | null;\n\tdescription: string | null;\n\tbody: string;\n\tdescription_is_multiline: boolean;\n}\n\n/**\n * Check if content has valid YAML frontmatter\n */\nexport function has_yaml_frontmatter(content: string): boolean {\n\treturn content.startsWith('---\\n') || content.startsWith('---\\r\\n');\n}\n\n/**\n * Check if description field spans multiple lines in raw YAML\n */\nexport function is_description_multiline(\n\tfrontmatter: string,\n): boolean {\n\t// Find the description line\n\tconst desc_line_match = frontmatter.match(/^description:\\s*(.*)$/m);\n\tif (!desc_line_match) {\n\t\treturn false;\n\t}\n\n\tconst value_on_same_line = desc_line_match[1].trim();\n\n\t// If there's no value on the same line as \"description:\", it's multi-line\n\tif (!value_on_same_line) {\n\t\treturn true;\n\t}\n\n\t// Check if there are continuation lines (indented lines after description:)\n\t// that are not other YAML fields\n\tconst lines = frontmatter.split('\\n');\n\tlet found_desc = false;\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst line = lines[i];\n\t\tif (line.match(/^description:/)) {\n\t\t\tfound_desc = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (found_desc) {\n\t\t\t// If next line starts with spaces/tabs and is not a comment and is not another field\n\t\t\tif (\n\t\t\t\tline.match(/^\\s+\\S/) &&\n\t\t\t\t!line.trim().startsWith('#') &&\n\t\t\t\t!line.match(/^[a-z_-]+:/)\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t// Stop checking after we hit another field or end\n\t\t\tif (line.match(/^[a-z_-]+:/)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Extract frontmatter and body from SKILL.md content\n */\nexport function extract_frontmatter(\n\tcontent: string,\n): FrontmatterData {\n\tif (!has_yaml_frontmatter(content)) {\n\t\treturn {\n\t\t\tname: null,\n\t\t\tdescription: null,\n\t\t\tbody: content,\n\t\t\tdescription_is_multiline: false,\n\t\t};\n\t}\n\n\tconst parts = content.split('---\\n');\n\tif (parts.length < 3) {\n\t\treturn {\n\t\t\tname: null,\n\t\t\tdescription: null,\n\t\t\tbody: content,\n\t\t\tdescription_is_multiline: false,\n\t\t};\n\t}\n\n\tconst frontmatter = parts[1];\n\tconst body = parts.slice(2).join('---\\n');\n\n\t// Extract name\n\tconst name_match = frontmatter.match(/name:\\s*(.+)/);\n\tconst name = name_match ? name_match[1].trim() : null;\n\n\t// Extract description\n\tconst desc_match = frontmatter.match(\n\t\t/description:\\s*(.+?)(?=\\n[a-z]+:|$)/s,\n\t);\n\tconst description = desc_match ? desc_match[1].trim() : null;\n\n\t// Check if description spans multiple lines in the raw YAML\n\tconst description_is_multiline =\n\t\tis_description_multiline(frontmatter);\n\n\treturn { name, description, body, description_is_multiline };\n}\n\n/**\n * Extract array field values from raw YAML frontmatter\n * Handles both inline [a, b] and YAML list (- item) formats\n */\nexport function extract_array_field(\n\tfrontmatter: string,\n\tfield: string,\n): string[] | null {\n\tconst lines = frontmatter.split('\\n');\n\tlet field_index = -1;\n\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tif (lines[i].match(new RegExp(`^${field}:`))) {\n\t\t\tfield_index = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (field_index === -1) return null;\n\n\tconst value = lines[field_index]\n\t\t.replace(new RegExp(`^${field}:\\\\s*`), '')\n\t\t.trim();\n\n\t// Inline bracket syntax: [item1, item2]\n\tif (value.startsWith('[')) {\n\t\tconst inner = value.slice(1, value.lastIndexOf(']'));\n\t\tif (!inner.trim()) return [];\n\t\treturn inner\n\t\t\t.split(',')\n\t\t\t.map((s) => s.trim().replace(/^[\"']|[\"']$/g, ''));\n\t}\n\n\t// Empty value — check for YAML list items on following lines\n\tif (!value) {\n\t\tconst items: string[] = [];\n\t\tfor (let i = field_index + 1; i < lines.length; i++) {\n\t\t\tconst item_match = lines[i].match(/^\\s+-\\s+(.+)/);\n\t\t\tif (item_match) {\n\t\t\t\titems.push(item_match[1].trim().replace(/^[\"']|[\"']$/g, ''));\n\t\t\t} else if (lines[i].match(/^[a-z]/)) {\n\t\t\t\tbreak; // next field\n\t\t\t}\n\t\t}\n\t\treturn items;\n\t}\n\n\t// Plain string value (not an array) — return null to signal format error\n\treturn null;\n}\n\n/**\n * Known frontmatter fields per Anthropic spec\n * https://code.claude.com/docs/en/skills#frontmatter-reference\n */\nconst KNOWN_FRONTMATTER_FIELDS = new Set([\n\t'name',\n\t'description',\n\t'version',\n\t'argument-hint',\n\t'disable-model-invocation',\n\t'user-invocable',\n\t'allowed-tools',\n\t'model',\n\t'effort',\n\t'context',\n\t'agent',\n\t'hooks',\n\t'paths',\n\t'shell',\n\t'depends-on-skills',\n\t'depends-on-mcp',\n\t'depends-on-packages',\n]);\n\n/**\n * Fields that expect array values\n */\nconst ARRAY_FIELDS = new Set([\n\t'depends-on-skills',\n\t'depends-on-mcp',\n\t'depends-on-packages',\n]);\n\n/**\n * Fields with constrained values\n */\nconst FIELD_VALUES: Record<string, readonly string[]> = {\n\teffort: ['low', 'medium', 'high', 'max'],\n\tcontext: ['fork'],\n\tshell: ['bash', 'powershell'],\n\t'disable-model-invocation': ['true', 'false'],\n\t'user-invocable': ['true', 'false'],\n};\n\n/**\n * Extract top-level field names from raw YAML frontmatter\n */\nfunction extract_field_names(\n\tfrontmatter: string,\n): Map<string, string> {\n\tconst fields = new Map<string, string>();\n\tfor (const line of frontmatter.split('\\n')) {\n\t\tconst match = line.match(/^([a-z][a-z0-9_-]*):\\s*(.*)/);\n\t\tif (match) {\n\t\t\tfields.set(match[1], match[2].trim());\n\t\t}\n\t}\n\treturn fields;\n}\n\n/**\n * Validate YAML frontmatter structure\n */\nexport function validate_frontmatter_structure(\n\tcontent: string,\n): YAMLValidation {\n\tconst validation: YAMLValidation = {\n\t\tvalid: true,\n\t\thas_frontmatter: false,\n\t\tparse_error: null,\n\t\tmissing_fields: [],\n\t\tunknown_fields: [],\n\t\tfield_value_warnings: [],\n\t};\n\n\tif (!has_yaml_frontmatter(content)) {\n\t\tvalidation.valid = false;\n\t\tvalidation.parse_error = 'Missing YAML frontmatter';\n\t\treturn validation;\n\t}\n\n\tvalidation.has_frontmatter = true;\n\n\tconst parts = content.split('---\\n');\n\tif (parts.length < 3) {\n\t\tvalidation.valid = false;\n\t\tvalidation.parse_error = 'Malformed YAML frontmatter';\n\t\treturn validation;\n\t}\n\n\tconst frontmatter = parts[1];\n\n\t// Check required fields\n\tif (!frontmatter.includes('name:')) {\n\t\tvalidation.missing_fields.push('name');\n\t\tvalidation.valid = false;\n\t}\n\n\tif (!frontmatter.includes('description:')) {\n\t\tvalidation.missing_fields.push('description');\n\t\tvalidation.valid = false;\n\t}\n\n\t// Check for unknown fields\n\tconst fields = extract_field_names(frontmatter);\n\tfor (const [field, value] of fields) {\n\t\tif (!KNOWN_FRONTMATTER_FIELDS.has(field)) {\n\t\t\tvalidation.unknown_fields!.push(field);\n\t\t}\n\n\t\t// Validate constrained field values\n\t\tconst allowed = FIELD_VALUES[field];\n\t\tif (allowed && value && !allowed.includes(value)) {\n\t\t\tvalidation.field_value_warnings!.push(\n\t\t\t\t`'${field}' has value '${value}' (expected: ${allowed.join(', ')})`,\n\t\t\t);\n\t\t}\n\n\t\t// Validate version format\n\t\tif (field === 'version' && value) {\n\t\t\tif (value.startsWith('v')) {\n\t\t\t\tvalidation.field_value_warnings!.push(\n\t\t\t\t\t`'version' should not start with 'v' prefix — use '${value.slice(1)}' instead`,\n\t\t\t\t);\n\t\t\t} else if (!SEMVER_REGEX.test(value)) {\n\t\t\t\tvalidation.field_value_warnings!.push(\n\t\t\t\t\t`'version' must be valid semver (e.g. 1.0.0) — got '${value}'`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Validate array fields have correct format\n\t\tif (ARRAY_FIELDS.has(field) && value) {\n\t\t\t// Inline bracket syntax [a, b] is valid\n\t\t\tif (!value.startsWith('[')) {\n\t\t\t\tvalidation.field_value_warnings!.push(\n\t\t\t\t\t`'${field}' should be a YAML list (e.g. [item1, item2] or - item) — got plain string '${value}'`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn validation;\n}\n\n/**\n * Validate skill name format\n */\nexport function validate_name_format(\n\tname: string,\n\tdirectory_name: string,\n): NameFormatValidation {\n\tconst validation: NameFormatValidation = {\n\t\tname,\n\t\tformat_valid: true,\n\t\tdirectory_name,\n\t\tmatches_directory: true,\n\t\terrors: [],\n\t};\n\n\t// Validate kebab-case format\n\tif (!/^[a-z0-9-]+$/.test(name)) {\n\t\tvalidation.format_valid = false;\n\t\tvalidation.errors.push(\n\t\t\t`Skill name must be lowercase kebab-case: '${name}'`,\n\t\t);\n\t}\n\n\t// Reject leading/trailing hyphens\n\tif (name.startsWith('-') || name.endsWith('-')) {\n\t\tvalidation.format_valid = false;\n\t\tvalidation.errors.push(\n\t\t\t`Skill name must not start or end with a hyphen: '${name}'`,\n\t\t);\n\t}\n\n\t// Reject consecutive hyphens\n\tif (name.includes('--')) {\n\t\tvalidation.format_valid = false;\n\t\tvalidation.errors.push(\n\t\t\t`Skill name must not contain consecutive hyphens: '${name}'`,\n\t\t);\n\t}\n\n\t// Reject reserved prefixes\n\tif (name.startsWith('claude') || name.startsWith('anthropic')) {\n\t\tvalidation.format_valid = false;\n\t\tvalidation.errors.push(\n\t\t\t`Skill name must not use reserved prefix 'claude' or 'anthropic': '${name}'`,\n\t\t);\n\t}\n\n\t// Reject XML angle brackets in name (security)\n\tif (name.includes('<') || name.includes('>')) {\n\t\tvalidation.format_valid = false;\n\t\tvalidation.errors.push(\n\t\t\t`Skill name must not contain XML angle brackets: '${name}'`,\n\t\t);\n\t}\n\n\t// Check name matches directory\n\tif (name !== directory_name) {\n\t\tvalidation.matches_directory = false;\n\t\tvalidation.errors.push(\n\t\t\t`Skill name '${name}' must match directory name '${directory_name}'`,\n\t\t);\n\t}\n\n\treturn validation;\n}\n\n/**\n * Validate hard limits for name and description\n */\nexport function validate_hard_limits(\n\tname: string | null,\n\tdescription: string | null,\n): HardLimitValidation {\n\tconst limits: HardLimitValidation = {\n\t\tname: { length: 0, limit: 64, valid: true, error: null },\n\t\tdescription: {\n\t\t\tlength: 0,\n\t\t\tlimit: DESCRIPTION_MAX_LENGTH,\n\t\t\tvalid: true,\n\t\t\terror: null,\n\t\t},\n\t};\n\n\t// Validate name length\n\tif (name) {\n\t\tlimits.name.length = name.length;\n\t\tif (name.length > NAME_MAX_LENGTH) {\n\t\t\tlimits.name.valid = false;\n\t\t\tlimits.name.error = `Skill name too long (max ${NAME_MAX_LENGTH} chars): ${name.length}`;\n\t\t}\n\t}\n\n\t// Validate description length (truncated at limit in skill listing)\n\tif (description) {\n\t\tlimits.description.length = description.length;\n\t\tif (description.length > DESCRIPTION_MAX_LENGTH) {\n\t\t\tlimits.description.valid = false;\n\t\t\tlimits.description.error = `Description too long (max ${DESCRIPTION_MAX_LENGTH} chars — Claude truncates at this limit): ${description.length}`;\n\t\t}\n\t\t// Reject XML angle brackets in description (security)\n\t\tif (description.includes('<') || description.includes('>')) {\n\t\t\tlimits.description.valid = false;\n\t\t\tlimits.description.error = `Description must not contain XML angle brackets`;\n\t\t}\n\t}\n\n\treturn limits;\n}\n","/**\n * Dependency validation for skill frontmatter\n * Checks depends-on-skills, depends-on-mcp, depends-on-packages\n */\n\nimport { execSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { DependencyValidation } from '../types.js';\nimport { extract_array_field } from './frontmatter-validator.js';\n\ninterface DependencyResult {\n\terrors: string[];\n\twarnings: string[];\n\tvalidation: DependencyValidation;\n}\n\n/**\n * Validate all dependency declarations in frontmatter\n */\nexport function validate_dependencies(\n\tfrontmatter_raw: string,\n): DependencyResult {\n\tconst errors: string[] = [];\n\tconst warnings: string[] = [];\n\tconst validation: DependencyValidation = {\n\t\tdepends_on_skills: [],\n\t\tdepends_on_mcp: [],\n\t\tdepends_on_packages: [],\n\t};\n\n\t// Parse depends-on-skills\n\tconst skills = extract_array_field(\n\t\tfrontmatter_raw,\n\t\t'depends-on-skills',\n\t);\n\tif (skills !== null) {\n\t\tvalidation.depends_on_skills = check_skill_dependencies(skills);\n\t\tfor (const dep of validation.depends_on_skills) {\n\t\t\tif (!dep.found) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Skill dependency '${dep.name}' not found in ~/.claude/skills/ or .claude/skills/`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Parse depends-on-mcp\n\tconst mcp = extract_array_field(frontmatter_raw, 'depends-on-mcp');\n\tif (mcp !== null) {\n\t\tvalidation.depends_on_mcp = check_mcp_dependencies(mcp);\n\t\tfor (const dep of validation.depends_on_mcp) {\n\t\t\tif (!dep.found) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`MCP server dependency '${dep.name}' not found in Claude settings`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Parse depends-on-packages\n\tconst packages = extract_array_field(\n\t\tfrontmatter_raw,\n\t\t'depends-on-packages',\n\t);\n\tif (packages !== null) {\n\t\tvalidation.depends_on_packages =\n\t\t\tcheck_package_dependencies(packages);\n\t\tfor (const dep of validation.depends_on_packages) {\n\t\t\tif (!dep.found) {\n\t\t\t\twarnings.push(`Package dependency '${dep.name}' not found`);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { errors, warnings, validation };\n}\n\n/**\n * Check if skill dependencies exist on disk\n */\nexport function check_skill_dependencies(\n\tnames: string[],\n): DependencyValidation['depends_on_skills'] {\n\tconst home = homedir();\n\tconst search_paths = [\n\t\tjoin(home, '.claude', 'skills'),\n\t\tjoin('.claude', 'skills'),\n\t];\n\n\treturn names.map((name) => {\n\t\tfor (const base of search_paths) {\n\t\t\tconst skill_md = join(base, name, 'SKILL.md');\n\t\t\tif (existsSync(skill_md)) {\n\t\t\t\treturn { name, found: true, path: join(base, name) };\n\t\t\t}\n\t\t}\n\t\treturn { name, found: false, path: null };\n\t});\n}\n\n/**\n * Check if MCP server dependencies are configured in Claude settings\n */\nexport function check_mcp_dependencies(\n\tnames: string[],\n): DependencyValidation['depends_on_mcp'] {\n\tconst configured_servers = new Set<string>();\n\tconst home = homedir();\n\n\tconst settings_paths = [\n\t\tjoin(home, '.claude', 'settings.json'),\n\t\tjoin('.claude', 'settings.json'),\n\t\tjoin('.claude', 'settings.local.json'),\n\t];\n\n\tfor (const settings_path of settings_paths) {\n\t\ttry {\n\t\t\tif (!existsSync(settings_path)) continue;\n\t\t\tconst content = readFileSync(settings_path, 'utf-8');\n\t\t\tconst settings = JSON.parse(content);\n\t\t\tif (settings.mcpServers) {\n\t\t\t\tfor (const key of Object.keys(settings.mcpServers)) {\n\t\t\t\t\tconfigured_servers.add(key);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Skip unparseable settings files\n\t\t}\n\t}\n\n\treturn names.map((name) => ({\n\t\tname,\n\t\tfound: configured_servers.has(name),\n\t}));\n}\n\n/**\n * Check if package dependencies are available\n * Tries node_modules and system PATH\n */\nexport function check_package_dependencies(\n\tnames: string[],\n): DependencyValidation['depends_on_packages'] {\n\treturn names.map((name) => {\n\t\t// Check node_modules\n\t\tif (existsSync(join('node_modules', name))) {\n\t\t\treturn { name, found: true, type: 'node' as const };\n\t\t}\n\n\t\t// Check system PATH\n\t\ttry {\n\t\t\texecSync(`which ${name}`, { stdio: 'pipe' });\n\t\t\treturn { name, found: true, type: 'system' as const };\n\t\t} catch {\n\t\t\t// Not in PATH\n\t\t}\n\n\t\treturn { name, found: false, type: 'unknown' as const };\n\t});\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { n as error, o as success, r as info } from "./output-Dz8fk6Gu.js";
2
- import { n as is_description_multiline, t as extract_frontmatter } from "./frontmatter-validator-DO686mla.js";
1
+ import { n as error, o as success, r as info } from "./output-DiffPD2u.js";
2
+ import { a as has_yaml_frontmatter, i as extract_frontmatter, o as is_description_multiline, r as extract_array_field, t as check_package_dependencies } from "./dependency-validator-CQJ1hCoU.js";
3
3
  import { defineCommand } from "citty";
4
4
  import { readFileSync, writeFileSync } from "node:fs";
5
5
  import { basename, join } from "node:path";
@@ -17,23 +17,46 @@ function doctor_command(options) {
17
17
  error(`Failed to read SKILL.md: ${String(err)}`);
18
18
  process.exit(1);
19
19
  }
20
- if (!extract_frontmatter(content).description_is_multiline) {
21
- success("No issues found. Description is already on a single line.");
22
- process.exit(0);
20
+ let fixes_applied = 0;
21
+ let issues_found = 0;
22
+ if (extract_frontmatter(content).description_is_multiline) {
23
+ issues_found++;
24
+ info("Found multi-line description. Fixing...");
25
+ const fixed_content = fix_multiline_description(content);
26
+ try {
27
+ writeFileSync(skill_md_path, fixed_content, "utf-8");
28
+ content = fixed_content;
29
+ fixes_applied++;
30
+ success("Fixed multi-line description");
31
+ console.log(" • Added # prettier-ignore comment before description");
32
+ console.log(" • Reflowed description to single line");
33
+ } catch (err) {
34
+ error(`Failed to write SKILL.md: ${String(err)}`);
35
+ }
23
36
  }
24
- info("Found multi-line description. Fixing...");
25
- const fixed_content = fix_multiline_description(content);
26
- try {
27
- writeFileSync(skill_md_path, fixed_content, "utf-8");
28
- success("Fixed multi-line description!");
29
- console.log("\nChanges made:");
30
- console.log(" • Added # prettier-ignore comment before description");
31
- console.log(" • Reflowed description to single line");
32
- console.log("\n✓ Run validate command to confirm the fix");
33
- process.exit(0);
34
- } catch (err) {
35
- error(`Failed to write SKILL.md: ${String(err)}`);
36
- process.exit(1);
37
+ if (has_yaml_frontmatter(content)) {
38
+ const parts = content.split("---\n");
39
+ if (parts.length >= 3) {
40
+ const frontmatter_raw = parts[1];
41
+ const packages = extract_array_field(frontmatter_raw, "depends-on-packages");
42
+ if (packages && packages.length > 0) {
43
+ const missing = check_package_dependencies(packages).filter((r) => !r.found);
44
+ if (missing.length > 0) {
45
+ issues_found++;
46
+ console.log("");
47
+ info(`Missing ${missing.length} package dependenc${missing.length === 1 ? "y" : "ies"}:`);
48
+ for (const dep of missing) console.log(` • ${dep.name}`);
49
+ console.log("\nSuggested commands:");
50
+ for (const dep of missing) console.log(` npm install ${dep.name} # or: pip install ${dep.name}`);
51
+ }
52
+ }
53
+ }
54
+ }
55
+ console.log("");
56
+ if (issues_found === 0) success("No issues found");
57
+ else {
58
+ console.log(`Found ${issues_found} issue${issues_found === 1 ? "" : "s"}, applied ${fixes_applied} fix${fixes_applied === 1 ? "" : "es"}`);
59
+ if (fixes_applied > 0) console.log("\n✓ Run validate command to confirm the fixes");
37
60
  }
38
61
  }
39
62
  /**
@@ -116,4 +139,4 @@ var doctor_cmd_default = defineCommand({
116
139
  //#endregion
117
140
  export { doctor_cmd_default as default };
118
141
 
119
- //# sourceMappingURL=doctor.cmd-CkNw6ine.js.map
142
+ //# sourceMappingURL=doctor.cmd-DJpHLDCV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.cmd-DJpHLDCV.js","names":[],"sources":["../src/commands/doctor.ts","../src/commands/doctor.cmd.ts"],"sourcesContent":["import { readFileSync, writeFileSync } from 'node:fs';\nimport { basename, join } from 'node:path';\nimport type { DoctorOptions } from '../types.js';\nimport { error, info, success } from '../utils/output.js';\nimport { check_package_dependencies } from '../validators/dependency-validator.js';\nimport {\n\textract_array_field,\n\textract_frontmatter,\n\thas_yaml_frontmatter,\n\tis_description_multiline,\n} from '../validators/frontmatter-validator.js';\n\nexport function doctor_command(options: DoctorOptions): void {\n\tconst { skill_path } = options;\n\tconst skill_name = basename(skill_path);\n\tconst skill_md_path = join(skill_path, 'SKILL.md');\n\n\tinfo(`Running doctor on: ${skill_name}`);\n\tconsole.log('='.repeat(60));\n\n\t// Read SKILL.md\n\tlet content: string;\n\ttry {\n\t\tcontent = readFileSync(skill_md_path, 'utf-8');\n\t} catch (err) {\n\t\terror(`Failed to read SKILL.md: ${String(err)}`);\n\t\tprocess.exit(1);\n\t}\n\n\tlet fixes_applied = 0;\n\tlet issues_found = 0;\n\n\t// Check 1: Multi-line description\n\tconst frontmatter_data = extract_frontmatter(content);\n\n\tif (frontmatter_data.description_is_multiline) {\n\t\tissues_found++;\n\t\tinfo('Found multi-line description. Fixing...');\n\n\t\tconst fixed_content = fix_multiline_description(content);\n\n\t\ttry {\n\t\t\twriteFileSync(skill_md_path, fixed_content, 'utf-8');\n\t\t\tcontent = fixed_content;\n\t\t\tfixes_applied++;\n\t\t\tsuccess('Fixed multi-line description');\n\t\t\tconsole.log(\n\t\t\t\t' • Added # prettier-ignore comment before description',\n\t\t\t);\n\t\t\tconsole.log(' • Reflowed description to single line');\n\t\t} catch (err) {\n\t\t\terror(`Failed to write SKILL.md: ${String(err)}`);\n\t\t}\n\t}\n\n\t// Check 2: Missing package dependencies\n\tif (has_yaml_frontmatter(content)) {\n\t\tconst parts = content.split('---\\n');\n\t\tif (parts.length >= 3) {\n\t\t\tconst frontmatter_raw = parts[1];\n\t\t\tconst packages = extract_array_field(\n\t\t\t\tfrontmatter_raw,\n\t\t\t\t'depends-on-packages',\n\t\t\t);\n\n\t\t\tif (packages && packages.length > 0) {\n\t\t\t\tconst results = check_package_dependencies(packages);\n\t\t\t\tconst missing = results.filter((r) => !r.found);\n\n\t\t\t\tif (missing.length > 0) {\n\t\t\t\t\tissues_found++;\n\t\t\t\t\tconsole.log('');\n\t\t\t\t\tinfo(\n\t\t\t\t\t\t`Missing ${missing.length} package dependenc${missing.length === 1 ? 'y' : 'ies'}:`,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const dep of missing) {\n\t\t\t\t\t\tconsole.log(` • ${dep.name}`);\n\t\t\t\t\t}\n\t\t\t\t\tconsole.log('\\nSuggested commands:');\n\t\t\t\t\tfor (const dep of missing) {\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t` npm install ${dep.name} # or: pip install ${dep.name}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Summary\n\tconsole.log('');\n\tif (issues_found === 0) {\n\t\tsuccess('No issues found');\n\t} else {\n\t\tconsole.log(\n\t\t\t`Found ${issues_found} issue${issues_found === 1 ? '' : 's'}, applied ${fixes_applied} fix${fixes_applied === 1 ? '' : 'es'}`,\n\t\t);\n\t\tif (fixes_applied > 0) {\n\t\t\tconsole.log('\\n✓ Run validate command to confirm the fixes');\n\t\t}\n\t}\n}\n\n/**\n * Fix multi-line description by adding prettier-ignore and reflowing to single line\n */\nfunction fix_multiline_description(content: string): string {\n\tconst lines = content.split('\\n');\n\tconst fixed_lines: string[] = [];\n\tlet in_frontmatter = false;\n\tlet frontmatter_count = 0;\n\tlet in_description = false;\n\tlet description_parts: string[] = [];\n\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst line = lines[i];\n\n\t\t// Track frontmatter boundaries\n\t\tif (line.trim() === '---') {\n\t\t\tfrontmatter_count++;\n\n\t\t\t// If we're closing frontmatter and still collecting description\n\t\t\tif (frontmatter_count === 2 && in_description) {\n\t\t\t\t// Write out the collected description\n\t\t\t\tconst full_description = description_parts.join(' ');\n\t\t\t\tfixed_lines.push(`description: ${full_description}`);\n\t\t\t\tdescription_parts = [];\n\t\t\t\tin_description = false;\n\t\t\t}\n\n\t\t\tin_frontmatter = frontmatter_count === 1;\n\t\t\tfixed_lines.push(line);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Not in frontmatter, just pass through\n\t\tif (!in_frontmatter) {\n\t\t\tfixed_lines.push(line);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Check if this is the description line\n\t\tif (line.match(/^description:/)) {\n\t\t\t// Check if it's already multi-line\n\t\t\tif (!is_description_multiline(lines.slice(i).join('\\n'))) {\n\t\t\t\t// Single line, just pass through\n\t\t\t\tfixed_lines.push(line);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tin_description = true;\n\n\t\t\t// Extract value on same line (if any)\n\t\t\tconst match = line.match(/^description:\\s*(.*)$/);\n\t\t\tconst value_on_line = match ? match[1].trim() : '';\n\t\t\tif (value_on_line) {\n\t\t\t\tdescription_parts.push(value_on_line);\n\t\t\t}\n\n\t\t\t// Add prettier-ignore comment\n\t\t\tfixed_lines.push('# prettier-ignore');\n\t\t\t// We'll add the description line later\n\t\t\tcontinue;\n\t\t}\n\n\t\t// If we're in description, collect continuation lines\n\t\tif (in_description) {\n\t\t\t// Stop if we hit another YAML field\n\t\t\tif (line.match(/^[a-z_-]+:/)) {\n\t\t\t\t// Done collecting description, write it out\n\t\t\t\tconst full_description = description_parts.join(' ');\n\t\t\t\tfixed_lines.push(`description: ${full_description}`);\n\t\t\t\tdescription_parts = [];\n\t\t\t\tin_description = false;\n\n\t\t\t\t// Add the current line (next field)\n\t\t\t\tfixed_lines.push(line);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Continuation line - collect it\n\t\t\tconst trimmed = line.trim();\n\t\t\tif (trimmed && !trimmed.startsWith('#')) {\n\t\t\t\tdescription_parts.push(trimmed);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Regular frontmatter line\n\t\tfixed_lines.push(line);\n\t}\n\n\t// If we ended while still in description (at end of frontmatter)\n\tif (in_description && description_parts.length > 0) {\n\t\tconst full_description = description_parts.join(' ');\n\t\tfixed_lines.push(`description: ${full_description}`);\n\t}\n\n\treturn fixed_lines.join('\\n');\n}\n","import { defineCommand } from 'citty';\nimport { doctor_command } from './doctor.js';\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'doctor',\n\t\tdescription: 'Fix common skill issues automatically',\n\t},\n\targs: {\n\t\tskill_path: {\n\t\t\ttype: 'positional',\n\t\t\tdescription: 'Path to skill directory',\n\t\t\trequired: true,\n\t\t},\n\t},\n\trun({ args }) {\n\t\tdoctor_command({ skill_path: args.skill_path });\n\t},\n});\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -12,13 +12,13 @@ runMain(defineCommand({
12
12
  description: "CLI toolkit for creating and managing Claude Agent Skills\n\nIMPORTANT FOR LLMs:\n ALWAYS run validate after creating or editing a skill:\n claude-skills-cli validate <skill-path>\n Skills MUST pass validation before use.\n Fix all errors immediately. Address warnings promptly.\n\nResources:\n Pre-built skills & hooks: https://github.com/spences10/claude-code-toolkit\n Includes a UserPromptSubmit hook for automatic skill activation."
13
13
  },
14
14
  subCommands: {
15
- init: () => import("./init.cmd-BoeuCgQP.js").then((r) => r.default),
16
- install: () => import("./install.cmd-CH7yZ92g.js").then((r) => r.default),
17
- validate: () => import("./validate.cmd-CDUJDKGs.js").then((r) => r.default),
18
- doctor: () => import("./doctor.cmd-CkNw6ine.js").then((r) => r.default),
19
- package: () => import("./package.cmd-CwGRHdEq.js").then((r) => r.default),
20
- stats: () => import("./stats.cmd-D1ujNiDO.js").then((r) => r.default),
21
- "add-hook": () => import("./add-hook.cmd-B6iZtoPi.js").then((r) => r.default)
15
+ init: () => import("./init.cmd-BdqImX8b.js").then((r) => r.default),
16
+ install: () => import("./install.cmd-BaP8k9d2.js").then((r) => r.default),
17
+ validate: () => import("./validate.cmd-BxF4HNsu.js").then((r) => r.default),
18
+ doctor: () => import("./doctor.cmd-DJpHLDCV.js").then((r) => r.default),
19
+ package: () => import("./package.cmd-BYhkheya.js").then((r) => r.default),
20
+ stats: () => import("./stats.cmd-Dd46qjoV.js").then((r) => r.default),
21
+ "add-hook": () => import("./add-hook.cmd-JWw5UqA3.js").then((r) => r.default)
22
22
  }
23
23
  }));
24
24
  //#endregion
@@ -1,6 +1,6 @@
1
- import { i as SCRIPT_TEMPLATE, o as SKILL_MD_TEMPLATE, r as REFERENCE_TEMPLATE } from "./templates-BQTgkXfH.js";
1
+ import { i as SCRIPT_TEMPLATE, o as SKILL_MD_TEMPLATE, r as REFERENCE_TEMPLATE } from "./templates-fyteNbD0.js";
2
2
  import { a as write_file, i as to_title_case, n as is_lowercase, r as make_executable, t as ensure_dir } from "./fs-CuGv3Ob2.js";
3
- import { n as error, o as success } from "./output-Dz8fk6Gu.js";
3
+ import { n as error, o as success } from "./output-DiffPD2u.js";
4
4
  import { defineCommand } from "citty";
5
5
  import { join } from "node:path";
6
6
  import { homedir } from "node:os";
@@ -105,4 +105,4 @@ var init_cmd_default = defineCommand({
105
105
  //#endregion
106
106
  export { init_cmd_default as default };
107
107
 
108
- //# sourceMappingURL=init.cmd-BoeuCgQP.js.map
108
+ //# sourceMappingURL=init.cmd-BdqImX8b.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"init.cmd-BoeuCgQP.js","names":[],"sources":["../src/commands/init.ts","../src/commands/init.cmd.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport {\n\tREFERENCE_TEMPLATE,\n\tSCRIPT_TEMPLATE,\n\tSKILL_MD_TEMPLATE,\n} from '../core/templates.js';\nimport type { InitOptions } from '../types.js';\nimport {\n\tensure_dir,\n\tis_lowercase,\n\tmake_executable,\n\tto_title_case,\n\twrite_file,\n} from '../utils/fs.js';\nimport { error, success } from '../utils/output.js';\n\nexport function init_command(options: InitOptions): void {\n\tlet skill_path: string;\n\tlet name: string;\n\tlet description: string;\n\n\t// Determine path and name\n\tif (options.path) {\n\t\tskill_path = options.path;\n\t\tname = skill_path.split('/').pop() || '';\n\t\tdescription =\n\t\t\toptions.description ||\n\t\t\t'TODO: [Domain] [operations]. Use when [trigger phrase]';\n\t} else if (options.name) {\n\t\tname = options.name;\n\t\tdescription =\n\t\t\toptions.description ||\n\t\t\t'TODO: [Domain] [operations]. Use when [trigger phrase]';\n\t\t// Use ~/.claude/skills/ for global, .claude/skills/ for project\n\t\tskill_path = options.global\n\t\t\t? join(homedir(), '.claude', 'skills', name)\n\t\t\t: join('.claude', 'skills', name);\n\t} else {\n\t\terror('Either --name or --path must be provided');\n\t\tconsole.log('\\nUsage:');\n\t\tconsole.log(\n\t\t\t' claude-skills-cli init --name my-skill --description \"Description\"',\n\t\t);\n\t\tconsole.log(\n\t\t\t' claude-skills-cli init --path /custom/path/my-skill',\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\t// Validate name format\n\tconst alphanumeric_check = name.replace(/-/g, '').replace(/_/g, '');\n\tif (!/^[a-z0-9]+$/.test(alphanumeric_check)) {\n\t\terror(`Skill name must be kebab-case alphanumeric: ${name}`);\n\t\tprocess.exit(1);\n\t}\n\n\tif (!is_lowercase(name)) {\n\t\terror(`Skill name must be lowercase: ${name}`);\n\t\tprocess.exit(1);\n\t}\n\n\t// Create skill\n\tcreate_skill(\n\t\tskill_path,\n\t\tname,\n\t\tdescription,\n\t\toptions.with_examples || false,\n\t\toptions.global || false,\n\t);\n}\n\nfunction create_skill(\n\tpath: string,\n\tname: string,\n\tdescription: string,\n\twith_examples: boolean = false,\n\tglobal: boolean = false,\n): void {\n\t// Create base directories\n\tensure_dir(path);\n\tensure_dir(join(path, 'references'));\n\n\t// Create SKILL.md\n\tconst title = to_title_case(name);\n\tconst skill_md = SKILL_MD_TEMPLATE(\n\t\tname,\n\t\tdescription,\n\t\ttitle,\n\t\twith_examples,\n\t);\n\twrite_file(join(path, 'SKILL.md'), skill_md);\n\n\t// Only create example files if requested\n\tif (with_examples) {\n\t\t// Create example directories\n\t\tensure_dir(join(path, 'scripts'));\n\t\tensure_dir(join(path, 'assets'));\n\n\t\t// Create example reference\n\t\tconst reference_md = REFERENCE_TEMPLATE(title);\n\t\twrite_file(\n\t\t\tjoin(path, 'references', 'detailed-guide.md'),\n\t\t\treference_md,\n\t\t);\n\n\t\t// Create example script\n\t\tconst script_js = SCRIPT_TEMPLATE('example.js');\n\t\tconst script_path = join(path, 'scripts', 'example.js');\n\t\twrite_file(script_path, script_js);\n\t\tmake_executable(script_path);\n\t}\n\n\tconst scope = global ? 'global' : 'project';\n\tsuccess(`Skill created at: ${path} (${scope})`);\n\tconsole.log('\\nNext steps:');\n\tconsole.log(\n\t\t`1. Edit ${path}/SKILL.md with your skill instructions`,\n\t);\n\tconsole.log(`2. Add detailed documentation to references/`);\n\tif (with_examples) {\n\t\tconsole.log(`3. Add executable scripts to scripts/`);\n\t\tconsole.log(`4. Remove example files you don't need`);\n\t} else {\n\t\tconsole.log(\n\t\t\t`3. Use --with-examples flag if you need scripts/ and example files`,\n\t\t);\n\t}\n\tconsole.log(`\\n⚠️ REQUIRED: Validate the skill before use:`);\n\tconsole.log(` claude-skills-cli validate ${path}`);\n\tconsole.log(` Fix all errors immediately.`);\n}\n","import { defineCommand } from 'citty';\nimport { init_command } from './init.js';\n\nexport default defineCommand({\n\tmeta: { name: 'init', description: 'Create a new skill' },\n\targs: {\n\t\tname: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Skill name (kebab-case, lowercase)',\n\t\t},\n\t\tdescription: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Brief description with trigger keywords',\n\t\t},\n\t\tpath: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Custom path (alternative to --name)',\n\t\t},\n\t\t'with-examples': {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Include example files (scripts/, assets/)',\n\t\t},\n\t\tglobal: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription:\n\t\t\t\t'Install skill in ~/.claude/skills/ (available in all projects)',\n\t\t},\n\t},\n\trun({ args }) {\n\t\tinit_command({\n\t\t\tname: args.name,\n\t\t\tdescription: args.description,\n\t\t\tpath: args.path,\n\t\t\twith_examples: args['with-examples'],\n\t\t\tglobal: args.global,\n\t\t});\n\t},\n});\n"],"mappings":";;;;;;;AAiBA,SAAgB,aAAa,SAA4B;CACxD,IAAI;CACJ,IAAI;CACJ,IAAI;AAGJ,KAAI,QAAQ,MAAM;AACjB,eAAa,QAAQ;AACrB,SAAO,WAAW,MAAM,IAAI,CAAC,KAAK,IAAI;AACtC,gBACC,QAAQ,eACR;YACS,QAAQ,MAAM;AACxB,SAAO,QAAQ;AACf,gBACC,QAAQ,eACR;AAED,eAAa,QAAQ,SAClB,KAAK,SAAS,EAAE,WAAW,UAAU,KAAK,GAC1C,KAAK,WAAW,UAAU,KAAK;QAC5B;AACN,QAAM,2CAA2C;AACjD,UAAQ,IAAI,WAAW;AACvB,UAAQ,IACP,yEACA;AACD,UAAQ,IACP,wDACA;AACD,UAAQ,KAAK,EAAE;;CAIhB,MAAM,qBAAqB,KAAK,QAAQ,MAAM,GAAG,CAAC,QAAQ,MAAM,GAAG;AACnE,KAAI,CAAC,cAAc,KAAK,mBAAmB,EAAE;AAC5C,QAAM,+CAA+C,OAAO;AAC5D,UAAQ,KAAK,EAAE;;AAGhB,KAAI,CAAC,aAAa,KAAK,EAAE;AACxB,QAAM,iCAAiC,OAAO;AAC9C,UAAQ,KAAK,EAAE;;AAIhB,cACC,YACA,MACA,aACA,QAAQ,iBAAiB,OACzB,QAAQ,UAAU,MAClB;;AAGF,SAAS,aACR,MACA,MACA,aACA,gBAAyB,OACzB,SAAkB,OACX;AAEP,YAAW,KAAK;AAChB,YAAW,KAAK,MAAM,aAAa,CAAC;CAGpC,MAAM,QAAQ,cAAc,KAAK;CACjC,MAAM,WAAW,kBAChB,MACA,aACA,OACA,cACA;AACD,YAAW,KAAK,MAAM,WAAW,EAAE,SAAS;AAG5C,KAAI,eAAe;AAElB,aAAW,KAAK,MAAM,UAAU,CAAC;AACjC,aAAW,KAAK,MAAM,SAAS,CAAC;EAGhC,MAAM,eAAe,mBAAmB,MAAM;AAC9C,aACC,KAAK,MAAM,cAAc,oBAAoB,EAC7C,aACA;EAGD,MAAM,YAAY,gBAAgB,aAAa;EAC/C,MAAM,cAAc,KAAK,MAAM,WAAW,aAAa;AACvD,aAAW,aAAa,UAAU;AAClC,kBAAgB,YAAY;;AAI7B,SAAQ,qBAAqB,KAAK,IADpB,SAAS,WAAW,UACU,GAAG;AAC/C,SAAQ,IAAI,gBAAgB;AAC5B,SAAQ,IACP,WAAW,KAAK,wCAChB;AACD,SAAQ,IAAI,+CAA+C;AAC3D,KAAI,eAAe;AAClB,UAAQ,IAAI,wCAAwC;AACpD,UAAQ,IAAI,yCAAyC;OAErD,SAAQ,IACP,qEACA;AAEF,SAAQ,IAAI,iDAAiD;AAC7D,SAAQ,IAAI,iCAAiC,OAAO;AACpD,SAAQ,IAAI,iCAAiC;;;;AC/H9C,IAAA,mBAAe,cAAc;CAC5B,MAAM;EAAE,MAAM;EAAQ,aAAa;EAAsB;CACzD,MAAM;EACL,MAAM;GACL,MAAM;GACN,aAAa;GACb;EACD,aAAa;GACZ,MAAM;GACN,aAAa;GACb;EACD,MAAM;GACL,MAAM;GACN,aAAa;GACb;EACD,iBAAiB;GAChB,MAAM;GACN,aAAa;GACb;EACD,QAAQ;GACP,MAAM;GACN,aACC;GACD;EACD;CACD,IAAI,EAAE,QAAQ;AACb,eAAa;GACZ,MAAM,KAAK;GACX,aAAa,KAAK;GAClB,MAAM,KAAK;GACX,eAAe,KAAK;GACpB,QAAQ,KAAK;GACb,CAAC;;CAEH,CAAC"}
1
+ {"version":3,"file":"init.cmd-BdqImX8b.js","names":[],"sources":["../src/commands/init.ts","../src/commands/init.cmd.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport {\n\tREFERENCE_TEMPLATE,\n\tSCRIPT_TEMPLATE,\n\tSKILL_MD_TEMPLATE,\n} from '../core/templates.js';\nimport type { InitOptions } from '../types.js';\nimport {\n\tensure_dir,\n\tis_lowercase,\n\tmake_executable,\n\tto_title_case,\n\twrite_file,\n} from '../utils/fs.js';\nimport { error, success } from '../utils/output.js';\n\nexport function init_command(options: InitOptions): void {\n\tlet skill_path: string;\n\tlet name: string;\n\tlet description: string;\n\n\t// Determine path and name\n\tif (options.path) {\n\t\tskill_path = options.path;\n\t\tname = skill_path.split('/').pop() || '';\n\t\tdescription =\n\t\t\toptions.description ||\n\t\t\t'TODO: [Domain] [operations]. Use when [trigger phrase]';\n\t} else if (options.name) {\n\t\tname = options.name;\n\t\tdescription =\n\t\t\toptions.description ||\n\t\t\t'TODO: [Domain] [operations]. Use when [trigger phrase]';\n\t\t// Use ~/.claude/skills/ for global, .claude/skills/ for project\n\t\tskill_path = options.global\n\t\t\t? join(homedir(), '.claude', 'skills', name)\n\t\t\t: join('.claude', 'skills', name);\n\t} else {\n\t\terror('Either --name or --path must be provided');\n\t\tconsole.log('\\nUsage:');\n\t\tconsole.log(\n\t\t\t' claude-skills-cli init --name my-skill --description \"Description\"',\n\t\t);\n\t\tconsole.log(\n\t\t\t' claude-skills-cli init --path /custom/path/my-skill',\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\t// Validate name format\n\tconst alphanumeric_check = name.replace(/-/g, '').replace(/_/g, '');\n\tif (!/^[a-z0-9]+$/.test(alphanumeric_check)) {\n\t\terror(`Skill name must be kebab-case alphanumeric: ${name}`);\n\t\tprocess.exit(1);\n\t}\n\n\tif (!is_lowercase(name)) {\n\t\terror(`Skill name must be lowercase: ${name}`);\n\t\tprocess.exit(1);\n\t}\n\n\t// Create skill\n\tcreate_skill(\n\t\tskill_path,\n\t\tname,\n\t\tdescription,\n\t\toptions.with_examples || false,\n\t\toptions.global || false,\n\t);\n}\n\nfunction create_skill(\n\tpath: string,\n\tname: string,\n\tdescription: string,\n\twith_examples: boolean = false,\n\tglobal: boolean = false,\n): void {\n\t// Create base directories\n\tensure_dir(path);\n\tensure_dir(join(path, 'references'));\n\n\t// Create SKILL.md\n\tconst title = to_title_case(name);\n\tconst skill_md = SKILL_MD_TEMPLATE(\n\t\tname,\n\t\tdescription,\n\t\ttitle,\n\t\twith_examples,\n\t);\n\twrite_file(join(path, 'SKILL.md'), skill_md);\n\n\t// Only create example files if requested\n\tif (with_examples) {\n\t\t// Create example directories\n\t\tensure_dir(join(path, 'scripts'));\n\t\tensure_dir(join(path, 'assets'));\n\n\t\t// Create example reference\n\t\tconst reference_md = REFERENCE_TEMPLATE(title);\n\t\twrite_file(\n\t\t\tjoin(path, 'references', 'detailed-guide.md'),\n\t\t\treference_md,\n\t\t);\n\n\t\t// Create example script\n\t\tconst script_js = SCRIPT_TEMPLATE('example.js');\n\t\tconst script_path = join(path, 'scripts', 'example.js');\n\t\twrite_file(script_path, script_js);\n\t\tmake_executable(script_path);\n\t}\n\n\tconst scope = global ? 'global' : 'project';\n\tsuccess(`Skill created at: ${path} (${scope})`);\n\tconsole.log('\\nNext steps:');\n\tconsole.log(\n\t\t`1. Edit ${path}/SKILL.md with your skill instructions`,\n\t);\n\tconsole.log(`2. Add detailed documentation to references/`);\n\tif (with_examples) {\n\t\tconsole.log(`3. Add executable scripts to scripts/`);\n\t\tconsole.log(`4. Remove example files you don't need`);\n\t} else {\n\t\tconsole.log(\n\t\t\t`3. Use --with-examples flag if you need scripts/ and example files`,\n\t\t);\n\t}\n\tconsole.log(`\\n⚠️ REQUIRED: Validate the skill before use:`);\n\tconsole.log(` claude-skills-cli validate ${path}`);\n\tconsole.log(` Fix all errors immediately.`);\n}\n","import { defineCommand } from 'citty';\nimport { init_command } from './init.js';\n\nexport default defineCommand({\n\tmeta: { name: 'init', description: 'Create a new skill' },\n\targs: {\n\t\tname: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Skill name (kebab-case, lowercase)',\n\t\t},\n\t\tdescription: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Brief description with trigger keywords',\n\t\t},\n\t\tpath: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Custom path (alternative to --name)',\n\t\t},\n\t\t'with-examples': {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Include example files (scripts/, assets/)',\n\t\t},\n\t\tglobal: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription:\n\t\t\t\t'Install skill in ~/.claude/skills/ (available in all projects)',\n\t\t},\n\t},\n\trun({ args }) {\n\t\tinit_command({\n\t\t\tname: args.name,\n\t\t\tdescription: args.description,\n\t\t\tpath: args.path,\n\t\t\twith_examples: args['with-examples'],\n\t\t\tglobal: args.global,\n\t\t});\n\t},\n});\n"],"mappings":";;;;;;;AAiBA,SAAgB,aAAa,SAA4B;CACxD,IAAI;CACJ,IAAI;CACJ,IAAI;AAGJ,KAAI,QAAQ,MAAM;AACjB,eAAa,QAAQ;AACrB,SAAO,WAAW,MAAM,IAAI,CAAC,KAAK,IAAI;AACtC,gBACC,QAAQ,eACR;YACS,QAAQ,MAAM;AACxB,SAAO,QAAQ;AACf,gBACC,QAAQ,eACR;AAED,eAAa,QAAQ,SAClB,KAAK,SAAS,EAAE,WAAW,UAAU,KAAK,GAC1C,KAAK,WAAW,UAAU,KAAK;QAC5B;AACN,QAAM,2CAA2C;AACjD,UAAQ,IAAI,WAAW;AACvB,UAAQ,IACP,yEACA;AACD,UAAQ,IACP,wDACA;AACD,UAAQ,KAAK,EAAE;;CAIhB,MAAM,qBAAqB,KAAK,QAAQ,MAAM,GAAG,CAAC,QAAQ,MAAM,GAAG;AACnE,KAAI,CAAC,cAAc,KAAK,mBAAmB,EAAE;AAC5C,QAAM,+CAA+C,OAAO;AAC5D,UAAQ,KAAK,EAAE;;AAGhB,KAAI,CAAC,aAAa,KAAK,EAAE;AACxB,QAAM,iCAAiC,OAAO;AAC9C,UAAQ,KAAK,EAAE;;AAIhB,cACC,YACA,MACA,aACA,QAAQ,iBAAiB,OACzB,QAAQ,UAAU,MAClB;;AAGF,SAAS,aACR,MACA,MACA,aACA,gBAAyB,OACzB,SAAkB,OACX;AAEP,YAAW,KAAK;AAChB,YAAW,KAAK,MAAM,aAAa,CAAC;CAGpC,MAAM,QAAQ,cAAc,KAAK;CACjC,MAAM,WAAW,kBAChB,MACA,aACA,OACA,cACA;AACD,YAAW,KAAK,MAAM,WAAW,EAAE,SAAS;AAG5C,KAAI,eAAe;AAElB,aAAW,KAAK,MAAM,UAAU,CAAC;AACjC,aAAW,KAAK,MAAM,SAAS,CAAC;EAGhC,MAAM,eAAe,mBAAmB,MAAM;AAC9C,aACC,KAAK,MAAM,cAAc,oBAAoB,EAC7C,aACA;EAGD,MAAM,YAAY,gBAAgB,aAAa;EAC/C,MAAM,cAAc,KAAK,MAAM,WAAW,aAAa;AACvD,aAAW,aAAa,UAAU;AAClC,kBAAgB,YAAY;;AAI7B,SAAQ,qBAAqB,KAAK,IADpB,SAAS,WAAW,UACU,GAAG;AAC/C,SAAQ,IAAI,gBAAgB;AAC5B,SAAQ,IACP,WAAW,KAAK,wCAChB;AACD,SAAQ,IAAI,+CAA+C;AAC3D,KAAI,eAAe;AAClB,UAAQ,IAAI,wCAAwC;AACpD,UAAQ,IAAI,yCAAyC;OAErD,SAAQ,IACP,qEACA;AAEF,SAAQ,IAAI,iDAAiD;AAC7D,SAAQ,IAAI,iCAAiC,OAAO;AACpD,SAAQ,IAAI,iCAAiC;;;;AC/H9C,IAAA,mBAAe,cAAc;CAC5B,MAAM;EAAE,MAAM;EAAQ,aAAa;EAAsB;CACzD,MAAM;EACL,MAAM;GACL,MAAM;GACN,aAAa;GACb;EACD,aAAa;GACZ,MAAM;GACN,aAAa;GACb;EACD,MAAM;GACL,MAAM;GACN,aAAa;GACb;EACD,iBAAiB;GAChB,MAAM;GACN,aAAa;GACb;EACD,QAAQ;GACP,MAAM;GACN,aACC;GACD;EACD;CACD,IAAI,EAAE,QAAQ;AACb,eAAa;GACZ,MAAM,KAAK;GACX,aAAa,KAAK;GAClB,MAAM,KAAK;GACX,eAAe,KAAK;GACpB,QAAQ,KAAK;GACb,CAAC;;CAEH,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { t as ensure_dir } from "./fs-CuGv3Ob2.js";
2
- import { n as error, o as success, r as info } from "./output-Dz8fk6Gu.js";
2
+ import { n as error, o as success, r as info } from "./output-DiffPD2u.js";
3
3
  import { defineCommand } from "citty";
4
4
  import { cpSync, existsSync } from "node:fs";
5
5
  import { dirname, join } from "node:path";
@@ -76,4 +76,4 @@ var install_cmd_default = defineCommand({
76
76
  //#endregion
77
77
  export { install_cmd_default as default };
78
78
 
79
- //# sourceMappingURL=install.cmd-CH7yZ92g.js.map
79
+ //# sourceMappingURL=install.cmd-BaP8k9d2.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"install.cmd-CH7yZ92g.js","names":[],"sources":["../src/commands/install.ts","../src/commands/install.cmd.ts"],"sourcesContent":["import { cpSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { InstallOptions } from '../types.js';\nimport { ensure_dir } from '../utils/fs.js';\nimport { error, info, success } from '../utils/output.js';\n\n// Get the directory where this module is located (dist/commands/)\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// Bundled skills are in dist/skills/ directory\nconst BUNDLED_SKILLS_DIR = join(__dirname, '..', 'skills');\n\nconst AVAILABLE_SKILLS: string[] = [];\n\nexport function install_command(options: InstallOptions): void {\n\tconst skill_name = options.skill_name;\n\n\t// Validate skill name\n\tif (!skill_name || AVAILABLE_SKILLS.length === 0) {\n\t\tinfo('No bundled skills available');\n\t\tconsole.log(\n\t\t\t'\\nOfficial method: /plugin install <skill>@<marketplace>',\n\t\t);\n\t\tconsole.log('See: https://code.claude.com/docs/en/plugins');\n\t\tconsole.log(\n\t\t\t'\\nFor pre-built skills & activation hooks, see: https://github.com/spences10/claude-code-toolkit',\n\t\t);\n\t\tconsole.log(\n\t\t\t'\\nUse \"claude-skills-cli init\" to create new skills.',\n\t\t);\n\t\tprocess.exit(0);\n\t}\n\n\tif (!AVAILABLE_SKILLS.includes(skill_name)) {\n\t\terror(`Unknown skill: ${skill_name}`);\n\t\tconsole.log('\\nNo bundled skills available.');\n\t\tconsole.log(\n\t\t\t'For pre-built skills & activation hooks, see: https://github.com/spences10/claude-code-toolkit',\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\t// Source and destination paths\n\tconst source_path = join(BUNDLED_SKILLS_DIR, skill_name);\n\tconst dest_path = join('.claude', 'skills', skill_name);\n\n\t// Check if source exists in bundle\n\tif (!existsSync(source_path)) {\n\t\terror(`Bundled skill not found: ${skill_name}`);\n\t\tconsole.log(\n\t\t\t`Expected at: ${source_path}\\n\\nThis may be a package installation issue.`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\t// Check if already installed\n\tif (existsSync(dest_path)) {\n\t\tif (options.force) {\n\t\t\tinfo(\n\t\t\t\t`Skill already exists at ${dest_path}, overwriting (--force)...`,\n\t\t\t);\n\t\t} else {\n\t\t\tinfo(`Skill already installed at: ${dest_path}`);\n\t\t\tconsole.log('\\nUse --force to overwrite the existing skill.');\n\t\t\tprocess.exit(0);\n\t\t}\n\t}\n\n\t// Create destination directory\n\tensure_dir(join('.claude', 'skills'));\n\n\t// Copy skill directory\n\ttry {\n\t\tcpSync(source_path, dest_path, {\n\t\t\trecursive: true,\n\t\t\tforce: options.force || false,\n\t\t});\n\t\tsuccess(`Installed ${skill_name} to: ${dest_path}`);\n\t} catch (err) {\n\t\terror(`Failed to install skill: ${(err as Error).message}`);\n\t\tprocess.exit(1);\n\t}\n}\n","import { defineCommand } from 'citty';\nimport { install_command } from './install.js';\n\nexport default defineCommand({\n\tmeta: { name: 'install', description: 'Install a bundled skill' },\n\targs: {\n\t\tskill_name: {\n\t\t\ttype: 'positional',\n\t\t\tdescription: 'Name of bundled skill',\n\t\t\trequired: true,\n\t\t},\n\t\tforce: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Replace existing skill without prompting',\n\t\t},\n\t},\n\trun({ args }) {\n\t\tinstall_command({\n\t\t\tskill_name: args.skill_name,\n\t\t\tforce: args.force,\n\t\t});\n\t},\n});\n"],"mappings":";;;;;;;AAYA,MAAM,qBAAqB,KAHT,QADC,cAAc,OAAO,KAAK,IAAI,CACZ,EAGM,MAAM,SAAS;AAE1D,MAAM,mBAA6B,EAAE;AAErC,SAAgB,gBAAgB,SAA+B;CAC9D,MAAM,aAAa,QAAQ;AAG3B,KAAI,CAAC,cAAc,iBAAiB,WAAW,GAAG;AACjD,OAAK,8BAA8B;AACnC,UAAQ,IACP,2DACA;AACD,UAAQ,IAAI,+CAA+C;AAC3D,UAAQ,IACP,mGACA;AACD,UAAQ,IACP,yDACA;AACD,UAAQ,KAAK,EAAE;;AAGhB,KAAI,CAAC,iBAAiB,SAAS,WAAW,EAAE;AAC3C,QAAM,kBAAkB,aAAa;AACrC,UAAQ,IAAI,iCAAiC;AAC7C,UAAQ,IACP,iGACA;AACD,UAAQ,KAAK,EAAE;;CAIhB,MAAM,cAAc,KAAK,oBAAoB,WAAW;CACxD,MAAM,YAAY,KAAK,WAAW,UAAU,WAAW;AAGvD,KAAI,CAAC,WAAW,YAAY,EAAE;AAC7B,QAAM,4BAA4B,aAAa;AAC/C,UAAQ,IACP,gBAAgB,YAAY,+CAC5B;AACD,UAAQ,KAAK,EAAE;;AAIhB,KAAI,WAAW,UAAU,CACxB,KAAI,QAAQ,MACX,MACC,2BAA2B,UAAU,4BACrC;MACK;AACN,OAAK,+BAA+B,YAAY;AAChD,UAAQ,IAAI,iDAAiD;AAC7D,UAAQ,KAAK,EAAE;;AAKjB,YAAW,KAAK,WAAW,SAAS,CAAC;AAGrC,KAAI;AACH,SAAO,aAAa,WAAW;GAC9B,WAAW;GACX,OAAO,QAAQ,SAAS;GACxB,CAAC;AACF,UAAQ,aAAa,WAAW,OAAO,YAAY;UAC3C,KAAK;AACb,QAAM,4BAA6B,IAAc,UAAU;AAC3D,UAAQ,KAAK,EAAE;;;;;AC/EjB,IAAA,sBAAe,cAAc;CAC5B,MAAM;EAAE,MAAM;EAAW,aAAa;EAA2B;CACjE,MAAM;EACL,YAAY;GACX,MAAM;GACN,aAAa;GACb,UAAU;GACV;EACD,OAAO;GACN,MAAM;GACN,aAAa;GACb;EACD;CACD,IAAI,EAAE,QAAQ;AACb,kBAAgB;GACf,YAAY,KAAK;GACjB,OAAO,KAAK;GACZ,CAAC;;CAEH,CAAC"}
1
+ {"version":3,"file":"install.cmd-BaP8k9d2.js","names":[],"sources":["../src/commands/install.ts","../src/commands/install.cmd.ts"],"sourcesContent":["import { cpSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { InstallOptions } from '../types.js';\nimport { ensure_dir } from '../utils/fs.js';\nimport { error, info, success } from '../utils/output.js';\n\n// Get the directory where this module is located (dist/commands/)\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// Bundled skills are in dist/skills/ directory\nconst BUNDLED_SKILLS_DIR = join(__dirname, '..', 'skills');\n\nconst AVAILABLE_SKILLS: string[] = [];\n\nexport function install_command(options: InstallOptions): void {\n\tconst skill_name = options.skill_name;\n\n\t// Validate skill name\n\tif (!skill_name || AVAILABLE_SKILLS.length === 0) {\n\t\tinfo('No bundled skills available');\n\t\tconsole.log(\n\t\t\t'\\nOfficial method: /plugin install <skill>@<marketplace>',\n\t\t);\n\t\tconsole.log('See: https://code.claude.com/docs/en/plugins');\n\t\tconsole.log(\n\t\t\t'\\nFor pre-built skills & activation hooks, see: https://github.com/spences10/claude-code-toolkit',\n\t\t);\n\t\tconsole.log(\n\t\t\t'\\nUse \"claude-skills-cli init\" to create new skills.',\n\t\t);\n\t\tprocess.exit(0);\n\t}\n\n\tif (!AVAILABLE_SKILLS.includes(skill_name)) {\n\t\terror(`Unknown skill: ${skill_name}`);\n\t\tconsole.log('\\nNo bundled skills available.');\n\t\tconsole.log(\n\t\t\t'For pre-built skills & activation hooks, see: https://github.com/spences10/claude-code-toolkit',\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\t// Source and destination paths\n\tconst source_path = join(BUNDLED_SKILLS_DIR, skill_name);\n\tconst dest_path = join('.claude', 'skills', skill_name);\n\n\t// Check if source exists in bundle\n\tif (!existsSync(source_path)) {\n\t\terror(`Bundled skill not found: ${skill_name}`);\n\t\tconsole.log(\n\t\t\t`Expected at: ${source_path}\\n\\nThis may be a package installation issue.`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\t// Check if already installed\n\tif (existsSync(dest_path)) {\n\t\tif (options.force) {\n\t\t\tinfo(\n\t\t\t\t`Skill already exists at ${dest_path}, overwriting (--force)...`,\n\t\t\t);\n\t\t} else {\n\t\t\tinfo(`Skill already installed at: ${dest_path}`);\n\t\t\tconsole.log('\\nUse --force to overwrite the existing skill.');\n\t\t\tprocess.exit(0);\n\t\t}\n\t}\n\n\t// Create destination directory\n\tensure_dir(join('.claude', 'skills'));\n\n\t// Copy skill directory\n\ttry {\n\t\tcpSync(source_path, dest_path, {\n\t\t\trecursive: true,\n\t\t\tforce: options.force || false,\n\t\t});\n\t\tsuccess(`Installed ${skill_name} to: ${dest_path}`);\n\t} catch (err) {\n\t\terror(`Failed to install skill: ${(err as Error).message}`);\n\t\tprocess.exit(1);\n\t}\n}\n","import { defineCommand } from 'citty';\nimport { install_command } from './install.js';\n\nexport default defineCommand({\n\tmeta: { name: 'install', description: 'Install a bundled skill' },\n\targs: {\n\t\tskill_name: {\n\t\t\ttype: 'positional',\n\t\t\tdescription: 'Name of bundled skill',\n\t\t\trequired: true,\n\t\t},\n\t\tforce: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Replace existing skill without prompting',\n\t\t},\n\t},\n\trun({ args }) {\n\t\tinstall_command({\n\t\t\tskill_name: args.skill_name,\n\t\t\tforce: args.force,\n\t\t});\n\t},\n});\n"],"mappings":";;;;;;;AAYA,MAAM,qBAAqB,KAHT,QADC,cAAc,OAAO,KAAK,IAAI,CACZ,EAGM,MAAM,SAAS;AAE1D,MAAM,mBAA6B,EAAE;AAErC,SAAgB,gBAAgB,SAA+B;CAC9D,MAAM,aAAa,QAAQ;AAG3B,KAAI,CAAC,cAAc,iBAAiB,WAAW,GAAG;AACjD,OAAK,8BAA8B;AACnC,UAAQ,IACP,2DACA;AACD,UAAQ,IAAI,+CAA+C;AAC3D,UAAQ,IACP,mGACA;AACD,UAAQ,IACP,yDACA;AACD,UAAQ,KAAK,EAAE;;AAGhB,KAAI,CAAC,iBAAiB,SAAS,WAAW,EAAE;AAC3C,QAAM,kBAAkB,aAAa;AACrC,UAAQ,IAAI,iCAAiC;AAC7C,UAAQ,IACP,iGACA;AACD,UAAQ,KAAK,EAAE;;CAIhB,MAAM,cAAc,KAAK,oBAAoB,WAAW;CACxD,MAAM,YAAY,KAAK,WAAW,UAAU,WAAW;AAGvD,KAAI,CAAC,WAAW,YAAY,EAAE;AAC7B,QAAM,4BAA4B,aAAa;AAC/C,UAAQ,IACP,gBAAgB,YAAY,+CAC5B;AACD,UAAQ,KAAK,EAAE;;AAIhB,KAAI,WAAW,UAAU,CACxB,KAAI,QAAQ,MACX,MACC,2BAA2B,UAAU,4BACrC;MACK;AACN,OAAK,+BAA+B,YAAY;AAChD,UAAQ,IAAI,iDAAiD;AAC7D,UAAQ,KAAK,EAAE;;AAKjB,YAAW,KAAK,WAAW,SAAS,CAAC;AAGrC,KAAI;AACH,SAAO,aAAa,WAAW;GAC9B,WAAW;GACX,OAAO,QAAQ,SAAS;GACxB,CAAC;AACF,UAAQ,aAAa,WAAW,OAAO,YAAY;UAC3C,KAAK;AACb,QAAM,4BAA6B,IAAc,UAAU;AAC3D,UAAQ,KAAK,EAAE;;;;;AC/EjB,IAAA,sBAAe,cAAc;CAC5B,MAAM;EAAE,MAAM;EAAW,aAAa;EAA2B;CACjE,MAAM;EACL,YAAY;GACX,MAAM;GACN,aAAa;GACb,UAAU;GACV;EACD,OAAO;GACN,MAAM;GACN,aAAa;GACb;EACD;CACD,IAAI,EAAE,QAAQ;AACb,kBAAgB;GACf,YAAY,KAAK;GACjB,OAAO,KAAK;GACZ,CAAC;;CAEH,CAAC"}
@@ -46,6 +46,8 @@ const LIMITS = {
46
46
  }
47
47
  }
48
48
  };
49
+ /** Semver format regex for version field validation */
50
+ const SEMVER_REGEX = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$/;
49
51
  //#endregion
50
52
  //#region src/utils/output.ts
51
53
  const success = (msg) => console.log(`✅ ${msg}`);
@@ -97,6 +99,6 @@ function display_validation_stats(stats) {
97
99
  else console.log(" ❌ Violates progressive disclosure (move content to references/)");
98
100
  }
99
101
  //#endregion
100
- export { search as a, warning as c, package_ as i, LIMITS as l, error as n, success as o, info as r, upload as s, display_validation_stats as t };
102
+ export { search as a, warning as c, package_ as i, LIMITS as l, error as n, success as o, info as r, upload as s, display_validation_stats as t, SEMVER_REGEX as u };
101
103
 
102
- //# sourceMappingURL=output-Dz8fk6Gu.js.map
104
+ //# sourceMappingURL=output-DiffPD2u.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-DiffPD2u.js","names":[],"sources":["../src/constants.ts","../src/utils/output.ts"],"sourcesContent":["/**\n * Anthropic skill spec limits\n * https://code.claude.com/docs/en/skills#frontmatter-reference\n */\n\n/** Max chars for skill name field */\nexport const NAME_MAX_LENGTH = 64;\n\n/** Max chars for description — Claude truncates at this limit in skill listing */\nexport const DESCRIPTION_MAX_LENGTH = 250;\n\n/** Min recommended chars for description to be useful */\nexport const DESCRIPTION_MIN_LENGTH = 50;\n\n/** Token budget for Level 2 content (~5000 words * 1.3) */\nexport const TOKEN_BUDGET = 6500;\n\n/** Words per paragraph before it's considered \"long\" */\nexport const LONG_PARAGRAPH_WORDS = 100;\n\n/** Min body length (chars) before warning about short content */\nexport const MIN_BODY_LENGTH = 100;\n\n/**\n * Progressive disclosure limits — three tiers for SKILL.md body\n *\n * strict: opinionated defaults for minimal context usage\n * lenient: relaxed for larger skills\n * loose: matches Anthropic official limits (500 lines)\n */\nexport const LIMITS = {\n\tstrict: {\n\t\tlines: { excellent: 30, good: 40, max: 50 },\n\t\twords: { excellent: 300, good: 500, max: 1000 },\n\t},\n\tlenient: {\n\t\tlines: { excellent: 50, good: 100, max: 150 },\n\t\twords: { excellent: 500, good: 1000, max: 2000 },\n\t},\n\tloose: {\n\t\tlines: { excellent: 100, good: 200, max: 500 },\n\t\twords: { excellent: 1000, good: 2000, max: 5000 },\n\t},\n} as const;\n\n/** Semver format regex for version field validation */\nexport const SEMVER_REGEX =\n\t/^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.]+)?(\\+[a-zA-Z0-9.]+)?$/;\n","import {\n\tDESCRIPTION_MAX_LENGTH,\n\tLIMITS,\n\tTOKEN_BUDGET,\n} from '../constants.js';\nimport type { ValidationStats } from '../types.js';\n\nexport const success = (msg: string) => console.log(`✅ ${msg}`);\nexport const error = (msg: string) => console.log(`❌ ${msg}`);\nexport const warning = (msg: string) => console.log(`⚠️ ${msg}`);\nexport const info = (msg: string) => console.log(`📋 ${msg}`);\nexport const step = (msg: string) => console.log(` ${msg}`);\nexport const package_ = (msg: string) => console.log(`📦 ${msg}`);\nexport const upload = (msg: string) => console.log(`📤 ${msg}`);\nexport const search = (msg: string) => console.log(`🔍 ${msg}`);\n\nconst S = LIMITS.strict;\n\n/**\n * Display progressive disclosure statistics\n */\nexport function display_validation_stats(\n\tstats: ValidationStats,\n): void {\n\tconsole.log('\\n📊 Progressive Disclosure Stats:');\n\n\t// Level 1: Description\n\tconsole.log('\\n Level 1 (Metadata - Always Loaded):');\n\tconst desc_status =\n\t\tstats.description_length <= DESCRIPTION_MAX_LENGTH\n\t\t\t? '✅ Optimal'\n\t\t\t: '❌ Too long';\n\n\tconsole.log(\n\t\t` Description: ${stats.description_length} chars, ~${stats.description_tokens} tokens ${desc_status}`,\n\t);\n\tconsole.log(\n\t\t` (Target: <${DESCRIPTION_MAX_LENGTH} chars for Level 1 efficiency)`,\n\t);\n\n\t// Level 2: SKILL.md Body\n\tconsole.log('\\n Level 2 (SKILL.md Body - Loaded when triggered):');\n\n\t// Line count\n\tlet line_status: string;\n\tif (stats.line_count <= S.lines.excellent) {\n\t\tline_status = '✅ Excellent';\n\t} else if (stats.line_count <= S.lines.good) {\n\t\tline_status = '✅ Good';\n\t} else if (stats.line_count <= S.lines.max) {\n\t\tline_status = '⚠️ Consider splitting';\n\t} else {\n\t\tline_status = '❌ Too large';\n\t}\n\n\tconsole.log(\n\t\t` Lines: ${stats.line_count} (max: ${S.lines.max}) ${line_status}`,\n\t);\n\n\t// Word count\n\tlet word_status: string;\n\tif (stats.word_count < S.words.excellent) {\n\t\tword_status = '✅ Excellent';\n\t} else if (stats.word_count < S.words.good) {\n\t\tword_status = '✅ Good';\n\t} else if (stats.word_count < S.words.max) {\n\t\tword_status = '⚠️ Consider splitting';\n\t} else {\n\t\tword_status = '❌ Too large';\n\t}\n\n\tconsole.log(\n\t\t` Words: ${stats.word_count} (max: ${S.words.max}) ${word_status}`,\n\t);\n\n\t// Token estimation\n\tconst token_status =\n\t\tstats.estimated_tokens < TOKEN_BUDGET\n\t\t\t? 'within budget'\n\t\t\t: 'exceeds budget';\n\n\tconsole.log(\n\t\t` Est. tokens: ~${stats.estimated_tokens} (budget: <${TOKEN_BUDGET}) ${token_status}`,\n\t);\n\n\t// Code blocks\n\tconst code_status =\n\t\tstats.code_blocks > 3\n\t\t\t? ' (recommended: 1-2)'\n\t\t\t: stats.code_blocks <= 2\n\t\t\t\t? ' ✅'\n\t\t\t\t: '';\n\tconsole.log(` Code blocks: ${stats.code_blocks}${code_status}`);\n\n\t// Sections\n\tconst section_status =\n\t\tstats.sections > 8\n\t\t\t? ' (recommended: 3-5)'\n\t\t\t: stats.sections >= 3 && stats.sections <= 5\n\t\t\t\t? ' ✅'\n\t\t\t\t: '';\n\tconsole.log(` Sections: ${stats.sections}${section_status}`);\n\n\t// Long paragraphs\n\tif (stats.long_paragraphs > 0) {\n\t\tconst para_status =\n\t\t\tstats.long_paragraphs > 3\n\t\t\t\t? ' (consider moving to references/)'\n\t\t\t\t: '';\n\t\tconsole.log(\n\t\t\t` Long paragraphs: ${stats.long_paragraphs}${para_status}`,\n\t\t);\n\t}\n\n\t// Level 3 info\n\tconsole.log('\\n Level 3+ (References - Loaded as needed):');\n\tconsole.log(\n\t\t' Use references/ directory for detailed docs (unlimited size)',\n\t);\n\n\t// Overall assessment (based on strict defaults)\n\tconsole.log('\\n Overall Assessment:');\n\tif (\n\t\tstats.line_count <= S.lines.excellent &&\n\t\tstats.description_length <= DESCRIPTION_MAX_LENGTH\n\t) {\n\t\tconsole.log(' ✅ Excellent progressive disclosure!');\n\t} else if (\n\t\tstats.line_count <= S.lines.max &&\n\t\tstats.description_length <= DESCRIPTION_MAX_LENGTH\n\t) {\n\t\tconsole.log(' ✅ Good progressive disclosure');\n\t} else if (\n\t\tstats.line_count <= LIMITS.lenient.lines.max &&\n\t\tstats.word_count < LIMITS.lenient.words.max\n\t) {\n\t\tconsole.log(\n\t\t\t' ⚠️ Consider splitting content into references/',\n\t\t);\n\t} else {\n\t\tconsole.log(\n\t\t\t' ❌ Violates progressive disclosure (move content to references/)',\n\t\t);\n\t}\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { t as ensure_dir } from "./fs-CuGv3Ob2.js";
2
- import { a as search, c as warning, i as package_, n as error, o as success, s as upload } from "./output-Dz8fk6Gu.js";
3
- import { t as SkillValidator } from "./validator-DV5zeeel.js";
2
+ import { a as search, c as warning, i as package_, n as error, o as success, s as upload } from "./output-DiffPD2u.js";
3
+ import { t as SkillValidator } from "./validator-Dp5x-OjP.js";
4
4
  import { defineCommand } from "citty";
5
5
  import { existsSync, statSync } from "node:fs";
6
6
  import { basename, join, resolve } from "node:path";
@@ -104,4 +104,4 @@ var package_cmd_default = defineCommand({
104
104
  //#endregion
105
105
  export { package_cmd_default as default };
106
106
 
107
- //# sourceMappingURL=package.cmd-CwGRHdEq.js.map
107
+ //# sourceMappingURL=package.cmd-BYhkheya.js.map