Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Project Overview

Skern is a minimal, agent-first CLI tool for managing Agent Skills across agentic development platforms (Claude Code, Codex CLI, OpenCode). It follows the Agent Skills open standard (agentskills.io) and uses `SKILL.md` files with YAML frontmatter as the canonical format.
Skern is a minimal, agent-first CLI tool for managing Agent Skills across agentic development platforms (Claude Code, Codex CLI, OpenCode, Cursor, Gemini CLI, GitHub Copilot, Windsurf, Continue, and more). It follows the Agent Skills open standard (agentskills.io) and uses `SKILL.md` files with YAML frontmatter as the canonical format.

The project is written in **Go 1.25+** and the current release is **v0.2.0**.

Expand All @@ -15,7 +15,7 @@ internal/
skill/ # Domain logic: Skill struct, manifest parsing, validation, scaffolding
overlap/ # Fuzzy name matching and description similarity scoring
registry/ # Filesystem CRUD over ~/.skern/skills/ and .skern/skills/
platform/ # Platform adapters (Claude Code, Codex CLI, OpenCode)
platform/ # Platform adapters — declarative spec table + generic Adapter
output/ # JSON/text structured output formatting
scripts/
install.sh # Installer script
Expand Down Expand Up @@ -95,7 +95,7 @@ Each milestone gets its own feature branch. All commits for that milestone go on
- **`cli/`** — Only command wiring, flag parsing, and output. No business logic.
- **`skill/`** — Domain types and operations. The `Skill` struct, `Author`, `ModifiedByEntry` types, manifest parsing/serialization, validation rules, and scaffolding templates.
- **`registry/`** — Filesystem operations for skill storage. CRUD and discovery across user/project scopes.
- **`platform/`** — Each adapter implements the `Platform` interface: `Name()`, `Detect()`, `UserSkillsDir()`, `ProjectSkillsDir()`, `Install()`, `Uninstall()`, `InstalledSkills()`.
- **`platform/`** — Declarative `Spec` table (`spec.go`) plus a single generic `Adapter` (`adapter.go`) that implements the `Platform` interface (`Name()`, `Detect()`, `UserSkillsDir()`, `ProjectSkillsDir()`, `Install()`, `Uninstall()`, `InstalledSkills()`) from any spec row. Adding a platform = one row in `Specs` plus one `Type` constant; no per-platform Go file.
- **`overlap/`** — Similarity scoring (Levenshtein distance, keyword overlap). Returns a float64 score in [0, 1].
- **`output/`** — Handles `--json` and `--quiet` flags. All commands go through this package for consistent formatting.

Expand Down Expand Up @@ -134,7 +134,7 @@ Each milestone gets its own feature branch. All commits for that milestone go on

3. **Platform adapters are copiers** — Installing a skill to a platform means copying the skill directory to the platform's expected location. Each adapter knows its platform's directory convention.

