fix(kyberforge): resolve plugin-create path bug, validate.sh false positives, and write-skill YAML error
- plugin-create/SKILL.md: fix ${CLAUDE_PLUGIN_ROOT} path (create-plugin → plugin-create, 4 occurrences)
- plugin-create/SKILL.md: delegate reserved name validation to new references/reserved-names.md (complete list)
- plugin-create/SKILL.md: add displayName reminder in step 4, full-validation pointer in step 7
- plugin-create/references/reserved-names.md: complete reserved name list extracted from claude-code.md
- plugin-create/references/manifest-fields.md: quick-ref for both plugin.json manifests and marketplace entry
- plugin-create/META.md: update stale when: and references: fields to reflect post-migration paths
- plugin-create/assets/plugin-template/hooks.json: unify empty hooks schema to {} (was [])
- write-skill/SKILL.md: fix YAML frontmatter parse error — wrap description in >- block scalar
- validate.sh: strip backtick spans before ../ check to eliminate documentation false positives
- inventory.sh: same backtick-span fix, applied to both outer check and per-line reporting
- tests/test_scripts.sh: fix SCRIPTS_DIR path to skills/marketplace-architect/scripts/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -62,13 +62,16 @@ while IFS= read -r -d '' path; do
|
||||
|
||||
[[ -n "$asset_type" ]] && ROWS+=("$asset_type|$rel")
|
||||
|
||||
# Check for cross-references in text files
|
||||
# Check for cross-references in text files — skip occurrences inside backtick spans
|
||||
case "$name" in *.md|*.json|*.sh)
|
||||
if grep -q '\.\.\/' "$path" 2>/dev/null; then
|
||||
if grep '\.\.\/' "$path" 2>/dev/null | sed 's/`[^`]*`//g' | grep -q '\.\.\/'; then
|
||||
while IFS= read -r line; do
|
||||
lineno="${line%%:*}"
|
||||
content="${line#*:}"
|
||||
CROSS_REFS+=("$rel:$lineno: $content")
|
||||
stripped=$(echo "$content" | sed 's/`[^`]*`//g')
|
||||
if echo "$stripped" | grep -q '\.\.\/'; then
|
||||
CROSS_REFS+=("$rel:$lineno: $content")
|
||||
fi
|
||||
done < <(grep -n '\.\.\/' "$path" 2>/dev/null | head -20)
|
||||
fi
|
||||
;;
|
||||
|
||||
@@ -182,9 +182,9 @@ validate_plugin_dir() {
|
||||
validate_skill_md "$skill_md"
|
||||
done < <(find "$pd" -name "SKILL.md" -print0 2>/dev/null)
|
||||
|
||||
# Cross-reference check
|
||||
# Cross-reference check — skip occurrences inside backtick spans (documentation text)
|
||||
while IFS= read -r -d '' f; do
|
||||
if grep -q '\.\.\/' "$f" 2>/dev/null; then
|
||||
if grep '\.\.\/' "$f" 2>/dev/null | sed 's/`[^`]*`//g' | grep -q '\.\.\/'; then
|
||||
local rel="${f#"$pd/"}"
|
||||
error "$rel: contains '../' reference — plugins cannot access files outside their directory after caching"
|
||||
fi
|
||||
|
||||
@@ -3,11 +3,11 @@ version: "1.0"
|
||||
updated: 2026-06-20
|
||||
|
||||
when: Invoked when the user wants to create a new plugin in the marketplace. Scaffolds the
|
||||
directory structure from templates/plugin/, substitutes PLUGIN_NAME/PLUGIN_DESCRIPTION/
|
||||
AUTHOR_NAME/AUTHOR_EMAIL/AUTHOR_URL placeholders, writes to plugins/<name>/, registers
|
||||
the plugin in .claude-plugin/marketplace.json, runs claude plugin validate ., and hands
|
||||
off to /marketplace-architect.
|
||||
directory structure from assets/plugin-template/ (bundled inside this skill), substitutes
|
||||
PLUGIN_NAME/PLUGIN_DESCRIPTION/AUTHOR_NAME/AUTHOR_EMAIL/AUTHOR_URL placeholders, writes to
|
||||
plugins/<name>/, registers the plugin in .claude-plugin/marketplace.json, runs
|
||||
claude plugin validate ., and hands off to /marketplace-architect.
|
||||
|
||||
references:
|
||||
- docs/research/plugin-marketplace-architecture.md
|
||||
- "${CLAUDE_PLUGIN_ROOT}/docs/plugin-marketplace-architecture.md"
|
||||
```
|
||||
|
||||
@@ -16,7 +16,7 @@ metadata:
|
||||
|
||||
## Required inputs
|
||||
|
||||
- **Plugin name** — kebab-case slug; ask if not stated. Validate: lowercase letters, digits, hyphens only; not already present in `plugins/` or `.claude-plugin/marketplace.json`; not a reserved name (`anthropic-*`, `claude-*`, `agent-skills`, `official-claude-plugins`).
|
||||
- **Plugin name** — kebab-case slug; ask if not stated. Validate: lowercase letters, digits, hyphens only; not already present in `plugins/` or `.claude-plugin/marketplace.json`; not a reserved name. Load `references/reserved-names.md` for the full reserved list.
|
||||
- **Plugin description** — one sentence; ask if not stated.
|
||||
- **Author name** — ask if not stated.
|
||||
- **Author email** — ask if not stated; used in the Copilot root `plugin.json`.
|
||||
@@ -24,8 +24,8 @@ metadata:
|
||||
|
||||
## Constraints
|
||||
|
||||
- Load the plugin template from `assets/plugin-template/` bundled inside this skill (`${CLAUDE_PLUGIN_ROOT}/skills/create-plugin/assets/plugin-template/`). Stop if the path is missing — do not generate files from memory.
|
||||
- Plugin name must be kebab-case and not a reserved name (`anthropic-*`, `claude-*`, `agent-skills`, `official-claude-plugins`) — halt and ask for a replacement before Gate A if violated.
|
||||
- Load the plugin template from `assets/plugin-template/` bundled inside this skill (`${CLAUDE_PLUGIN_ROOT}/skills/plugin-create/assets/plugin-template/`). Stop if the path is missing — do not generate files from memory.
|
||||
- Plugin name must be kebab-case and not a reserved name — see `references/reserved-names.md` for the full list; halt and ask for a replacement before Gate A if violated.
|
||||
- Do not write any file until Gate A (plan approval) and Gate B (file contents approval) are both explicitly confirmed.
|
||||
- Replace all five placeholder markers — `PLUGIN_NAME`, `PLUGIN_DESCRIPTION`, `AUTHOR_NAME`, `AUTHOR_EMAIL`, `AUTHOR_URL` — in every copied file before Gate B review. No marker may appear in written output.
|
||||
- Do not generate skill content — `skills/` is scaffolded as an empty directory with README only. Direct the user to `/write-skill` to add skills.
|
||||
@@ -39,19 +39,19 @@ metadata:
|
||||
|
||||
## Process
|
||||
|
||||
1. **Collect inputs.** Ask for plugin name, description, author name, author email, and author URL — one question at a time. Validate the plugin name: kebab-case format, not a reserved name, not already present in `plugins/` or `.claude-plugin/marketplace.json`. If any validation fails, stop and ask for a replacement before continuing.
|
||||
1. **Collect inputs.** Ask for plugin name, description, author name, author email, and author URL — one question at a time. Validate the plugin name: kebab-case format, not already present in `plugins/` or `.claude-plugin/marketplace.json`, not a reserved name (load `references/reserved-names.md` to check). If any validation fails, stop and ask for a replacement before continuing.
|
||||
|
||||
2. **Check template.** Load the bundled template from `assets/plugin-template/` inside this skill (`${CLAUDE_PLUGIN_ROOT}/skills/create-plugin/assets/plugin-template/`). If the path is missing, stop and report it — do not proceed or generate files from memory.
|
||||
2. **Check template.** Load the bundled template from `assets/plugin-template/` inside this skill (`${CLAUDE_PLUGIN_ROOT}/skills/plugin-create/assets/plugin-template/`). If the path is missing, stop and report it — do not proceed or generate files from memory.
|
||||
|
||||
3. **Gate A — plan review.** Present: the list of files that will be written (derived from `assets/plugin-template/` with `PLUGIN_NAME` substituted into filenames), the new `plugins/<name>/` directory path, the marketplace entry that will be added to `.claude-plugin/marketplace.json`, and whether `.github/plugin/marketplace.json` will also be updated. Wait for explicit approval — do not proceed on "looks good" or silence.
|
||||
|
||||
4. **Copy and substitute.** Copy `assets/plugin-template/` to `plugins/<name>/`. In every copied file, replace all occurrences of `PLUGIN_NAME`, `PLUGIN_DESCRIPTION`, `AUTHOR_NAME`, `AUTHOR_EMAIL`, and `AUTHOR_URL` with the collected values. Rename any file or directory whose name contains `PLUGIN_NAME`.
|
||||
4. **Copy and substitute.** Copy `assets/plugin-template/` to `plugins/<name>/`. In every copied file, replace all occurrences of `PLUGIN_NAME`, `PLUGIN_DESCRIPTION`, `AUTHOR_NAME`, `AUTHOR_EMAIL`, and `AUTHOR_URL` with the collected values. Rename any file or directory whose name contains `PLUGIN_NAME`. Note: after substitution, `displayName` in `.claude-plugin/plugin.json` will equal the kebab slug — remind the user to update it to a human-readable string (e.g. "My Plugin") before publishing.
|
||||
|
||||
5. **Gate B — file contents review.** Show every file with its full substituted content. Wait for explicit approval — do not write until confirmed.
|
||||
|
||||
6. **Write files.** Write all substituted files to `plugins/<name>/`. Append the new plugin entry to `.claude-plugin/marketplace.json`. If `.github/plugin/marketplace.json` exists, append the same entry there.
|
||||
|
||||
7. **Validate.** Run `claude plugin validate .` from `plugins/<name>/`. Report all output inline — do not suppress warnings. If the command is unavailable, note it and suggest the user run it manually after local install.
|
||||
7. **Validate.** Run `claude plugin validate .` from `plugins/<name>/`. Report all output inline — do not suppress warnings. If the command is unavailable, note it and suggest the user run it manually after local install. For full cross-tool and marketplace validation, direct the user to run `/marketplace-architect validate` after the plugin is installed.
|
||||
|
||||
8. **Hand off.** Print: "Plugin `<name>` created and registered in `marketplace.json`. Fill in skill and agent content, then run `/marketplace-architect` to audit the full marketplace."
|
||||
|
||||
@@ -68,7 +68,7 @@ metadata:
|
||||
|
||||
## Failure handling
|
||||
|
||||
- `assets/plugin-template/` not found at `${CLAUDE_PLUGIN_ROOT}/skills/create-plugin/assets/plugin-template/` — stop, report the path, do not generate from memory.
|
||||
- `assets/plugin-template/` not found at `${CLAUDE_PLUGIN_ROOT}/skills/plugin-create/assets/plugin-template/` — stop, report the path, do not generate from memory.
|
||||
- Plugin name already exists in `plugins/` or `marketplace.json` — stop, report the conflict, ask for a different name.
|
||||
- Reserved name detected — stop, report the name and the reserved list, ask for a replacement before continuing.
|
||||
- `claude plugin validate .` unavailable — report that automated validation was skipped; suggest running it manually with `claude --plugin-dir ./plugins/<name>`.
|
||||
@@ -76,7 +76,7 @@ metadata:
|
||||
## Self-check
|
||||
|
||||
- [ ] Plugin name validated: kebab-case, not reserved, not already present in `plugins/` or `marketplace.json`
|
||||
- [ ] `assets/plugin-template/` verified to exist at `${CLAUDE_PLUGIN_ROOT}/skills/create-plugin/assets/plugin-template/` before any file generation
|
||||
- [ ] `assets/plugin-template/` verified to exist at `${CLAUDE_PLUGIN_ROOT}/skills/plugin-create/assets/plugin-template/` before any file generation
|
||||
- [ ] Gate A presented with file list and marketplace entry — explicit approval received
|
||||
- [ ] All five markers substituted in all files — none appear in written output
|
||||
- [ ] Gate B presented with full substituted file contents — explicit approval received
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"hooks": []
|
||||
"hooks": {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# Manifest Field Reference
|
||||
|
||||
Quick reference for the two plugin manifests generated by this skill.
|
||||
Source of truth: `${CLAUDE_PLUGIN_ROOT}/skills/marketplace-architect/references/claude-code.md`
|
||||
and `copilot-cli.md` in the same directory.
|
||||
|
||||
---
|
||||
|
||||
## Claude Code — `.claude-plugin/plugin.json`
|
||||
|
||||
Only `name` is required. Add other fields only when needed.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-plugin",
|
||||
"displayName": "My Plugin",
|
||||
"description": "What it does",
|
||||
"author": { "name": "Name", "url": "https://..." },
|
||||
"license": "MIT",
|
||||
"keywords": []
|
||||
}
|
||||
```
|
||||
|
||||
- `displayName` — human-readable label shown in the Claude Code UI. Update it to a
|
||||
title-cased string after placeholder substitution — it defaults to the kebab slug.
|
||||
- `version` — omit to use git SHA per commit (recommended). Set only for explicit release gates.
|
||||
- Component paths (`skills/`, `agents/`, etc.) are declared in the marketplace entry, not here.
|
||||
|
||||
---
|
||||
|
||||
## Copilot CLI — `plugin.json` at plugin root
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-plugin",
|
||||
"description": "What it does",
|
||||
"author": { "name": "Name", "email": "you@example.com" },
|
||||
"license": "MIT",
|
||||
"keywords": [],
|
||||
"agents": "agents/",
|
||||
"skills": ["skills/"],
|
||||
"hooks": "hooks.json",
|
||||
"mcpServers": ".mcp.json"
|
||||
}
|
||||
```
|
||||
|
||||
- `author.email` (not `url`) — Copilot uses email; Claude uses url. Both manifests diverge here.
|
||||
- Component paths are declared inline in this manifest (Copilot requires them; Claude ignores them).
|
||||
|
||||
---
|
||||
|
||||
## Marketplace entry (in `.claude-plugin/marketplace.json`)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-plugin",
|
||||
"source": "./plugins/my-plugin",
|
||||
"description": "..."
|
||||
}
|
||||
```
|
||||
|
||||
- `source` must start with `./` for Claude Code compatibility.
|
||||
- Do not set `version` here if it is also set in `plugin.json` — `plugin.json` wins silently.
|
||||
|
||||
---
|
||||
|
||||
## Environment variables (for hooks and MCP configs)
|
||||
|
||||
- `${CLAUDE_PLUGIN_ROOT}` — absolute path to the plugin's installation cache. Use for all
|
||||
in-plugin file references in hooks and `.mcp.json`. Changes on update.
|
||||
- `${CLAUDE_PLUGIN_DATA}` — persistent directory that survives updates. Use for state,
|
||||
caches, and `node_modules`.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Reserved Plugin and Marketplace Names
|
||||
|
||||
These names are blocked for third-party use by the Claude Code marketplace. Reject any
|
||||
plugin or marketplace name that matches an exact entry or a wildcard pattern below.
|
||||
|
||||
## Exact reserved names
|
||||
|
||||
```
|
||||
claude-code-marketplace
|
||||
claude-code-plugins
|
||||
claude-plugins-official
|
||||
claude-plugins-community
|
||||
claude-community
|
||||
anthropic-marketplace
|
||||
anthropic-plugins
|
||||
agent-skills
|
||||
anthropic-agent-skills
|
||||
knowledge-work-plugins
|
||||
life-sciences
|
||||
claude-for-legal
|
||||
claude-for-financial-services
|
||||
financial-services-plugins
|
||||
```
|
||||
|
||||
## Reserved name patterns (prefix match)
|
||||
|
||||
Any name starting with these strings is reserved:
|
||||
|
||||
```
|
||||
anthropic-
|
||||
claude-
|
||||
official-claude
|
||||
anthropic-tools
|
||||
```
|
||||
|
||||
## Kebab-case requirement
|
||||
|
||||
Plugin names must match `^[a-z0-9]+(-[a-z0-9]+)*$` — lowercase letters, digits, and
|
||||
hyphens only. No underscores, spaces, or uppercase. The Claude.ai marketplace sync rejects
|
||||
non-kebab-case names even if the local CLI tolerates them.
|
||||
|
||||
## How to check
|
||||
|
||||
1. Exact match: is the name in the exact reserved list above?
|
||||
2. Pattern match: does the name start with any reserved prefix?
|
||||
3. Format: does the name match the kebab-case regex?
|
||||
|
||||
If any check fails, halt and ask the user for a different name before continuing.
|
||||
@@ -1,6 +1,12 @@
|
||||
---
|
||||
name: write-skill
|
||||
description: Use when the user wants to author a new skill file or convert an existing placeholder to the canonical authoring standard. Triggers: "write a new skill for X", "create a SKILL.md that does Y", "build a skill to handle Z". Do NOT use when fixing or updating an existing well-formed skill (use upgrade-skill), running existing evals (use write-eval), refactoring application code, or writing documentation for non-skill artifacts.
|
||||
description: >-
|
||||
Use when the user wants to author a new skill file or convert an existing
|
||||
placeholder to the canonical authoring standard. Triggers: "write a new skill
|
||||
for X", "create a SKILL.md that does Y", "build a skill to handle Z". Do NOT
|
||||
use when fixing or updating an existing well-formed skill (use upgrade-skill),
|
||||
running existing evals (use write-eval), refactoring application code, or
|
||||
writing documentation for non-skill artifacts.
|
||||
metadata:
|
||||
category: factory
|
||||
model: sonnet
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPTS_DIR="$(cd "$(dirname "$0")/../scripts" && pwd)"
|
||||
SCRIPTS_DIR="$(cd "$(dirname "$0")/../skills/marketplace-architect/scripts" && pwd)"
|
||||
PASS=0; FAIL=0
|
||||
# Use += to avoid ((var++)) returning 0 exit code when var was 0 under set -e
|
||||
|
||||
|
||||
Reference in New Issue
Block a user