ccski is a CLI + MCP server to discover, install, enable/disable, and serve Claude/Codex-compatible skills. It also exports a small “kernel” API so you can embed discovery/validation in your own scripts. This README covers install and usage. For architecture and UX philosophy, see SPEC.md.
Documentation site: https://jixoai-labs.github.io/ccski/
Requires Node.js >= 20.
# run directly
npx ccski --help
# or install locally
pnpm install ccski
ccski --helpnpx ccski mcp- Add extra skill roots:
npx ccski mcp --skill-dir /extra/skills - Disable auto refresh:
npx ccski mcp --no-refresh
MCP plugin config example (Codex/Cursor/Windsurf/VS Code):
{
"mcpServers": {
"ccski": {
"command": "npx",
"args": ["ccski", "mcp"]
}
}
}| Command | Purpose |
|---|---|
ccski list |
List discovered skills (project, user, plugin) |
ccski info <name> |
Show metadata and content preview |
| `ccski install |
--all |
| `ccski enable [names...] [-i | --all]` |
| `ccski disable [names...] [-i | --all]` |
ccski validate <path> |
Validate SKILL.md or skill directory |
ccski mcp |
Start MCP server (stdio/http/sse) |
- Git repo (auto-detect marketplace):
ccski install https://github.com/wshobson/agents - Specific branch or path:
ccski install https://github.com/wshobson/agents/tree/main --branch mainccski install https://github.com/wshobson/agents --path skills/foo/SKILL.md - Local directory or single file:
ccski install /path/to/skills --mode fileccski install ./plugins/foo/SKILL.md - Overwrite existing skill: add
--force(or--override).
# Enable via interactive picker
ccski enable -i
# Disable all enabled skills
ccski disable --all- Programmatic API is available from the package export; see API Reference (or the docs site) for usage examples.
- Claude users: prefer
ccski mcp --exclude=claudeto avoid echoing built-in Claude skills. - Codex users: prefer
ccski mcp --exclude=codexwhen avoid echoing built-in Codex skills. - All commands support
--jsonfor scripting. - Use
--no-colorto disable colors or--colorto force them. - Read
SPEC.mdfor deep technical details and design philosophy.
- openskills — established the SKILL.md authoring pattern; ccski aligns with that spec.
- universal-skills — MCP-first skill set; ccski focuses on management, not bundling content.
Public exports from import ... from "ccski".
Notes:
- Package is ESM (
"type": "module"). Useimportin Node.js >= 20. discoverSkills()/SkillRegistry.getAll()return metadata. UseloadSkill()/SkillRegistry.load()to read full SKILL.md content.
import { discoverSkills, SkillRegistry, validateSkillFile } from "ccski";
import type { Skill, SkillMetadata } from "ccski";SkillProvider:"claude" | "codex" | "file"SkillLocation:"user" | "project" | "plugin"SkillFrontmatter: required SKILL.md frontmatter shape (name,description, plus extra fields)SkillMetadata: discovered skill summary (name/description/provider/location/path + bundled resources flags)Skill:SkillMetadata+content(full markdown, including frontmatter) +fullNameParseResult:{ frontmatter, content, fullContent }DiscoveryOptions: configure default roots, custom directories, provider tagging, and disabled-skill handlingSkillRegistryOptions:DiscoveryOptions+ plugin discovery options (pluginsFile,pluginsRoot,settingsFile,userDir)
Reference shapes (simplified):
export interface SkillMetadata {
name: string;
description: string;
disabled?: boolean;
provider: "claude" | "codex" | "file";
location: "user" | "project" | "plugin";
path: string;
hasReferences: boolean;
hasScripts: boolean;
hasAssets: boolean;
pluginInfo?: { pluginName: string; marketplace: string; version: string };
}
export interface Skill extends SkillMetadata {
content: string; // full markdown (including frontmatter)
fullName: string;
}All errors extend CcskiError and include a suggestions: string[] field for UX-friendly guidance.
SkillNotFoundError: thrown when a skill name cannot be resolvedAmbiguousSkillNameError: thrown when multiple skills match; includesmatches: string[]ParseError: SKILL.md read/UTF-8/frontmatter parsing failures; includesfilePath,reasonValidationError: frontmatter schema validation failures; includesfilePath,issues: string[]
Parse a SKILL.md (or .SKILL.md) file and return:
frontmatter: validated & normalized (descriptionwhitespace is normalized)content: markdown body without frontmatterfullContent: original file content including frontmatter
Throws ParseError (IO/encoding/YAML) and ValidationError (schema).
Safe validator wrapper around parseSkillFile():
success: truewhen file is valid- otherwise returns
errorsandsuggestionswithout throwing
import { validateSkillFile } from "ccski";
const result = validateSkillFile("/abs/path/to/SKILL.md");
if (!result.success) {
console.error(result.errors);
console.error(result.suggestions);
}Return the default search roots (project + user) with provider tagging (Claude/Codex).
Scan built-in directories (unless scanDefaultDirs: false) plus customDirs.
skills:SkillMetadata[]diagnostics: scanned paths, warnings, conflicts, and counts by provider
import { discoverSkills } from "ccski";
const { skills, diagnostics } = discoverSkills({
includeDisabled: true,
customDirs: ["/extra/skills"],
customProvider: "file",
});Load a discovered skill’s full content (reads SKILL.md or .SKILL.md based on metadata.disabled).
Lower-level scanner used by discoverSkills(). Useful when you want full control over:
- root path + recursion
- provider tagging (
"claude" | "codex" | "file") - optional
scopeprefixing for names
Convenience wrapper around discovery + fuzzy resolution.
import { SkillRegistry } from "ccski";
const registry = new SkillRegistry({ includeDisabled: true });
const all = registry.getAll(); // SkillMetadata[]
const full = registry.load("some-skill"); // Skill (with content)Methods:
refresh(): rescan directories (and plugins unlessskipPlugins: true)getAll(): list all discovered skillsfind(name): resolve a name (case-insensitive, supports short name andprovider:name)has(name): boolean existence checkload(name): resolve + read full contentgetDiagnostics(): totals + scanned roots + warnings/conflicts
These are exported for validating/parsing external JSON and frontmatter in a type-safe way:
SkillFrontmatterSchema/SkillFrontmatterTypePluginEntrySchema/PluginEntryTypeInstalledPluginsSchema/InstalledPluginsTypeClaudeSettingsSchema/ClaudeSettingsType