4. **Platform auto-detection** — Skern detects which platforms are installed by checking for their config directories/binaries (`~/.claude/`, `~/.codex/` or `~/.agents/`, `~/.config/opencode/`). Each `install`/`uninstall` invocation targets exactly one platform (per #52 D6); `--platform all` is not accepted. Agents specify the platform they are running on, and `skill install`/`skill uninstall` accept multiple skill names per call for batch operations.
4. **Platform auto-detection** — Skern detects which platforms are installed by checking each adapter's home-relative `DetectHome` paths (e.g. `~/.claude/`, `~/.cursor/`, `~/.gemini/`). Detection is per-platform even when several adapters share `.agents/skills/` as their project directory. Each `install`/`uninstall` invocation targets exactly one platform (per #52 D6); `--platform all` is not accepted. Agents specify the platform they are running on, and `skill install`/`skill uninstall` accept multiple skill names per call for batch operations.

5. **JSON output as first-class** — Every command supports `--json` for machine-readable output. Default is human-friendly text. Exit codes are semantic: 0=success, 1=error, 2=validation failure.

Expand Down Expand Up @@ -237,11 +237,18 @@ Names must match `^[a-z0-9]+(-[a-z0-9]+)*$` and be 1-64 characters.

### Platform Paths

| Platform | User-level | Project-level |
|-------------|-------------------------------------|--------------------------|
| Claude Code | `~/.claude/skills/<name>/` | `.claude/skills/<name>/` |
| Codex CLI | `~/.agents/skills/<name>/` | `.agents/skills/<name>/` |
| OpenCode | `~/.config/opencode/skills/<name>/`| `.opencode/skills/<name>/`|
| Adapter name | User-level | Project-level |
|------------------|--------------------------------------|----------------------------|
| `claude-code` | `~/.claude/skills/<name>/` | `.claude/skills/<name>/` |
| `codex-cli` | `~/.agents/skills/<name>/` | `.agents/skills/<name>/` |
| `opencode` | `~/.config/opencode/skills/<name>/` | `.opencode/skills/<name>/` |
| `cursor` | `~/.cursor/skills/<name>/` | `.agents/skills/<name>/` |
| `gemini-cli` | `~/.gemini/skills/<name>/` | `.agents/skills/<name>/` |
| `github-copilot` | `~/.copilot/skills/<name>/` | `.agents/skills/<name>/` |
| `windsurf` | `~/.codeium/windsurf/skills/<name>/`| `.windsurf/skills/<name>/` |
| `continue` | `~/.continue/skills/<name>/` | `.continue/skills/<name>/` |

The full list is generated from `internal/platform/spec.go` — append a row there to add a platform.

### Overlap Detection Thresholds

Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **Five new platform adapters: `cursor`, `gemini-cli`, `github-copilot`,
`windsurf`, `continue`.** All five accept the same `--platform` flag, route
installs to the platform's expected skill directory, and participate in
`skern platform list`/`status` matrices. Path conventions follow
[vercel-labs/skills](https://github.com/vercel-labs/skills#supported-agents).
([#80])
- **Declarative platform registry.** Adapters are now defined as one row in
`internal/platform/spec.go` — a `Spec` carrying name, user dir, project dir,
and home-relative detection paths. A single generic `Adapter` struct
implements the `Platform` interface from any spec row, replacing the
per-platform Go files (`claude.go`, `codex.go`, `opencode.go`). Adding a
platform is a one-line PR. ([#80])

### Changed

- **Platform detection is per-platform, not per-directory.** Several adapters
share `.agents/skills/` as their project dir; detection now keys on each
adapter's distinct user-level config dir (`~/.cursor`, `~/.gemini`,
`~/.copilot`, `~/.codex`, etc.) so `platform list` doesn't false-positive
for platforms whose CLI isn't installed.
- **CLI flag help and error messages enumerate the registered platforms
dynamically** — adding a platform updates `--platform` help and the
"unknown platform" error text without touching the CLI.
- **`skill create --from-template <path>` now requires a skill directory.**
A skill is a folder, so `--from-template` accepts only a directory containing
a `SKILL.md`. Passing a bare file (a `SKILL.md` or a body-only markdown file)
Expand All @@ -22,6 +45,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
previous behavior of treating a non-frontmatter markdown file as a raw body
is removed — wrap such bodies in a directory with a `SKILL.md` instead.

[#80]: https://github.com/devrimcavusoglu/skern/issues/80

## [v0.2.1] — 2026-05-03

Cross-platform install. No code changes; release pipeline + install UX only.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

---

Skern is a minimal, agent-first CLI for managing [Agent Skills](https://agentskills.io) across **Claude Code**, **Codex CLI**, and **OpenCode**. One `SKILL.md` per skill — portable, validated, and instantly installable to any supported platform.
Skern is a minimal, agent-first CLI for managing [Agent Skills](https://agentskills.io) across **Claude Code**, **Codex CLI**, **OpenCode**, **Cursor**, **Gemini CLI**, **GitHub Copilot**, **Windsurf**, **Continue**, and more. One `SKILL.md` per skill — portable, validated, and instantly installable to any supported platform.

## Quick Example

Expand All @@ -31,7 +31,7 @@ skern skill install code-review --platform claude-code

- **Unified skill lifecycle** — create, validate, search, install, and remove across platforms
- **Agent Skills spec** — reads and writes `SKILL.md` directly, no proprietary format
- **Cross-platform** — install to Claude Code, Codex CLI, or OpenCode in one command
- **Cross-platform** — install to Claude Code, Codex CLI, OpenCode, Cursor, Gemini CLI, GitHub Copilot, Windsurf, Continue, and more
- **Tool-forming loop** — agents scaffold and reuse skills automatically
- **Overlap detection** — fuzzy matching prevents duplication
- **JSON output** — every command supports `--json` for agent-operable workflows
Expand Down
47 changes: 34 additions & 13 deletions docs/concepts/platform-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,42 @@ When you run `skern skill install`, the adapter:

## Supported Platforms

Adapter names match `vercel-labs/skills` agent identifiers wherever possible.

| Platform | Adapter name | Detection |
|----------|-------------|-----------|
| Claude Code | `claude-code` | Looks for `.claude/` or `~/.claude/` |
| Codex CLI | `codex-cli` | Looks for `.agents/` or `~/.agents/` |
| OpenCode | `opencode` | Looks for `.opencode/` or `~/.config/opencode/` |
|----------|--------------|-----------|
| Claude Code | `claude-code` | `~/.claude/` |
| Codex CLI | `codex-cli` | `~/.codex/` or `~/.agents/` |
| OpenCode | `opencode` | `~/.config/opencode/` |
| Cursor | `cursor` | `~/.cursor/` |
| Gemini CLI | `gemini-cli` | `~/.gemini/` |
| GitHub Copilot | `github-copilot` | `~/.copilot/` |
| Windsurf | `windsurf` | `~/.codeium/windsurf/` or `~/.windsurf/` |
| Continue | `continue` | `~/.continue/` |

See [Supported Platforms](/platforms/) for the full path reference.

## Installation Paths

Each platform uses different directories for user-level and project-level skills:

| Platform | User-level | Project-level |
|----------|-----------|---------------|
| Claude Code | `~/.claude/skills/<name>/` | `.claude/skills/<name>/` |
| Codex CLI | `~/.agents/skills/<name>/` | `.agents/skills/<name>/` |
| OpenCode | `~/.config/opencode/skills/<name>/` | `.opencode/skills/<name>/` |
Each platform uses different directories for user-level and project-level skills. Several platforms share `.agents/skills/` as the project directory — see [Shared project directory](#shared-project-directory) below.

## Auto-Detection

Skern auto-detects which platforms are installed on your system. Use `skern platform list` to see detected platforms:
Skern auto-detects which platforms are installed on your system by checking each platform's user-level config directory. Use `skern platform list` to see detected platforms:

```sh
skern platform list
```

## Shared project directory

`codex-cli`, `cursor`, `gemini-cli`, and `github-copilot` all use `.agents/skills/` as their project-level skills directory. A skill installed there is visible to every agent that reads from it — that is intentional and matches the conventions in `vercel-labs/skills`.

Two consequences:

- **Detection is per-platform**, not per-directory. The presence of `.agents/skills/` does not by itself indicate which agents are installed; skern looks at each platform's distinct user-level config dir (`~/.cursor`, `~/.gemini`, `~/.copilot`, `~/.codex`) to disambiguate.
- **Capacity is per-directory.** When two platforms share a directory, both adapters see the same installed-skills count. Capacity thresholds protect the directory, not the logical agent — installing 50 skills via `cursor` will register as full capacity for `gemini-cli` too, because the agent will load all of them.

## One Platform per Invocation

Each `skern skill install` call targets exactly one platform. Agents are expected to specify the platform they are running on — there is no `all` value, and skern does not broadcast skills across platforms automatically.
Expand All @@ -46,7 +58,7 @@ This design supports the [dynamic skill loading](./registry) model: each agent m
To deploy a skill across several platforms, loop the call:

```sh
for p in claude-code codex-cli opencode; do
for p in claude-code codex-cli opencode cursor gemini-cli; do
skern skill install code-review --platform "$p"
done
```
Expand All @@ -71,3 +83,12 @@ skern platform status
```

This shows a matrix of skills and their installation status across all detected platforms.

## Adding a Platform

Adapters are declarative: every supported platform is one row in `Specs` in [`internal/platform/spec.go`](https://github.com/devrimcavusoglu/skern/blob/main/internal/platform/spec.go). Adding a new platform takes:

1. A new `Type` constant in `internal/platform/platform.go`.
2. A new `Spec` row giving its name, user-level skills dir, project-level skills dir, and one or more home-relative detection paths.

A single generic `Adapter` struct implements every platform's `Platform` interface from the spec, so no platform-specific Go code is required. The table-driven tests in `internal/platform/platform_test.go` cover every registered spec automatically.
2 changes: 1 addition & 1 deletion docs/platforms/codex-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ skern skill uninstall code-review --platform codex-cli

## Detection

Skern detects Codex CLI by checking for the presence of `.agents/` in the current project or `~/.agents/` at the user level.
Skern detects Codex CLI by checking for `~/.codex/` (preferred) or `~/.agents/` at the user level. The `.agents/skills/` project directory is shared with several other agents (cursor, gemini-cli, github-copilot) — see [Platform Adapters › Shared project directory](/concepts/platform-adapters#shared-project-directory).

## How Skills Work in Codex CLI

Expand Down
28 changes: 20 additions & 8 deletions docs/platforms/index.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
# Supported Platforms

Skern supports three agentic development platforms. Each platform has a dedicated adapter that handles skill installation and uninstallation.
Skern ships adapters for the most common agentic development platforms. Each adapter knows where its platform stores user-level and project-level skills, copies skills into place, and reports which platforms are installed on the host.

## Platform Comparison
The set is driven by a declarative registry in `internal/platform/spec.go` — adding a new platform is a one-line append (see [Platform Adapters](/concepts/platform-adapters#adding-a-platform)).

| Feature | Claude Code | Codex CLI | OpenCode |
|---------|-------------|-----------|----------|
| User-level skills | `~/.claude/skills/` | `~/.agents/skills/` | `~/.config/opencode/skills/` |
| Project-level skills | `.claude/skills/` | `.agents/skills/` | `.opencode/skills/` |
| Auto-detection | Yes | Yes | Yes |
| Batch install (multiple skills, one call) | Yes | Yes | Yes |
## Path Reference

| Adapter name | User-level skills | Project-level skills |
|--------------|--------------------|----------------------|
| `claude-code` | `~/.claude/skills/` | `.claude/skills/` |
| `codex-cli` | `~/.agents/skills/` | `.agents/skills/` |
| `opencode` | `~/.config/opencode/skills/` | `.opencode/skills/` |
| `cursor` | `~/.cursor/skills/` | `.agents/skills/` |
| `gemini-cli` | `~/.gemini/skills/` | `.agents/skills/` |
| `github-copilot` | `~/.copilot/skills/` | `.agents/skills/` |
| `windsurf` | `~/.codeium/windsurf/skills/` | `.windsurf/skills/` |
| `continue` | `~/.continue/skills/` | `.continue/skills/` |

Several platforms share `.agents/skills/` as their project directory — a skill installed there is visible to every agent that reads from it. See [Platform Adapters](/concepts/platform-adapters#shared-project-directory) for the implications on capacity reporting and detection.

## Auto-detection

`skern platform list` reports which adapters appear installed on the current host. Detection is per-platform: each adapter checks its own user-level config dir (e.g. `~/.cursor`, `~/.gemini`, `~/.copilot`) so platforms that share a project directory are still distinguished.

## Feature Comparison

Expand Down
4 changes: 2 additions & 2 deletions docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ The response includes a `capacity` block reporting the platform's installed-skil

| Flag | Description |
|------|-------------|
| `--platform` | `claude-code`, `codex-cli`, or `opencode` (required) |
| `--platform` | One of: `claude-code`, `codex-cli`, `opencode`, `cursor`, `gemini-cli`, `github-copilot`, `windsurf`, `continue` (required) |
| `--scope` | `user` or `project` |
| `--force` | Overwrite existing installation |
| `--enforce-budget` | Refuse the operation if it would push the platform's installed-skill count past the per-scope threshold |
Expand All @@ -207,7 +207,7 @@ Mirrors `install` semantics: one platform per call, multiple skills allowed, par

| Flag | Description |
|------|-------------|
| `--platform` | `claude-code`, `codex-cli`, or `opencode` (required) |
| `--platform` | One of: `claude-code`, `codex-cli`, `opencode`, `cursor`, `gemini-cli`, `github-copilot`, `windsurf`, `continue` (required) |
| `--scope` | `user` or `project` |

## `skern platform list`
Expand Down
13 changes: 13 additions & 0 deletions internal/cli/capacity.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,25 @@ package cli

import (
"fmt"
"strings"

"github.com/devrimcavusoglu/skern/internal/output"
"github.com/devrimcavusoglu/skern/internal/platform"
"github.com/devrimcavusoglu/skern/internal/skill"
)

// platformNamesList returns a comma-separated list of supported platform names
// for use in flag help text and error messages. Pulled from the platform spec
// registry so adding a platform takes one line.
func platformNamesList() string {
names := platform.SupportedNames()
parts := make([]string, len(names))
for i, n := range names {
parts[i] = string(n)
}
return strings.Join(parts, ", ")
}

// buildCapacityReport queries a platform for its currently-installed skills
// at the given scope and returns a CapacityReport ready for inclusion in
// command output. Returns nil if the query fails (capacity is best-effort
Expand Down
12 changes: 6 additions & 6 deletions internal/cli/platform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ func withTestDetector(t *testing.T, cc *CommandContext, home, project string) {

cc.NewDetector = func() (*platform.Detector, error) {
return platform.NewDetectorWithPlatforms([]platform.Platform{
platform.NewClaudeCode(home, project),
platform.NewCodexCLI(home, project),
platform.NewOpenCode(home, project),
platform.New(platform.TypeClaudeCode, home, project),
platform.New(platform.TypeCodexCLI, home, project),
platform.New(platform.TypeOpenCode, home, project),
}), nil
}
}
Expand Down Expand Up @@ -427,9 +427,9 @@ func TestPlatformList_PartialDetection(t *testing.T) {
NewRegistry: defaultNewRegistry,
NewDetector: func() (*platform.Detector, error) {
return platform.NewDetectorWithPlatforms([]platform.Platform{
platform.NewClaudeCode(home, project),
platform.NewCodexCLI(home, project),
platform.NewOpenCode(home, project),
platform.New(platform.TypeClaudeCode, home, project),
platform.New(platform.TypeCodexCLI, home, project),
platform.New(platform.TypeOpenCode, home, project),
}), nil
},
}
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/skill_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ With two arguments, compares two registry skills by name
}

cmd.Flags().StringVar(&scope, "scope", "", "skill scope (user or project)")
cmd.Flags().StringVar(&platformFlag, "platform", "", "platform to compare against (claude-code, codex-cli, opencode)")
cmd.Flags().StringVar(&platformFlag, "platform", "", "platform to compare against (one of: "+platformNamesList()+")")

return cmd
}
Expand Down
Loading
Loading