feat(kyberforge): add plugin-author and marketplace-author skills
## Why Plugin and marketplace management had no governed authoring path. Creating or updating a plugin required knowing the dual-manifest convention, version parity rules, and directory skeleton by memory — nothing enforced consistency or guided the process. `/plugin-author` closes that gap by owning the full plugin scaffold lifecycle: create, update, rename, and release. `/marketplace-author` handles the marketplace-facing side: register, deregister, and update plugin entries in `marketplace.json`. ADR-0016 codifies the version parity convention (identical `version` in both `plugin.json` and `.claude-plugin/plugin.json`) that `/plugin-author` now enforces. The two plugin.json files in this repo are backfilled to comply (keys also sorted to pass the pretty-format-json hook). CONTEXT.md gains glossary entries for "plugin scaffold" and "version parity" so future agents have shared vocabulary for these concepts. ## Implementation Notes `/plugin-author` ships a `scripts/new-plugin.sh` scaffold script that generates the directory skeleton and both manifests in one shot; the skill calls the script rather than generating files ad hoc so the scaffold is reviewable and repeatable. Version parity is an invariant, not a suggestion — the skill will fail loudly on create/update if the two versions would diverge. ADR: docs/adr/0016-plugin-version-parity.md
This commit is contained in:
40
plugins/kyberforge/skills/plugin-author/README.md
Normal file
40
plugins/kyberforge/skills/plugin-author/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# plugin-author
|
||||
|
||||
Creates, updates, and releases plugin scaffolds for the holocron marketplace.
|
||||
|
||||
## What it does
|
||||
|
||||
Manages both manifests (`plugin.json` for Copilot CLI and `.claude-plugin/plugin.json` for Claude Code) in one pass. Three operations: create a new plugin scaffold with placeholder manifests and skeleton dirs; update configuration fields (shared fields updated in both manifests simultaneously); release a version with HITL gate before tagging.
|
||||
|
||||
Out of scope: plugin content (skills, agents, hooks, MCP servers inside those dirs) and `marketplace.json` entries.
|
||||
|
||||
## Before you start
|
||||
|
||||
Have ready: the plugin name (kebab-case) and the repo root path.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/plugin-author
|
||||
```
|
||||
|
||||
**Manual scaffold (human workflow):**
|
||||
```bash
|
||||
bash scripts/new-plugin.sh <plugin-name> <repo-root>
|
||||
|
||||
# Examples:
|
||||
bash scripts/new-plugin.sh my-tools /root/ai-development
|
||||
bash scripts/new-plugin.sh data-tools .
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents |
|
||||
| `scripts/new-plugin.sh` | Scaffolds both manifests and skeleton dirs for a new plugin |
|
||||
| `references/manifest-fields.md` | All optional fields for both manifests beyond the scaffolded defaults |
|
||||
| `references/sources.md` | Research provenance — sources that informed this skill |
|
||||
| `scripts/README.md` | Directory meta-documentation for scripts/ |
|
||||
| `references/README.md` | Directory meta-documentation for references/ |
|
||||
| `tests/README.md` | Test dependency instructions and run command |
|
||||
168
plugins/kyberforge/skills/plugin-author/SKILL.md
Normal file
168
plugins/kyberforge/skills/plugin-author/SKILL.md
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
name: plugin-author
|
||||
description: >
|
||||
Use when the user wants to create a new plugin scaffold ("create a plugin
|
||||
for X", "new plugin called Y"), update plugin configuration ("change the
|
||||
description", "add keyword", "bump version"), or release a plugin version
|
||||
("release", "tag", "publish"). Manages both Claude Code
|
||||
(.claude-plugin/plugin.json) and Copilot CLI (plugin.json) manifests in one
|
||||
pass. Explicitly out of scope: plugin content (skills, agents, hooks, MCP
|
||||
servers) and marketplace.json entries — use /marketplace-author for those.
|
||||
allowed-tools: Bash Read Write Edit
|
||||
metadata:
|
||||
category: factory
|
||||
source_keys:
|
||||
- context7-websites-code-claude
|
||||
- claude-code-plugins-docs
|
||||
- github-cli-plugin-reference
|
||||
- github-plugins-creating
|
||||
- github-plugins-finding-installing
|
||||
---
|
||||
|
||||
## Gotchas
|
||||
|
||||
- Both manifests must carry identical `version` values — version parity is a hard invariant (ADR-0016). Never update version in one manifest without updating the other in the same edit pass.
|
||||
- `author.email` belongs in the Copilot manifest (`plugin.json`); `author.url` belongs in the CC manifest (`.claude-plugin/plugin.json`). Do not swap them.
|
||||
- `claude plugin tag --push` is irreversible: it creates a git tag and pushes it to remote. Always present the HITL gate and wait for explicit confirmation before running it.
|
||||
- `name` in both manifests must be kebab-case and must not use reserved prefixes: `anthropic-*`, `claude-*`, `agent-skills`, `official-claude-plugins`.
|
||||
- Copilot manifest lookup order: `.plugin/plugin.json` → `plugin.json` → `.github/plugin/plugin.json` → `.claude-plugin/plugin.json`. The canonical location for the Copilot manifest in this repo is `plugin.json` at the plugin root.
|
||||
- `displayName` is CC-only — do not add it to the Copilot manifest.
|
||||
- `skills`, `agents`, `hooks`, `mcpServers` are Copilot-only fields — do not add them to the CC manifest.
|
||||
|
||||
## Route
|
||||
|
||||
Determine which flow before touching the filesystem. Read both manifest files if the plugin directory exists.
|
||||
|
||||
- **Plugin directory does not exist** → follow **CREATE flow**
|
||||
- **Plugin directory exists + version/release intent** ("release", "tag", "bump", "publish", "version") → follow **RELEASE flow**
|
||||
- **Plugin directory exists + field change intent** ("update description", "add keyword", "change author") → follow **UPDATE flow**
|
||||
- **Ambiguous** → ask: "Did you mean to create a new plugin, update its configuration, or release a version?"
|
||||
|
||||
Validate runs automatically before tagging (in RELEASE flow) and can be invoked explicitly at any time.
|
||||
|
||||
## CREATE flow
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before touching the filesystem, confirm you have:
|
||||
- [ ] Plugin name (kebab-case, e.g. `my-tools`)
|
||||
- [ ] Repo root (absolute path or `.` for current directory)
|
||||
|
||||
If either is missing, stop and ask before proceeding.
|
||||
|
||||
### Step 1 — Scaffold
|
||||
|
||||
Run the scaffold script:
|
||||
|
||||
```bash
|
||||
bash scripts/new-plugin.sh <name> <repo-root>
|
||||
```
|
||||
|
||||
Examples:
|
||||
```bash
|
||||
bash scripts/new-plugin.sh my-tools /root/ai-development
|
||||
bash scripts/new-plugin.sh data-tools .
|
||||
```
|
||||
|
||||
The script creates under `<repo-root>/plugins/<name>/`:
|
||||
- `plugin.json` — Copilot manifest with `FILL_IN_*` placeholders
|
||||
- `.claude-plugin/plugin.json` — CC manifest with `FILL_IN_*` placeholders
|
||||
- Empty skeleton directories: `skills/`, `agents/`, `hooks/`, `bin/`
|
||||
|
||||
Each file/dir is a no-op if it already exists.
|
||||
|
||||
### Step 2 — Fill in placeholders
|
||||
|
||||
Open both manifest files and replace every `FILL_IN_*` placeholder.
|
||||
|
||||
**Fields shared by both manifests** (must be identical in both):
|
||||
- `name` — kebab-case plugin identifier (already set by script; verify it is correct)
|
||||
- `description` — one or two sentences; what the plugin provides
|
||||
- `version` — SemVer; defaults to `1.0.0`; must be identical in both manifests
|
||||
- `author.name` — author display name
|
||||
- `license` — SPDX identifier (default: `MIT`)
|
||||
- `keywords` — search/discovery tags (default: `[]`)
|
||||
|
||||
**CC-only fields** (`.claude-plugin/plugin.json` only):
|
||||
- `displayName` — human-readable name shown in plugin manager; capitalised form of `name`
|
||||
- `author.url` — author URL (e.g. Gitea profile URL)
|
||||
|
||||
**Copilot-only fields** (`plugin.json` only):
|
||||
- `author.email` — author email
|
||||
- `skills`, `agents`, `hooks`, `mcpServers` — paths; defaults are already set by the script
|
||||
|
||||
### Step 3 — Validate
|
||||
|
||||
Check:
|
||||
- [ ] `name` identical in both manifests, kebab-case, no reserved prefixes
|
||||
- [ ] `description` identical in both manifests, non-empty
|
||||
- [ ] `version` identical in both manifests (version parity — ADR-0016)
|
||||
- [ ] `author.name` identical in both manifests
|
||||
- [ ] `license` identical in both manifests
|
||||
- [ ] No `FILL_IN_*` placeholders remain
|
||||
- [ ] `displayName` present in CC manifest only
|
||||
- [ ] `author.url` in CC manifest, `author.email` in Copilot manifest
|
||||
|
||||
## UPDATE flow
|
||||
|
||||
### Step 1 — Read both manifests
|
||||
|
||||
Read `plugins/<name>/plugin.json` and `plugins/<name>/.claude-plugin/plugin.json`. Identify the current field values.
|
||||
|
||||
### Step 2 — Classify each change
|
||||
|
||||
For every field the user wants to change:
|
||||
|
||||
| Change type | What to update |
|
||||
|---|---|
|
||||
| Shared field (`name`, `description`, `version`, `author.name`, `license`, `keywords`) | Both manifests in the same edit pass |
|
||||
| CC-only (`displayName`, `author.url`) | `.claude-plugin/plugin.json` only |
|
||||
| Copilot-only (`author.email`, `skills`, `agents`, `hooks`, `mcpServers`, `category`, `tags`, `extensions`) | `plugin.json` only |
|
||||
|
||||
Never update a shared field in one manifest without updating the other in the same pass.
|
||||
|
||||
### Step 3 — Announce and apply
|
||||
|
||||
State which fields change and which files are affected. Then apply. For `version` changes not part of a release, bump both manifests in the same edit.
|
||||
|
||||
### Step 4 — Validate
|
||||
|
||||
Re-run the validation checklist from CREATE flow Step 3 on both files.
|
||||
|
||||
## RELEASE flow
|
||||
|
||||
### Step 1 — Confirm version
|
||||
|
||||
If the user has not stated the new SemVer version, ask: "What version are you releasing?" Do not proceed until you have the version.
|
||||
|
||||
### Step 2 — Bump version in both manifests
|
||||
|
||||
Update `version` in both `plugin.json` and `.claude-plugin/plugin.json` in the same edit pass. Confirm they are identical after the edit.
|
||||
|
||||
### Step 3 — Validate
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
claude plugin validate plugins/<name>
|
||||
```
|
||||
|
||||
Stop and report errors if validation fails. Do not proceed to tagging until validation passes.
|
||||
|
||||
### Step 4 — HITL gate
|
||||
|
||||
State exactly:
|
||||
|
||||
> "I will run `claude plugin tag --push` for plugin `<name>`, which will create git tag `<name>--v<version>` and push it to remote. This is irreversible. Confirm?"
|
||||
|
||||
Do not call the tool until the user explicitly confirms in the conversation.
|
||||
|
||||
### Step 5 — Tag and release
|
||||
|
||||
After explicit confirmation, run from the repo root:
|
||||
|
||||
```bash
|
||||
claude plugin tag --push
|
||||
```
|
||||
|
||||
Report the created tag name and confirm the push completed.
|
||||
@@ -0,0 +1,9 @@
|
||||
# references/
|
||||
|
||||
## manifest-fields.md
|
||||
|
||||
Complete field reference for both plugin manifests. Covers all optional fields beyond the scaffolded defaults: field classification (shared / CC-only / Copilot-only), usage examples, and the version parity convention (ADR-0016). Loaded when a user asks to add a non-default field to either manifest.
|
||||
|
||||
## sources.md
|
||||
|
||||
Research provenance record for this skill. Lists the upstream research sources (claude-code-plugins and github-copilot-plugins research docs) that informed SKILL.md, the scaffold script, and the manifest-fields reference. Used by `skill-audit` to validate the provenance chain.
|
||||
@@ -0,0 +1,141 @@
|
||||
---
|
||||
topic: manifest-fields
|
||||
source_keys:
|
||||
- context7-websites-code-claude
|
||||
- claude-code-plugins-docs
|
||||
- github-cli-plugin-reference
|
||||
- github-plugins-creating
|
||||
- github-plugins-finding-installing
|
||||
---
|
||||
|
||||
# Manifest Fields Reference
|
||||
|
||||
This document covers optional fields beyond the scaffolded defaults. Consult it when a user asks to add a non-default field to either manifest.
|
||||
|
||||
## Field Classification
|
||||
|
||||
| Field | Copilot `plugin.json` | CC `.claude-plugin/plugin.json` | Notes |
|
||||
|---|---|---|---|
|
||||
| `name` | Yes (shared) | Yes (shared) | Identical in both; kebab-case; max 64 chars (Copilot) |
|
||||
| `description` | Yes (shared) | Yes (shared) | Identical in both; max 1024 chars (Copilot) |
|
||||
| `version` | Yes (shared) | Yes (shared) | Identical in both; SemVer; version parity required (ADR-0016) |
|
||||
| `author.name` | Yes (shared) | Yes (shared) | Identical in both |
|
||||
| `license` | Yes (shared) | Yes (shared) | Identical in both; SPDX identifier |
|
||||
| `keywords` | Yes (shared) | Yes (shared) | Identical in both; string array |
|
||||
| `displayName` | No | Yes (CC-only) | Human-readable name shown in plugin manager |
|
||||
| `author.url` | No | Yes (CC-only) | Author profile or homepage URL |
|
||||
| `author.email` | Yes (Copilot-only) | No | Author contact email |
|
||||
| `agents` | Yes (Copilot-only) | No | Path or array; default: `agents/` |
|
||||
| `skills` | Yes (Copilot-only) | No | Path or array; default: `skills/` |
|
||||
| `hooks` | Yes (Copilot-only) | No | Path to hooks config |
|
||||
| `mcpServers` | Yes (Copilot-only) | No | Path or object for MCP server config |
|
||||
| `category` | Yes (Copilot-only) | No | Marketplace category string |
|
||||
| `tags` | Yes (Copilot-only) | No | Additional taxonomy tags (distinct from `keywords`) |
|
||||
| `extensions` | Yes (Copilot-only) | No | Path, array, or `{ paths, exclusive: true }` to disable built-ins |
|
||||
| `homepage` | Both (independent) | Both (independent) | Documentation URL; not required to be identical |
|
||||
| `repository` | Both (independent) | Both (independent) | Source repo URL |
|
||||
|
||||
## Non-Default Optional Fields
|
||||
|
||||
### `homepage`
|
||||
|
||||
Documentation or project page URL. Shown in the plugin manager. Independent in each manifest — the two values do not need to match.
|
||||
|
||||
```json
|
||||
// Copilot plugin.json
|
||||
{ "homepage": "https://example.com/docs" }
|
||||
|
||||
// CC .claude-plugin/plugin.json
|
||||
{ "homepage": "https://example.com/docs" }
|
||||
```
|
||||
|
||||
### `repository`
|
||||
|
||||
Source repository URL. Independent in each manifest.
|
||||
|
||||
```json
|
||||
{ "repository": "https://git.example.com/owner/repo" }
|
||||
```
|
||||
|
||||
### `category` (Copilot-only)
|
||||
|
||||
Marketplace browsing category. Single string. Copilot manifest only.
|
||||
|
||||
```json
|
||||
{ "category": "developer-tools" }
|
||||
```
|
||||
|
||||
### `tags` (Copilot-only)
|
||||
|
||||
Additional taxonomy tags for Copilot marketplace browsing. Distinct from `keywords`.
|
||||
|
||||
```json
|
||||
{ "tags": ["testing", "ci"] }
|
||||
```
|
||||
|
||||
### `extensions` (Copilot-only)
|
||||
|
||||
Path to extension files, an array of paths, or an object. Use `{ "paths": [...], "exclusive": true }` to disable built-in extensions.
|
||||
|
||||
```json
|
||||
{ "extensions": "extensions/" }
|
||||
// or
|
||||
{ "extensions": { "paths": ["extensions/"], "exclusive": true } }
|
||||
```
|
||||
|
||||
### `lspServers`
|
||||
|
||||
Language Server Protocol configuration. Supported in both Copilot and CC manifests.
|
||||
|
||||
```json
|
||||
{ "lspServers": ".lsp.json" }
|
||||
```
|
||||
|
||||
### `outputStyles` (CC-only)
|
||||
|
||||
Path to output styles directory. Claude Code manifest only.
|
||||
|
||||
```json
|
||||
{ "outputStyles": "styles/" }
|
||||
```
|
||||
|
||||
### `experimental.themes` (CC-only)
|
||||
|
||||
Path to themes directory. Claude Code manifest only. Experimental — may change.
|
||||
|
||||
```json
|
||||
{ "experimental": { "themes": "themes/" } }
|
||||
```
|
||||
|
||||
### `experimental.monitors` (CC-only)
|
||||
|
||||
Path to `monitors.json`. Claude Code manifest only. Experimental.
|
||||
|
||||
```json
|
||||
{ "experimental": { "monitors": "monitors.json" } }
|
||||
```
|
||||
|
||||
### `dependencies` (CC-only)
|
||||
|
||||
Plugin dependencies. Each entry is a string (plugin name) or `{ "name": "<name>", "version": "<semver>" }`.
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": [
|
||||
"base-tools",
|
||||
{ "name": "data-tools", "version": "^2.0.0" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `commands` (legacy, both)
|
||||
|
||||
Explicit list of `.md` command file paths. Deprecated in favour of `skills/`. Use `skills` instead for new plugins.
|
||||
|
||||
## Version Parity Convention (ADR-0016)
|
||||
|
||||
The `version` field must be present and identical in both manifests at all times. This is a hard invariant enforced by `/plugin-author` on every create, update, and release operation.
|
||||
|
||||
- If only the CC manifest had `version` before this convention was introduced, backfill the Copilot manifest immediately.
|
||||
- Never change `version` in one manifest without changing it in the other in the same edit pass.
|
||||
- The RELEASE flow bumps both manifests simultaneously before tagging.
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
source_keys:
|
||||
- context7-websites-code-claude
|
||||
- claude-code-plugins-docs
|
||||
- github-cli-plugin-reference
|
||||
- github-plugins-creating
|
||||
- github-plugins-finding-installing
|
||||
---
|
||||
|
||||
# Sources
|
||||
|
||||
## context7-websites-code-claude
|
||||
|
||||
- **URL:** context7:/websites/code_claude
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/claude-code-plugins/sources.md
|
||||
- **Description:** Official Claude Code documentation site indexed by Context7 — plugin manifest schema, marketplace JSON format, `claude plugin` CLI commands, validation, tagging
|
||||
- **Contributing files:** SKILL.md, references/manifest-fields.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## claude-code-plugins-docs
|
||||
|
||||
- **URL:** https://code.claude.com/docs/en/plugins
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/claude-code-plugins/sources.md
|
||||
- **Description:** Official Claude Code plugin authoring guide — plugin structure, CC manifest fields (`displayName`, `author.url`, `version`, `outputStyles`, `experimental`), marketplace submission, `claude plugin tag`
|
||||
- **Contributing files:** SKILL.md, references/manifest-fields.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## github-cli-plugin-reference
|
||||
|
||||
- **URL:** https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-plugin-reference
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
||||
- **Description:** Full Copilot CLI plugin reference — `plugin.json` schema (all fields, types, constraints), marketplace.json schema, CLI commands, manifest lookup order
|
||||
- **Contributing files:** SKILL.md, scripts/new-plugin.sh, references/manifest-fields.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## github-plugins-creating
|
||||
|
||||
- **URL:** https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/plugins-creating
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
||||
- **Description:** How-to for creating Copilot CLI plugins — plugin structure, Copilot manifest fields, development lifecycle, hooks format, MCP config
|
||||
- **Contributing files:** SKILL.md, scripts/new-plugin.sh
|
||||
- **Status:** `extracted`
|
||||
|
||||
## github-plugins-finding-installing
|
||||
|
||||
- **URL:** https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/plugins-finding-and-installing
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
||||
- **Description:** Finding and installing Copilot CLI plugins — install spec formats, marketplace registration, `copilot plugin` CLI commands
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Status:** `extracted`
|
||||
11
plugins/kyberforge/skills/plugin-author/scripts/README.md
Normal file
11
plugins/kyberforge/skills/plugin-author/scripts/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# scripts/
|
||||
|
||||
## new-plugin.sh
|
||||
|
||||
Scaffolds a new plugin directory with both manifests and empty skeleton dirs.
|
||||
|
||||
```
|
||||
Usage: new-plugin.sh <plugin-name> <repo-root>
|
||||
```
|
||||
|
||||
Creates `<repo-root>/plugins/<plugin-name>/` containing: `plugin.json` (Copilot manifest), `.claude-plugin/plugin.json` (CC manifest), and empty `skills/`, `agents/`, `hooks/`, `bin/` directories. Both manifest files carry `FILL_IN_*` placeholders for fields the user must supply. Each file and directory is a no-op if it already exists. Does not touch `marketplace.json`. See `--help` for full usage.
|
||||
147
plugins/kyberforge/skills/plugin-author/scripts/new-plugin.sh
Executable file
147
plugins/kyberforge/skills/plugin-author/scripts/new-plugin.sh
Executable file
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: new-plugin.sh <plugin-name> <repo-root>
|
||||
|
||||
Scaffold a new plugin directory with both manifests and skeleton dirs.
|
||||
|
||||
Arguments:
|
||||
plugin-name Kebab-case plugin identifier (e.g. my-tools, data-tools).
|
||||
Must be lowercase letters, numbers, and hyphens only.
|
||||
No leading, trailing, or consecutive hyphens.
|
||||
repo-root Absolute or relative path to the repository root.
|
||||
The plugin is created at <repo-root>/plugins/<plugin-name>/.
|
||||
|
||||
Created structure:
|
||||
<repo-root>/plugins/<plugin-name>/
|
||||
plugin.json Copilot CLI manifest (FILL_IN_* placeholders)
|
||||
.claude-plugin/
|
||||
plugin.json Claude Code manifest (FILL_IN_* placeholders)
|
||||
skills/ Empty skeleton directory
|
||||
agents/ Empty skeleton directory
|
||||
hooks/ Empty skeleton directory
|
||||
bin/ Empty skeleton directory
|
||||
|
||||
Each file and directory is a no-op if it already exists.
|
||||
Does NOT touch marketplace.json.
|
||||
|
||||
Exit codes:
|
||||
0 Files created or already existed (no-op)
|
||||
1 Invalid arguments or missing root
|
||||
EOF
|
||||
}
|
||||
|
||||
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo "Error: plugin-name and repo-root are required." >&2
|
||||
echo "" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PLUGIN_NAME="$1"
|
||||
REPO_ROOT="$2"
|
||||
|
||||
# Validate plugin name format
|
||||
if ! echo "$PLUGIN_NAME" | grep -qE '^[a-z0-9]+(-[a-z0-9]+)*$'; then
|
||||
echo "Error: plugin-name must use lowercase letters, numbers, and hyphens only." >&2
|
||||
echo " No leading, trailing, or consecutive hyphens." >&2
|
||||
echo " Received: '$PLUGIN_NAME'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Expand tilde
|
||||
REPO_ROOT="${REPO_ROOT/#\~/$HOME}"
|
||||
|
||||
# Resolve to absolute path
|
||||
REPO_ROOT="$(cd "$REPO_ROOT" 2>/dev/null && pwd)" || {
|
||||
echo "Error: repo-root directory '$2' does not exist." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
PLUGIN_DIR="$REPO_ROOT/plugins/$PLUGIN_NAME"
|
||||
CC_DIR="$PLUGIN_DIR/.claude-plugin"
|
||||
COPILOT_MANIFEST="$PLUGIN_DIR/plugin.json"
|
||||
CC_MANIFEST="$CC_DIR/plugin.json"
|
||||
|
||||
# Create directory skeleton
|
||||
created_any=false
|
||||
|
||||
create_dir_if_missing() {
|
||||
local dir="$1"
|
||||
if [[ -d "$dir" ]]; then
|
||||
echo "Skipping directory '$dir' — already exists." >&2
|
||||
else
|
||||
mkdir -p "$dir"
|
||||
echo "Created directory: $dir" >&2
|
||||
created_any=true
|
||||
fi
|
||||
}
|
||||
|
||||
create_dir_if_missing "$PLUGIN_DIR"
|
||||
create_dir_if_missing "$CC_DIR"
|
||||
create_dir_if_missing "$PLUGIN_DIR/skills"
|
||||
create_dir_if_missing "$PLUGIN_DIR/agents"
|
||||
create_dir_if_missing "$PLUGIN_DIR/hooks"
|
||||
create_dir_if_missing "$PLUGIN_DIR/bin"
|
||||
|
||||
# Create Copilot manifest (plugin.json)
|
||||
if [[ -f "$COPILOT_MANIFEST" ]]; then
|
||||
echo "Skipping '$COPILOT_MANIFEST' — already exists." >&2
|
||||
else
|
||||
cat > "$COPILOT_MANIFEST" <<COPILOT_JSON
|
||||
{
|
||||
"name": "$PLUGIN_NAME",
|
||||
"description": "FILL_IN_DESCRIPTION",
|
||||
"version": "1.0.0",
|
||||
"author": { "name": "FILL_IN_AUTHOR_NAME", "email": "FILL_IN_AUTHOR_EMAIL" },
|
||||
"license": "MIT",
|
||||
"keywords": [],
|
||||
"agents": "agents/",
|
||||
"skills": ["skills/"],
|
||||
"hooks": "hooks.json",
|
||||
"mcpServers": ".mcp.json"
|
||||
}
|
||||
COPILOT_JSON
|
||||
echo "Created: $COPILOT_MANIFEST" >&2
|
||||
created_any=true
|
||||
fi
|
||||
|
||||
# Create Claude Code manifest (.claude-plugin/plugin.json)
|
||||
if [[ -f "$CC_MANIFEST" ]]; then
|
||||
echo "Skipping '$CC_MANIFEST' — already exists." >&2
|
||||
else
|
||||
cat > "$CC_MANIFEST" <<CC_JSON
|
||||
{
|
||||
"name": "$PLUGIN_NAME",
|
||||
"displayName": "FILL_IN_DISPLAY_NAME",
|
||||
"description": "FILL_IN_DESCRIPTION",
|
||||
"version": "1.0.0",
|
||||
"author": { "name": "FILL_IN_AUTHOR_NAME", "url": "FILL_IN_AUTHOR_URL" },
|
||||
"license": "MIT",
|
||||
"keywords": []
|
||||
}
|
||||
CC_JSON
|
||||
echo "Created: $CC_MANIFEST" >&2
|
||||
created_any=true
|
||||
fi
|
||||
|
||||
if [[ "$created_any" == false ]]; then
|
||||
echo "All files already exist — nothing to do." >&2
|
||||
else
|
||||
echo "" >&2
|
||||
echo "Plugin: $PLUGIN_NAME" >&2
|
||||
echo "Location: $PLUGIN_DIR" >&2
|
||||
echo "" >&2
|
||||
echo "Next steps:" >&2
|
||||
echo " 1. Fill in $COPILOT_MANIFEST — replace all FILL_IN_* placeholders" >&2
|
||||
echo " 2. Fill in $CC_MANIFEST — replace all FILL_IN_* placeholders" >&2
|
||||
echo " 3. Verify version is identical in both manifests (version parity — ADR-0016)" >&2
|
||||
echo " 4. Add plugin content: skills in skills/, agents in agents/, etc." >&2
|
||||
fi
|
||||
33
plugins/kyberforge/skills/plugin-author/tests/README.md
Normal file
33
plugins/kyberforge/skills/plugin-author/tests/README.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# tests/
|
||||
|
||||
Test files for scripts bundled with this skill.
|
||||
|
||||
## When to add tests
|
||||
|
||||
Add tests here when the skill has scripts in `scripts/` that are complex enough
|
||||
to break silently — validators, parsers, generators, anything with branching
|
||||
logic or edge cases. Test infrastructure (`.bats`, `*_test.*`, `test_*.sh`)
|
||||
belongs here, not in `scripts/`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
Tests require [bats-support](https://github.com/bats-core/bats-support) and
|
||||
[bats-assert](https://github.com/bats-core/bats-assert). The test files load
|
||||
helpers from the repo root's `tests/test_helper/`.
|
||||
|
||||
From the repo root:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/bats-core/bats-support tests/test_helper/bats-support
|
||||
git clone https://github.com/bats-core/bats-assert tests/test_helper/bats-assert
|
||||
```
|
||||
|
||||
Run all tests for this skill (from the repo root):
|
||||
|
||||
```bash
|
||||
bats plugins/kyberforge/skills/plugin-author/tests/
|
||||
```
|
||||
|
||||
## If no tests are needed
|
||||
|
||||
Delete this README and the `tests/` directory entirely.
|
||||
Reference in New Issue
Block a user