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:
2026-06-20 18:41:29 +00:00
parent 0c92e32038
commit 2b649782a2
9 changed files with 151 additions and 22 deletions

View File

@@ -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
;;

View File

@@ -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

View File

@@ -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"
```

View File

@@ -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

View File

@@ -1,3 +1,3 @@
{
"hooks": []
"hooks": {}
}

View File

@@ -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`.

View File

@@ -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.

View File

@@ -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

View File

@@ -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