@crouton-kit/crouter 0.1.9 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builtin-skills/.crouter-plugin/plugin.json +5 -0
- package/dist/builtin-skills/skills/crouter-development/marketplaces/SKILL.md +157 -0
- package/dist/builtin-skills/skills/crouter-development/plugins/SKILL.md +156 -0
- package/dist/builtin-skills/skills/crouter-development/skills/SKILL.md +166 -0
- package/dist/commands/doctor.js +54 -2
- package/dist/commands/plugin.js +4 -1
- package/dist/commands/skill.js +61 -7
- package/dist/core/frontmatter.d.ts +1 -1
- package/dist/core/frontmatter.js +5 -0
- package/dist/core/resolver.d.ts +2 -0
- package/dist/core/resolver.js +63 -2
- package/dist/core/scope.d.ts +1 -0
- package/dist/core/scope.js +13 -2
- package/dist/prompts/skill.js +275 -27
- package/dist/types.d.ts +6 -1
- package/dist/types.js +4 -0
- package/package.json +2 -2
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: crouter-development/marketplaces
|
|
3
|
+
type: playbook
|
|
4
|
+
description: How to author a crtr marketplace — marketplace.json index, plugin entries, symlink-based install, auto-bump CI, dual-publishing. Use when creating a marketplace or contributing plugins to one.
|
|
5
|
+
keywords: [marketplace, marketplace.json, plugin, index, publishing]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Authoring crtr marketplaces
|
|
9
|
+
|
|
10
|
+
A **marketplace** is a git repo that indexes multiple plugins. Users register it once, then `crtr marketplace install <mkt>:<plugin>` symlinks any plugin from it. One `crtr marketplace update` pulls updates for every installed plugin at once.
|
|
11
|
+
|
|
12
|
+
Audience: LLM agents creating a marketplace or adding plugins to an existing one.
|
|
13
|
+
|
|
14
|
+
## When you need a marketplace (vs a single-plugin repo)
|
|
15
|
+
|
|
16
|
+
A solo plugin ships from any git URL via `crtr plugin install <url>` — no marketplace needed.
|
|
17
|
+
|
|
18
|
+
Reach for a marketplace when:
|
|
19
|
+
- You want to publish ≥3 plugins under one brand or theme.
|
|
20
|
+
- Users should install selectively without cloning each plugin's repo.
|
|
21
|
+
- You want centralized version-bump automation across many plugins.
|
|
22
|
+
|
|
23
|
+
If you have one plugin, ship it standalone. Promote to a marketplace later.
|
|
24
|
+
|
|
25
|
+
## Directory layout
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
<marketplace-repo>/
|
|
29
|
+
├── .crouter-marketplace/
|
|
30
|
+
│ └── marketplace.json # marketplace manifest — required
|
|
31
|
+
└── plugins/
|
|
32
|
+
├── plugin-a/
|
|
33
|
+
│ ├── .crouter-plugin/plugin.json
|
|
34
|
+
│ └── skills/...
|
|
35
|
+
└── plugin-b/
|
|
36
|
+
└── ...
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Each entry under `plugins/` is a complete plugin (see [[crouter-development/plugins]]). The marketplace adds an index on top.
|
|
40
|
+
|
|
41
|
+
## The manifest
|
|
42
|
+
|
|
43
|
+
`.crouter-marketplace/marketplace.json`:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"name": "my-marketplace",
|
|
48
|
+
"version": "0.3.0",
|
|
49
|
+
"owner": { "name": "Your Name", "email": "you@example.com" },
|
|
50
|
+
"plugins": [
|
|
51
|
+
{
|
|
52
|
+
"name": "plugin-a",
|
|
53
|
+
"version": "0.2.1",
|
|
54
|
+
"source": "https://github.com/<owner>/<repo>",
|
|
55
|
+
"description": "One sentence."
|
|
56
|
+
},
|
|
57
|
+
{ "name": "plugin-b", "version": "0.1.0", "source": "…", "description": "…" }
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
| Field | Required | Notes |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `name` | yes | Lowercase kebab. |
|
|
65
|
+
| `version` | yes | Semver. Bumps when any plugin bumps or when the marketplace manifest itself changes. |
|
|
66
|
+
| `owner` | optional | |
|
|
67
|
+
| `plugins` | yes | Array. Each entry: `name`, `version`, `source`, `description`. |
|
|
68
|
+
|
|
69
|
+
The `plugins[]` array IS the index — what's installable. A plugin on disk but not in the index won't resolve. A plugin in the index but missing from disk errors on install.
|
|
70
|
+
|
|
71
|
+
## Install mechanics — symlinks, not clones
|
|
72
|
+
|
|
73
|
+
When a user runs `crtr marketplace install my-marketplace:plugin-a`:
|
|
74
|
+
1. Crouter looks up `plugin-a` in the marketplace's manifest.
|
|
75
|
+
2. **Symlinks** `<marketplace-clone>/plugins/plugin-a/` → `<user-scope>/plugins/plugin-a/`.
|
|
76
|
+
3. Records the install in the scope config with `source_marketplace: my-marketplace`.
|
|
77
|
+
|
|
78
|
+
Therefore one `crtr marketplace update my-marketplace` (which does `git pull` in the marketplace clone) updates every installed plugin from it — no per-plugin re-install, no second clone.
|
|
79
|
+
|
|
80
|
+
## Version-bump automation (recommended)
|
|
81
|
+
|
|
82
|
+
Bumping N plugin manifests + the index by hand is error-prone. The canonical pattern is a GitHub Actions workflow that bumps versions from commit subjects (conventional commits):
|
|
83
|
+
|
|
84
|
+
| Commit subject | Bump |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `feat!: …` or `BREAKING CHANGE` in body | major |
|
|
87
|
+
| `feat: …` | minor |
|
|
88
|
+
| anything else (`fix:`, `chore:`, `docs:`, …) | patch |
|
|
89
|
+
|
|
90
|
+
Per commit:
|
|
91
|
+
- For each plugin folder touched, both `plugins/<name>/.crouter-plugin/plugin.json` and the matching entry in `marketplace.json` get bumped.
|
|
92
|
+
- Top-level `marketplace.json` version bumps when any plugin bumps OR the manifest itself was edited directly.
|
|
93
|
+
- Newly-added plugins are skipped — they keep the version you committed.
|
|
94
|
+
- Commits whose subject starts with `chore: release` are skipped to avoid loops.
|
|
95
|
+
|
|
96
|
+
If you adopt this: **never edit `version` fields by hand**. CI will overwrite. Document this in the marketplace's CLAUDE.md.
|
|
97
|
+
|
|
98
|
+
The crouter-official-marketplace repo's `.github/workflows/auto-bump.yml` is the reference implementation. Copy it.
|
|
99
|
+
|
|
100
|
+
## Adding a plugin to a marketplace
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
cd <marketplace-repo>
|
|
104
|
+
|
|
105
|
+
# Create the plugin (see [[crouter-development/plugins]] for plugin layout)
|
|
106
|
+
mkdir -p plugins/my-new-plugin/.crouter-plugin plugins/my-new-plugin/skills
|
|
107
|
+
$EDITOR plugins/my-new-plugin/.crouter-plugin/plugin.json
|
|
108
|
+
|
|
109
|
+
# Add at least one skill
|
|
110
|
+
crtr skill new my-new-plugin:first-skill --type playbook --description "Use when …"
|
|
111
|
+
|
|
112
|
+
# Add the plugin to the marketplace index
|
|
113
|
+
$EDITOR .crouter-marketplace/marketplace.json
|
|
114
|
+
# (append to plugins[] with name, initial version, source, description)
|
|
115
|
+
|
|
116
|
+
# Validate
|
|
117
|
+
crtr doctor
|
|
118
|
+
|
|
119
|
+
# Commit — CI bumps versions if you've wired up auto-bump
|
|
120
|
+
git add -A
|
|
121
|
+
git commit -m "feat: add my-new-plugin"
|
|
122
|
+
git push
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Existing users pick it up on their next `crtr marketplace update <marketplace-name>` (or on the next auto-update tick if they've set `auto_update.content: "apply"`).
|
|
126
|
+
|
|
127
|
+
## Updating an existing plugin
|
|
128
|
+
|
|
129
|
+
Edit content under `plugins/<name>/`. Commit with a conventional-commit subject. CI bumps the plugin manifest and the marketplace index entry together. No manual sync.
|
|
130
|
+
|
|
131
|
+
## Removing a plugin
|
|
132
|
+
|
|
133
|
+
Delete the plugin's directory AND its entry in `marketplace.json` → `plugins[]`. Commit with `feat!: remove <plugin>` to signal a major bump (the marketplace's contract changed for anyone depending on that plugin).
|
|
134
|
+
|
|
135
|
+
## Marketplace registration scopes
|
|
136
|
+
|
|
137
|
+
A marketplace itself registers per-scope:
|
|
138
|
+
|
|
139
|
+
| Scope | Path | Use case |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| user | `~/.crouter/marketplaces/<name>/` | Private to your machine — your personal subscriptions |
|
|
142
|
+
| project | `<project>/.crouter/marketplaces/<name>/` | Checked into the repo — anyone cloning gets the same marketplace pinned |
|
|
143
|
+
|
|
144
|
+
`crtr marketplace add <git-url> --scope user` is the default. Use `--scope project` when the marketplace is integral to how the repo expects to be developed.
|
|
145
|
+
|
|
146
|
+
## Validation
|
|
147
|
+
|
|
148
|
+
`crtr doctor` checks marketplaces:
|
|
149
|
+
- `marketplace.json` is valid JSON.
|
|
150
|
+
- Every entry in `plugins[]` corresponds to a real directory under `plugins/`.
|
|
151
|
+
- Each plugin under `plugins/` passes plugin-level validation.
|
|
152
|
+
|
|
153
|
+
A plugin on disk but missing from the manifest is a **warning** (probably forgotten); a plugin in the manifest but missing on disk is an **error** (install would break).
|
|
154
|
+
|
|
155
|
+
## Cross-publishing with Claude Code
|
|
156
|
+
|
|
157
|
+
Some marketplaces ship a parallel `.claude-plugin/` tree so plugins can load directly into Claude Code without crtr. Optional. Sync the two manifests if you adopt it.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: crouter-development/plugins
|
|
3
|
+
type: playbook
|
|
4
|
+
description: How to author a crtr plugin — plugin.json manifest, directory layout, scopes, install mechanics, versioning. Use when creating a new plugin, packaging skills for distribution, or debugging install/resolution.
|
|
5
|
+
keywords: [plugin, plugin.json, manifest, install, scope]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Authoring crtr plugins
|
|
9
|
+
|
|
10
|
+
A **plugin** is a directory shipping skills (and, in future, other artifact types like commands and hooks). Plugins are how you package skills for sharing across machines, projects, and people.
|
|
11
|
+
|
|
12
|
+
Audience: LLM agents creating or maintaining a crtr plugin.
|
|
13
|
+
|
|
14
|
+
## When you need a plugin (vs scope-owned skills)
|
|
15
|
+
|
|
16
|
+
Scope-owned skills live at `~/.crouter/skills/` (user) or `<project>/.crouter/skills/` (project). They're personal and per-machine/per-repo.
|
|
17
|
+
|
|
18
|
+
Reach for a **plugin** when:
|
|
19
|
+
- You want to share skills across multiple projects or with other people.
|
|
20
|
+
- You want versioning + update mechanics (`crtr plugin update`).
|
|
21
|
+
- You want a marketplace to index the work — see [[crouter-development/marketplaces]].
|
|
22
|
+
|
|
23
|
+
If it's a one-off note for yourself, scope-owned skills are simpler. Promote to a plugin later.
|
|
24
|
+
|
|
25
|
+
## Directory layout
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
<plugin-name>/
|
|
29
|
+
├── .crouter-plugin/
|
|
30
|
+
│ └── plugin.json # manifest — required
|
|
31
|
+
└── skills/
|
|
32
|
+
└── <skill-name>/
|
|
33
|
+
└── SKILL.md
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The `<plugin-name>` directory IS the plugin. The manifest's `name` field must match the directory name (install renames if needed). Future artifact types (`commands/`, `hooks/`, etc.) will be sibling dirs to `skills/`.
|
|
37
|
+
|
|
38
|
+
## The manifest
|
|
39
|
+
|
|
40
|
+
`.crouter-plugin/plugin.json`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"name": "my-plugin",
|
|
45
|
+
"version": "0.1.0",
|
|
46
|
+
"description": "One sentence — shown in `crtr plugin list`.",
|
|
47
|
+
"source": "https://github.com/<owner>/<repo>",
|
|
48
|
+
"owner": {
|
|
49
|
+
"name": "Your Name",
|
|
50
|
+
"email": "you@example.com"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
| Field | Required | Notes |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| `name` | yes | Must match the directory name. Lowercase kebab. |
|
|
58
|
+
| `version` | yes | Semver. Marketplace CI may bump automatically — see marketplaces skill. |
|
|
59
|
+
| `description` | yes | One sentence. |
|
|
60
|
+
| `source` | recommended | Git URL where the plugin lives. Used by `crtr plugin update`. |
|
|
61
|
+
| `owner` | optional | Author info. |
|
|
62
|
+
|
|
63
|
+
## Scopes
|
|
64
|
+
|
|
65
|
+
A plugin can live in either scope:
|
|
66
|
+
|
|
67
|
+
| Scope | Path | Use case |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| user | `~/.crouter/plugins/<name>/` | Personal, available in all your projects |
|
|
70
|
+
| project | `<project>/.crouter/plugins/<name>/` | Pinned to a specific repo — checked in or vendored |
|
|
71
|
+
|
|
72
|
+
Project-scope plugins outrank user-scope on resolution. Both outrank marketplace-installed plugins. The builtin `crtr` plugin (ships with the CLI) sits at the bottom.
|
|
73
|
+
|
|
74
|
+
## Install mechanics
|
|
75
|
+
|
|
76
|
+
Three ways a plugin lands in a scope:
|
|
77
|
+
|
|
78
|
+
1. **From a git URL** (`crtr plugin install <url> --scope user`):
|
|
79
|
+
- Clones into `<scope>/plugins/<name>/` using the manifest's name.
|
|
80
|
+
- `crtr plugin update <name>` does `git pull`.
|
|
81
|
+
- Independent of any marketplace.
|
|
82
|
+
|
|
83
|
+
2. **From a marketplace** (`crtr marketplace install <mkt>:<name> --scope user`):
|
|
84
|
+
- **Symlinks** the marketplace's `plugins/<name>/` into `<scope>/plugins/<name>/`.
|
|
85
|
+
- One `crtr marketplace update <mkt>` pulls updates for every installed plugin from that marketplace.
|
|
86
|
+
- See [[crouter-development/marketplaces]].
|
|
87
|
+
|
|
88
|
+
3. **Authored in place** (you're writing the plugin in a working repo):
|
|
89
|
+
- Symlink for tight dev loop: `ln -s $(pwd) ~/.crouter/plugins/<name>`.
|
|
90
|
+
- Or `crtr plugin install file://$(pwd) --scope project` to clone-install.
|
|
91
|
+
|
|
92
|
+
## Local development loop
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Scaffold dir + manifest + first skill
|
|
96
|
+
mkdir -p my-plugin/.crouter-plugin my-plugin/skills
|
|
97
|
+
$EDITOR my-plugin/.crouter-plugin/plugin.json # write the manifest
|
|
98
|
+
cd my-plugin
|
|
99
|
+
crtr skill new my-plugin:my-first-skill --type playbook --description "Use when …"
|
|
100
|
+
|
|
101
|
+
# Symlink for fast iteration — no clone, edits land immediately
|
|
102
|
+
ln -s $(pwd) ~/.crouter/plugins/my-plugin
|
|
103
|
+
|
|
104
|
+
# Verify
|
|
105
|
+
crtr plugin list # my-plugin appears
|
|
106
|
+
crtr plugin show my-plugin # lists its skills
|
|
107
|
+
crtr skill list --plugin my-plugin
|
|
108
|
+
crtr doctor # validates manifest + every skill
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
When ready to share: push to a git remote; anyone can `crtr plugin install <url>`.
|
|
112
|
+
|
|
113
|
+
## Versioning
|
|
114
|
+
|
|
115
|
+
Standard semver:
|
|
116
|
+
|
|
117
|
+
| Change | Bump |
|
|
118
|
+
|---|---|
|
|
119
|
+
| Typo, wording polish | patch (0.1.0 → 0.1.1) |
|
|
120
|
+
| New skill, new section, new example | minor (0.1.0 → 0.2.0) |
|
|
121
|
+
| Removed skill, renamed skill, changed manifest schema | major (0.1.0 → 1.0.0) |
|
|
122
|
+
|
|
123
|
+
`crtr plugin update <name>` reads the new version after pulling and updates the local config. Plugins published through a marketplace may have their `version` field bumped automatically by CI — see [[crouter-development/marketplaces]].
|
|
124
|
+
|
|
125
|
+
## Enable/disable
|
|
126
|
+
|
|
127
|
+
`crtr plugin disable <name>` flips the per-scope config without removing files. Disabled plugins are hidden from `crtr skill list` and don't resolve via `crtr skill show <name>`. Re-enable with `crtr plugin enable <name>`.
|
|
128
|
+
|
|
129
|
+
Individual skills inside an enabled plugin can also be disabled: `crtr skill disable <plugin>:<skill>`.
|
|
130
|
+
|
|
131
|
+
## What goes in a plugin
|
|
132
|
+
|
|
133
|
+
Good plugin scope:
|
|
134
|
+
- A coherent set of related skills (3–15 typical) sharing a theme.
|
|
135
|
+
- All skills serve the same user persona or workflow.
|
|
136
|
+
- Versioned together — a bump means a bump for the whole set.
|
|
137
|
+
|
|
138
|
+
Bad plugin scope:
|
|
139
|
+
- One mega-plugin with every skill you've ever written. Hard to install selectively, hard to version.
|
|
140
|
+
- A plugin per single skill. No value-add over scope-owned skills.
|
|
141
|
+
|
|
142
|
+
## Cross-plugin etiquette
|
|
143
|
+
|
|
144
|
+
If your skill conceptually depends on another plugin's skill, link via `## Related` with `` `<plugin>/<skill>` `` (e.g., `crtr/crouter-development/skills` for the builtin format guide). Don't fork content; link it.
|
|
145
|
+
|
|
146
|
+
## Validation
|
|
147
|
+
|
|
148
|
+
`crtr doctor` checks every plugin:
|
|
149
|
+
- Manifest exists and is valid JSON.
|
|
150
|
+
- Manifest `name` matches the directory name.
|
|
151
|
+
- Every skill under `skills/` passes the skill-validation contract (see [[crouter-development/skills]]).
|
|
152
|
+
- Sibling artifact dirs (`commands/`, `hooks/`, etc.) — validated by their respective specs as those land.
|
|
153
|
+
|
|
154
|
+
## Cross-publishing with Claude Code
|
|
155
|
+
|
|
156
|
+
Some plugins also publish a `.claude-plugin/` manifest alongside `.crouter-plugin/` so they can be loaded directly into Claude Code without going through crtr. Optional. Only worth doing when your skills/commands meaningfully stand alone in the Claude Code surface. Keep manifests in sync if you do.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: crouter-development/skills
|
|
3
|
+
type: playbook
|
|
4
|
+
description: How to author a crtr skill — SKILL.md format, frontmatter, naming, nested skills, template types. Use when creating a new skill (plugin-owned or scope-owned) or auditing an existing one.
|
|
5
|
+
keywords: [skill, SKILL.md, frontmatter, authoring, template]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Authoring crtr skills
|
|
9
|
+
|
|
10
|
+
A **skill** is a directory; `SKILL.md` is its entry file — the same role `index.html` plays for a web dir. The dir IS the skill: siblings (`reference.md`, scripts, examples) ride along, nested subdirs are themselves skills.
|
|
11
|
+
|
|
12
|
+
Audience: LLM agents loading this to create or audit a skill. Decision-first.
|
|
13
|
+
|
|
14
|
+
## Minimum viable skill
|
|
15
|
+
|
|
16
|
+
**Plugin-owned:**
|
|
17
|
+
```
|
|
18
|
+
<plugin-root>/skills/<name>/SKILL.md
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Scope-owned** (no plugin, just your project or user scope):
|
|
22
|
+
```
|
|
23
|
+
~/.crouter/skills/<name>/SKILL.md # user scope
|
|
24
|
+
<project>/.crouter/skills/<name>/SKILL.md # project scope
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```markdown
|
|
28
|
+
---
|
|
29
|
+
name: <name>
|
|
30
|
+
type: <type>
|
|
31
|
+
description: <one sentence — used by the agent to decide whether to load this>
|
|
32
|
+
keywords: [optional, list]
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
# Title
|
|
36
|
+
|
|
37
|
+
Body in markdown. This is what `crtr skill show <name>` prints.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Drop the directory in place; `crtr skill list` picks it up. No build, no registry.
|
|
41
|
+
|
|
42
|
+
## Template types
|
|
43
|
+
|
|
44
|
+
Pick the type matching what the agent *does* after reading.
|
|
45
|
+
|
|
46
|
+
| Type | Agent action | Source of truth | Example |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| `playbook` | Decides — applies judgment ("when X, do Y because Z") | The skill itself | this skill, cli-design |
|
|
49
|
+
| `primer` | Navigates — learns architectural facts about a codebase | Code in repo | crtr-internals |
|
|
50
|
+
| `reference` | Looks up — stable external facts | Docs/specs/protocols | semver-rules |
|
|
51
|
+
| `runbook` | Executes — numbered procedure with rollback | The skill (steps fixed) | deploy-plugin |
|
|
52
|
+
| `freeform` | None of the above | Varies | catch-all |
|
|
53
|
+
|
|
54
|
+
### Picking the type
|
|
55
|
+
|
|
56
|
+
- Agent needs to make a *judgment call*? → `playbook`. Lead with rules; show examples; state the *why* so edge cases resolve themselves.
|
|
57
|
+
- Agent needs to *navigate code* that drifts over time? → `primer`. Point at `file:line`. Don't transcribe code; explain the *shape*.
|
|
58
|
+
- Agent needs *stable facts* (semver, HTTP codes, protocol fields)? → `reference`. Tables, terse, no methodology.
|
|
59
|
+
- Agent needs to *run a procedure* with known steps and failure modes? → `runbook`. Numbered. Include rollback per step.
|
|
60
|
+
- None of the above → `freeform`. Try harder first — the other four catch ~95% of cases.
|
|
61
|
+
|
|
62
|
+
### Anti-patterns by type
|
|
63
|
+
|
|
64
|
+
- `playbook` that's mostly code listings → split code to siblings; keep judgment in SKILL.md.
|
|
65
|
+
- `primer` that duplicates code it points to → just point; don't transcribe.
|
|
66
|
+
- `reference` that contains methodology → split methodology into a `playbook` sibling.
|
|
67
|
+
- `runbook` with no decision points → it's a script; ship a script.
|
|
68
|
+
|
|
69
|
+
## The `name` rule
|
|
70
|
+
|
|
71
|
+
`name:` MUST equal the skill's path under `skills/`. For `skills/cli-design/SKILL.md`, `name: cli-design`. For `skills/web/frontend/design-website/SKILL.md`, `name: web/frontend/design-website` — slashes and all.
|
|
72
|
+
|
|
73
|
+
`crtr skill new <plugin>:<name>` (or `crtr skill new <name>` for scope-owned) scaffolds this correctly. `crtr doctor` flags drift.
|
|
74
|
+
|
|
75
|
+
## Nested skills
|
|
76
|
+
|
|
77
|
+
Nesting is directory nesting. No `parent:` field, no registry. The path IS the hierarchy.
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
skills/
|
|
81
|
+
├── prompting-effectively/SKILL.md # flat
|
|
82
|
+
├── cli-design/ # flat with sibling assets
|
|
83
|
+
│ ├── SKILL.md
|
|
84
|
+
│ └── reference.md
|
|
85
|
+
└── web/
|
|
86
|
+
└── frontend/
|
|
87
|
+
└── design-website/SKILL.md # name: web/frontend/design-website
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Resolve nested with `crtr skill show web/frontend/design-website`.
|
|
91
|
+
|
|
92
|
+
Intermediate dirs (`web/`, `web/frontend/`) can be purely organizational — no SKILL.md required at each level. Add one only if you want a primer for the whole nest.
|
|
93
|
+
|
|
94
|
+
## Assets and references
|
|
95
|
+
|
|
96
|
+
Anything sibling to `SKILL.md` is part of the skill. The `cli-design` shape — `SKILL.md` for prose, `reference.md` for tables you don't want inline — is canonical. Scripts, example configs, screenshots all live as siblings. Reference from `SKILL.md` with relative paths.
|
|
97
|
+
|
|
98
|
+
Don't use a top-level `assets/` dir; the dir IS the skill, and grouping per skill keeps moves atomic.
|
|
99
|
+
|
|
100
|
+
## Frontmatter fields
|
|
101
|
+
|
|
102
|
+
| Field | Required | Notes |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| `name` | yes | Must equal path under `skills/`. Slashes for nested. |
|
|
105
|
+
| `type` | yes | One of: `playbook` `primer` `reference` `runbook` `freeform`. |
|
|
106
|
+
| `description` | yes | One sentence. Front-load the trigger ("Use when…"). The agent decides whether to load based on this. |
|
|
107
|
+
| `keywords` | no | Array of strings. Improves `crtr skill grep` and search. |
|
|
108
|
+
|
|
109
|
+
Descriptions: active and specific. "Guide to X" is weak; "Use when doing X — covers Y and Z" is strong.
|
|
110
|
+
|
|
111
|
+
## The authoring loop
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# 1. Scaffold — plugin-owned or scope-owned
|
|
115
|
+
crtr skill new <plugin>:my-skill --type playbook --description "Use when …"
|
|
116
|
+
crtr skill new my-skill --type playbook --description "Use when …" # scope-owned
|
|
117
|
+
|
|
118
|
+
# 2. Find and edit
|
|
119
|
+
crtr skill path <plugin>:my-skill # absolute path
|
|
120
|
+
$EDITOR $(crtr skill path <plugin>:my-skill)
|
|
121
|
+
|
|
122
|
+
# 3. Validate
|
|
123
|
+
crtr doctor
|
|
124
|
+
|
|
125
|
+
# 4. Preview as the agent sees it (includes auto-appended neighbors)
|
|
126
|
+
crtr skill show my-skill
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Cross-skill links
|
|
130
|
+
|
|
131
|
+
- Sibling and nested skills in the same plugin/scope auto-append in a `<neighbors>` XML block inside the `<skill>` wrapper. Don't hand-roll these.
|
|
132
|
+
- `## Related` in the body is for **cross-plugin or distant** references only — not siblings.
|
|
133
|
+
- Cross-plugin refs: `` `<plugin>/<name>` ``. Scope-direct: `` `<scope>:<name>` ``.
|
|
134
|
+
- Suppress neighbors: `crtr skill show <name> --no-neighbors`.
|
|
135
|
+
|
|
136
|
+
If `## Related` would only list siblings, delete the section.
|
|
137
|
+
|
|
138
|
+
## What goes in the body
|
|
139
|
+
|
|
140
|
+
Skills are reference + methodology for an agent. Good skills:
|
|
141
|
+
- Lead with trigger conditions and a one-paragraph summary so the agent can decide whether to read further.
|
|
142
|
+
- Use greppable headings (`## When to use`, `## Common pitfalls`).
|
|
143
|
+
- Show concrete invocations — code blocks beat prose.
|
|
144
|
+
- Link sibling assets: `see [reference.md](./reference.md)`.
|
|
145
|
+
|
|
146
|
+
Bad skills:
|
|
147
|
+
- Tell the agent what it already knows ("markdown uses `#` for headings").
|
|
148
|
+
- Bury triggers under five paragraphs of motivation.
|
|
149
|
+
- Pile related-but-distinct topics into one skill — split into nested sub-skills.
|
|
150
|
+
|
|
151
|
+
Budget ~150 lines for `SKILL.md` bodies. If a section exceeds 20 lines without teaching judgment, move it to a sibling file.
|
|
152
|
+
|
|
153
|
+
## Validation
|
|
154
|
+
|
|
155
|
+
`crtr doctor` checks every skill:
|
|
156
|
+
- Frontmatter parses as YAML.
|
|
157
|
+
- `name` matches the directory path.
|
|
158
|
+
- `SKILL.md` exists and is non-empty.
|
|
159
|
+
- Referenced sibling files exist (best-effort from markdown links).
|
|
160
|
+
- `type` is present and in the enum (fails if not).
|
|
161
|
+
|
|
162
|
+
A skill failing doctor still loads — doctor is advisory — but `crtr skill list --json` flags it.
|
|
163
|
+
|
|
164
|
+
## Collisions
|
|
165
|
+
|
|
166
|
+
Skill names need only be unique *within a plugin*. Two plugins shipping the same name collide; resolution order (project plugin → user plugin → project marketplace → user marketplace → builtin) picks one, and `crtr` exits `4` for ambiguous lookups when explicitness is required. Disambiguate with `<plugin>:<skill>`: `crtr skill show crtr:crouter-development/skills`.
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
+
import { SKILL_TYPES, isSkillType } from '../types.js';
|
|
2
3
|
import { out, jsonOut, handleError, stdoutColor } from '../core/output.js';
|
|
3
|
-
import { scopeRoot, pluginsDir, marketplacesDir, listScopes } from '../core/scope.js';
|
|
4
|
+
import { scopeRoot, pluginsDir, marketplacesDir, listScopes, builtinSkillsRoot } from '../core/scope.js';
|
|
4
5
|
import { readConfig, updateConfig } from '../core/config.js';
|
|
5
6
|
import { listInstalledPlugins, listSkillsInPlugin } from '../core/resolver.js';
|
|
6
|
-
import { pathExists, listDirs, removePath } from '../core/fs-utils.js';
|
|
7
|
+
import { pathExists, listDirs, removePath, readText } from '../core/fs-utils.js';
|
|
7
8
|
import { readPluginManifest, readMarketplaceManifest } from '../core/manifest.js';
|
|
9
|
+
import { parseFrontmatter } from '../core/frontmatter.js';
|
|
8
10
|
import { lsRemote } from '../core/git.js';
|
|
9
11
|
import { ExitCode } from '../types.js';
|
|
10
12
|
function pass(scope, name, message) {
|
|
@@ -16,7 +18,46 @@ function fail(scope, name, message) {
|
|
|
16
18
|
function warn(scope, name, message) {
|
|
17
19
|
return { scope, name, status: 'warn', message };
|
|
18
20
|
}
|
|
21
|
+
function readRawTypeField(skillPath) {
|
|
22
|
+
const content = readText(skillPath);
|
|
23
|
+
const { raw } = parseFrontmatter(content);
|
|
24
|
+
if (!raw)
|
|
25
|
+
return undefined;
|
|
26
|
+
const m = raw.match(/^type:\s*(.+?)\s*$/m);
|
|
27
|
+
if (!m)
|
|
28
|
+
return undefined;
|
|
29
|
+
let v = m[1].trim();
|
|
30
|
+
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) {
|
|
31
|
+
v = v.slice(1, -1);
|
|
32
|
+
}
|
|
33
|
+
return v;
|
|
34
|
+
}
|
|
35
|
+
function runChecksForBuiltin() {
|
|
36
|
+
const root = builtinSkillsRoot();
|
|
37
|
+
const plugins = listInstalledPlugins('builtin');
|
|
38
|
+
if (plugins.length === 0) {
|
|
39
|
+
return [fail('builtin', 'builtin:crtr:root', `builtin-skills root missing or has no valid plugin.json: ${root}`)];
|
|
40
|
+
}
|
|
41
|
+
const results = [
|
|
42
|
+
pass('builtin', 'builtin:crtr:root', `builtin-skills root present: ${root}`),
|
|
43
|
+
];
|
|
44
|
+
for (const plugin of plugins) {
|
|
45
|
+
results.push(pass('builtin', `builtin:${plugin.name}:manifest`, `manifest valid`));
|
|
46
|
+
const skills = listSkillsInPlugin(plugin);
|
|
47
|
+
for (const skill of skills) {
|
|
48
|
+
if (!skill.frontmatter.name) {
|
|
49
|
+
results.push(fail('builtin', `builtin:${plugin.name}:skill:${skill.name}:frontmatter`, `frontmatter missing or name field empty`));
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
results.push(pass('builtin', `builtin:${plugin.name}:skill:${skill.name}:frontmatter`, `frontmatter valid`));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return results;
|
|
57
|
+
}
|
|
19
58
|
function runChecksForScope(scope, opts) {
|
|
59
|
+
if (scope === 'builtin')
|
|
60
|
+
return runChecksForBuiltin();
|
|
20
61
|
const results = [];
|
|
21
62
|
const root = scopeRoot(scope);
|
|
22
63
|
if (!root)
|
|
@@ -138,6 +179,17 @@ function runChecksForScope(scope, opts) {
|
|
|
138
179
|
else {
|
|
139
180
|
results.push(pass(scope, `plugin:${name}:skill:${skill.name}:frontmatter`, `frontmatter valid`));
|
|
140
181
|
}
|
|
182
|
+
const typeCheckName = `plugin:${name}:skill:${skill.name}:type`;
|
|
183
|
+
const rawType = readRawTypeField(skill.path);
|
|
184
|
+
if (rawType === undefined) {
|
|
185
|
+
results.push(warn(scope, typeCheckName, `missing type field — add one of: ${SKILL_TYPES.join(' | ')}`));
|
|
186
|
+
}
|
|
187
|
+
else if (!isSkillType(rawType)) {
|
|
188
|
+
results.push(fail(scope, typeCheckName, `invalid type "${rawType}" — valid: ${SKILL_TYPES.join(' | ')}`));
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
results.push(pass(scope, typeCheckName, `type: ${rawType}`));
|
|
192
|
+
}
|
|
141
193
|
}
|
|
142
194
|
}
|
|
143
195
|
// Git remote check (slow, opt-in)
|
package/dist/commands/plugin.js
CHANGED
|
@@ -178,10 +178,13 @@ export function registerPluginCommands(program) {
|
|
|
178
178
|
.option('--yes', 'skip confirmation in non-TTY mode')
|
|
179
179
|
.action(async (name, opts) => {
|
|
180
180
|
try {
|
|
181
|
+
if (name === 'crtr') {
|
|
182
|
+
throw usage(`cannot uninstall builtin plugin "crtr" — it ships with the binary`);
|
|
183
|
+
}
|
|
181
184
|
if (!isTTY() && !opts.yes) {
|
|
182
185
|
throw usage(`uninstall requires --yes in non-TTY mode: crtr plugin uninstall ${name} --yes`);
|
|
183
186
|
}
|
|
184
|
-
const scopes = listScopes(opts.scope);
|
|
187
|
+
const scopes = listScopes(opts.scope).filter((s) => s !== 'builtin');
|
|
185
188
|
let removed = false;
|
|
186
189
|
for (const scope of scopes) {
|
|
187
190
|
const pDir = pluginsDir(scope);
|