#!/usr/bin/env bash set -euo pipefail usage() { cat < 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 /plugins//. Created structure: /plugins// 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" <&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" <&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