fix(scripts): MCP server mapping and CLAUDE.md section markers

Issue 1 - MCP Server Mapping:
- Add mcp_servers field to plugin.json for plugins using shared MCP servers
- projman/pr-review now install gitea MCP server
- cmdb-assistant now installs netbox MCP server
- Scripts read MCP server names from plugin.json

Issue 2 - CLAUDE.md Section Markers:
- Install wraps content with HTML comment markers for precise removal
- Uninstall uses markers first, falls back to legacy header detection
- Fixes code block false positives during uninstall

Bug fix:
- Change ((servers_added++)) to ((++servers_added)) to avoid exit code 1
  with set -e when incrementing from 0

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-02 19:33:45 -05:00
parent dc113d8b09
commit eafcfe5bd1
10 changed files with 297 additions and 94 deletions

View File

@@ -34,6 +34,30 @@ New scripts for installing marketplace plugins into consumer projects:
**Documentation:** `docs/CONFIGURATION.md` updated with "Installing Plugins to Consumer Projects" section. **Documentation:** `docs/CONFIGURATION.md` updated with "Installing Plugins to Consumer Projects" section.
### Fixed
#### Plugin Installation Scripts — MCP Mapping & Section Markers
**MCP Server Mapping:**
- Added `mcp_servers` field to plugin.json for plugins that use shared MCP servers
- `projman` and `pr-review` now correctly install `gitea` MCP server
- `cmdb-assistant` now correctly installs `netbox` MCP server
- Scripts read MCP server names from plugin.json instead of assuming plugin name = server name
**CLAUDE.md Section Markers:**
- Install script now wraps integration content with HTML comment markers:
`<!-- BEGIN marketplace-plugin: {name} -->` and `<!-- END marketplace-plugin: {name} -->`
- Uninstall script uses markers for precise section removal (no more code block false positives)
- Backward compatible: falls back to legacy header detection for pre-marker installations
**Plugins updated with `mcp_servers` field:**
- `projman``["gitea"]`
- `pr-review``["gitea"]`
- `cmdb-assistant``["netbox"]`
- `data-platform``["data-platform"]`
- `viz-platform``["viz-platform"]`
- `contract-validator``["contract-validator"]`
--- ---
## [5.8.0] - 2026-02-02 ## [5.8.0] - 2026-02-02

View File

@@ -19,5 +19,6 @@
"data-quality", "data-quality",
"validation" "validation"
], ],
"mcp_servers": ["netbox"],
"commands": ["./commands/"] "commands": ["./commands/"]
} }

View File

@@ -17,5 +17,6 @@
"interfaces", "interfaces",
"cross-plugin" "cross-plugin"
], ],
"mcp_servers": ["contract-validator"],
"commands": ["./commands/"] "commands": ["./commands/"]
} }

View File

@@ -18,5 +18,6 @@
"etl", "etl",
"dataframe" "dataframe"
], ],
"mcp_servers": ["data-platform"],
"commands": ["./commands/"] "commands": ["./commands/"]
} }

View File

@@ -16,5 +16,6 @@
"performance", "performance",
"multi-agent" "multi-agent"
], ],
"mcp_servers": ["gitea"],
"commands": ["./commands/"] "commands": ["./commands/"]
} }

View File

@@ -16,5 +16,6 @@
"agile", "agile",
"lessons-learned" "lessons-learned"
], ],
"mcp_servers": ["gitea"],
"commands": ["./commands/"] "commands": ["./commands/"]
} }

View File

@@ -19,5 +19,6 @@
"visualization", "visualization",
"dmc" "dmc"
], ],
"mcp_servers": ["viz-platform"],
"commands": ["./commands/"] "commands": ["./commands/"]
} }

View File

@@ -7,7 +7,7 @@
# #
# This script: # This script:
# 1. Validates plugin exists in the marketplace # 1. Validates plugin exists in the marketplace
# 2. Updates target project's .mcp.json with MCP server entry (if applicable) # 2. Updates target project's .mcp.json with MCP server entries (if applicable)
# 3. Appends CLAUDE.md integration snippet to target project # 3. Appends CLAUDE.md integration snippet to target project
# 4. Is idempotent (safe to run multiple times) # 4. Is idempotent (safe to run multiple times)
# #
@@ -39,6 +39,7 @@ log_warning() { echo -e "${YELLOW}[WARN]${NC} $1"; }
# --- Track Changes --- # --- Track Changes ---
CHANGES_MADE=() CHANGES_MADE=()
SKIPPED=() SKIPPED=()
MCP_SERVERS_INSTALLED=()
# --- Usage --- # --- Usage ---
usage() { usage() {
@@ -114,15 +115,28 @@ validate_target() {
fi fi
} }
# --- Check if MCP Server Exists for Plugin --- # --- Get MCP Servers for Plugin ---
has_mcp_server() { # Reads the mcp_servers array from plugin.json
# Returns newline-separated list of MCP server names, or empty if none
get_mcp_servers() {
local plugin_name="$1" local plugin_name="$1"
local mcp_dir="$REPO_ROOT/mcp-servers/$plugin_name" local plugin_json="$REPO_ROOT/plugins/$plugin_name/.claude-plugin/plugin.json"
if [[ -d "$mcp_dir" && -f "$mcp_dir/run.sh" ]]; then if [[ ! -f "$plugin_json" ]]; then
return 0 return
fi fi
return 1
# Read mcp_servers array from plugin.json
# Returns empty if field doesn't exist or is empty
jq -r '.mcp_servers // [] | .[]' "$plugin_json" 2>/dev/null || true
}
# --- Check if plugin has any MCP servers ---
has_mcp_servers() {
local plugin_name="$1"
local servers
servers=$(get_mcp_servers "$plugin_name")
[[ -n "$servers" ]]
} }
# --- Update .mcp.json --- # --- Update .mcp.json ---
@@ -130,12 +144,14 @@ update_mcp_json() {
local plugin_name="$1" local plugin_name="$1"
local target_path="$2" local target_path="$2"
local mcp_json="$target_path/.mcp.json" local mcp_json="$target_path/.mcp.json"
local mcp_server_path="$REPO_ROOT/mcp-servers/$plugin_name/run.sh"
# Check if plugin has MCP server # Get MCP servers for this plugin
if ! has_mcp_server "$plugin_name"; then local mcp_servers
log_skip "Plugin '$plugin_name' has no MCP server - skipping .mcp.json update" mcp_servers=$(get_mcp_servers "$plugin_name")
SKIPPED+=(".mcp.json: No MCP server for $plugin_name")
if [[ -z "$mcp_servers" ]]; then
log_skip "Plugin '$plugin_name' has no MCP servers - skipping .mcp.json update"
SKIPPED+=(".mcp.json: No MCP servers for $plugin_name")
return 0 return 0
fi fi
@@ -146,21 +162,37 @@ update_mcp_json() {
CHANGES_MADE+=("Created .mcp.json") CHANGES_MADE+=("Created .mcp.json")
fi fi
# Add each MCP server
local servers_added=0
while IFS= read -r server_name; do
[[ -z "$server_name" ]] && continue
local mcp_server_path="$REPO_ROOT/mcp-servers/$server_name/run.sh"
# Verify server exists
if [[ ! -f "$mcp_server_path" ]]; then
log_warning "MCP server '$server_name' not found at $mcp_server_path"
continue
fi
# Check if entry already exists # Check if entry already exists
if jq -e ".mcpServers[\"$plugin_name\"]" "$mcp_json" > /dev/null 2>&1; then if jq -e ".mcpServers[\"$server_name\"]" "$mcp_json" > /dev/null 2>&1; then
log_skip "MCP server '$plugin_name' already in .mcp.json" log_skip "MCP server '$server_name' already in .mcp.json"
SKIPPED+=(".mcp.json: $plugin_name already present") SKIPPED+=(".mcp.json: $server_name already present")
return 0 continue
fi fi
# Add MCP server entry # Add MCP server entry
log_info "Adding MCP server '$plugin_name' to .mcp.json" log_info "Adding MCP server '$server_name' to .mcp.json"
local tmp_file=$(mktemp) local tmp_file=$(mktemp)
jq ".mcpServers[\"$plugin_name\"] = {\"command\": \"$mcp_server_path\", \"args\": []}" "$mcp_json" > "$tmp_file" jq ".mcpServers[\"$server_name\"] = {\"command\": \"$mcp_server_path\", \"args\": []}" "$mcp_json" > "$tmp_file"
mv "$tmp_file" "$mcp_json" mv "$tmp_file" "$mcp_json"
CHANGES_MADE+=("Added $plugin_name to .mcp.json") CHANGES_MADE+=("Added $server_name to .mcp.json")
log_success "Added MCP server entry for '$plugin_name'" MCP_SERVERS_INSTALLED+=("$server_name")
log_success "Added MCP server entry for '$server_name'"
((++servers_added))
done <<< "$mcp_servers"
} }
# --- Update CLAUDE.md --- # --- Update CLAUDE.md ---
@@ -189,22 +221,25 @@ EOF
CHANGES_MADE+=("Created CLAUDE.md") CHANGES_MADE+=("Created CLAUDE.md")
fi fi
# Read integration content # Check if already integrated using HTML comment marker (preferred)
local integration_content local begin_marker="<!-- BEGIN marketplace-plugin: $plugin_name -->"
integration_content=$(cat "$integration_file") if grep -qF "$begin_marker" "$target_claude_md" 2>/dev/null; then
# Extract the first line (# header) to use as marker for detection
local header_marker
header_marker=$(head -1 "$integration_file")
# Check if already integrated - look for the integration file's header
# Handles both formats: "# {name} Plugin - CLAUDE.md Integration" and "# {name} CLAUDE.md Integration"
if grep -qE "^# ${plugin_name}( Plugin)? -? ?CLAUDE\.md Integration" "$target_claude_md" 2>/dev/null; then
log_skip "Plugin '$plugin_name' integration already in CLAUDE.md" log_skip "Plugin '$plugin_name' integration already in CLAUDE.md"
SKIPPED+=("CLAUDE.md: $plugin_name already present") SKIPPED+=("CLAUDE.md: $plugin_name already present")
return 0 return 0
fi fi
# Fallback: check for legacy header format (backward compatibility)
if grep -qE "^# ${plugin_name}( Plugin)? -? ?CLAUDE\.md Integration" "$target_claude_md" 2>/dev/null; then
log_skip "Plugin '$plugin_name' integration already in CLAUDE.md (legacy format)"
SKIPPED+=("CLAUDE.md: $plugin_name already present")
return 0
fi
# Read integration content
local integration_content
integration_content=$(cat "$integration_file")
# Check for or create Marketplace Plugin Integration section # Check for or create Marketplace Plugin Integration section
local section_header="## Marketplace Plugin Integration" local section_header="## Marketplace Plugin Integration"
@@ -217,12 +252,18 @@ EOF
echo "" >> "$target_claude_md" echo "" >> "$target_claude_md"
fi fi
# Append integration content # Append integration content with HTML comment markers
log_info "Adding '$plugin_name' integration to CLAUDE.md" log_info "Adding '$plugin_name' integration to CLAUDE.md"
local end_marker="<!-- END marketplace-plugin: $plugin_name -->"
echo "" >> "$target_claude_md" echo "" >> "$target_claude_md"
echo "---" >> "$target_claude_md" echo "---" >> "$target_claude_md"
echo "" >> "$target_claude_md" echo "" >> "$target_claude_md"
echo "$begin_marker" >> "$target_claude_md"
echo "" >> "$target_claude_md"
echo "$integration_content" >> "$target_claude_md" echo "$integration_content" >> "$target_claude_md"
echo "" >> "$target_claude_md"
echo "$end_marker" >> "$target_claude_md"
CHANGES_MADE+=("Added $plugin_name integration to CLAUDE.md") CHANGES_MADE+=("Added $plugin_name integration to CLAUDE.md")
log_success "Added CLAUDE.md integration for '$plugin_name'" log_success "Added CLAUDE.md integration for '$plugin_name'"
@@ -287,8 +328,14 @@ print_summary() {
fi fi
echo "" echo ""
# MCP tools reminder # MCP servers info
if has_mcp_server "$plugin_name"; then if [[ ${#MCP_SERVERS_INSTALLED[@]} -gt 0 ]]; then
echo -e "${CYAN}MCP Servers Installed:${NC}"
for server in "${MCP_SERVERS_INSTALLED[@]}"; do
echo " - $server"
done
echo ""
elif has_mcp_servers "$plugin_name"; then
echo -e "${CYAN}MCP Tools:${NC}" echo -e "${CYAN}MCP Tools:${NC}"
echo " This plugin includes MCP server tools. Use ToolSearch to discover them." echo " This plugin includes MCP server tools. Use ToolSearch to discover them."
echo "" echo ""

View File

@@ -67,13 +67,25 @@ get_available_plugins() {
done done
} }
# --- Get Plugins with MCP Servers --- # --- Get MCP Servers for Plugin ---
get_mcp_plugins() { # Reads the mcp_servers array from plugin.json
for dir in "$REPO_ROOT"/mcp-servers/*/; do get_mcp_servers() {
if [[ -d "$dir" && -f "$dir/run.sh" ]]; then local plugin_name="$1"
basename "$dir" local plugin_json="$REPO_ROOT/plugins/$plugin_name/.claude-plugin/plugin.json"
if [[ ! -f "$plugin_json" ]]; then
return
fi fi
done
jq -r '.mcp_servers // [] | .[]' "$plugin_json" 2>/dev/null || true
}
# --- Check if plugin has any MCP servers defined ---
has_mcp_servers() {
local plugin_name="$1"
local servers
servers=$(get_mcp_servers "$plugin_name")
[[ -n "$servers" ]]
} }
# --- Check MCP Installation --- # --- Check MCP Installation ---
@@ -86,18 +98,28 @@ check_mcp_installed() {
return 1 return 1
fi fi
# Check if this plugin's MCP server is referenced # Get MCP servers for this plugin from plugin.json
if jq -e ".mcpServers[\"$plugin_name\"]" "$mcp_json" > /dev/null 2>&1; then local mcp_servers
mcp_servers=$(get_mcp_servers "$plugin_name")
if [[ -z "$mcp_servers" ]]; then
# Plugin has no MCP servers defined, so MCP check passes
return 0 return 0
fi fi
# Check if ALL required MCP servers are present
while IFS= read -r server_name; do
[[ -z "$server_name" ]] && continue
if ! jq -e ".mcpServers[\"$server_name\"]" "$mcp_json" > /dev/null 2>&1; then
# Also check if any entry points to this marketplace's mcp-servers # Also check if any entry points to this marketplace's mcp-servers
if grep -q "leo-claude-mktplace.*mcp-servers/$plugin_name" "$mcp_json" 2>/dev/null || \ if ! grep -q "mcp-servers/$server_name" "$mcp_json" 2>/dev/null; then
grep -q "claude-plugins-work.*mcp-servers/$plugin_name" "$mcp_json" 2>/dev/null; then
return 0
fi
return 1 return 1
fi
fi
done <<< "$mcp_servers"
return 0
} }
# --- Check CLAUDE.md Integration --- # --- Check CLAUDE.md Integration ---
@@ -110,11 +132,13 @@ check_claude_md_installed() {
return 1 return 1
fi fi
# Look for plugin-specific headers that install-plugin.sh adds # Check for HTML comment marker (preferred, new format)
# Formats vary by plugin: local begin_marker="<!-- BEGIN marketplace-plugin: $plugin_name -->"
# "# {plugin-name} Plugin - CLAUDE.md Integration" if grep -qF "$begin_marker" "$target_claude_md" 2>/dev/null; then
# "# {plugin-name} CLAUDE.md Integration" return 0
# Use regex to match both patterns fi
# Fallback: check for legacy header format
if grep -qE "^# ${plugin_name}( Plugin)? -? ?CLAUDE\.md Integration" "$target_claude_md" 2>/dev/null; then if grep -qE "^# ${plugin_name}( Plugin)? -? ?CLAUDE\.md Integration" "$target_claude_md" 2>/dev/null; then
return 0 return 0
fi fi
@@ -185,15 +209,15 @@ NOT_INSTALLED=()
for plugin in $(get_available_plugins); do for plugin in $(get_available_plugins); do
mcp_installed=false mcp_installed=false
claude_installed=false claude_installed=false
has_mcp_server=false needs_mcp=false
# Check if plugin has MCP server # Check if plugin has MCP servers defined
if [[ -d "$REPO_ROOT/mcp-servers/$plugin" ]]; then if has_mcp_servers "$plugin"; then
has_mcp_server=true needs_mcp=true
fi fi
# Check MCP installation (only if plugin has MCP server) # Check MCP installation
if $has_mcp_server && check_mcp_installed "$plugin" "$TARGET_PATH"; then if check_mcp_installed "$plugin" "$TARGET_PATH"; then
mcp_installed=true mcp_installed=true
INSTALLED_MCP[$plugin]=true INSTALLED_MCP[$plugin]=true
fi fi
@@ -205,19 +229,20 @@ for plugin in $(get_available_plugins); do
fi fi
# Categorize # Categorize
if $mcp_installed || $claude_installed; then if $claude_installed; then
if $has_mcp_server; then if $needs_mcp; then
if $mcp_installed && $claude_installed; then if $mcp_installed; then
INSTALLED_PLUGINS+=("$plugin") INSTALLED_PLUGINS+=("$plugin")
else else
PARTIAL_PLUGINS+=("$plugin") PARTIAL_PLUGINS+=("$plugin")
fi fi
else else
# Plugins without MCP servers just need CLAUDE.md # Plugins without MCP servers just need CLAUDE.md
if $claude_installed; then
INSTALLED_PLUGINS+=("$plugin") INSTALLED_PLUGINS+=("$plugin")
fi fi
fi elif $mcp_installed && $needs_mcp; then
# Has MCP but missing CLAUDE.md
PARTIAL_PLUGINS+=("$plugin")
else else
NOT_INSTALLED+=("$plugin") NOT_INSTALLED+=("$plugin")
fi fi
@@ -247,7 +272,11 @@ if [[ ${#PARTIAL_PLUGINS[@]} -gt 0 ]]; then
if [[ -v INSTALLED_MCP[$plugin] ]]; then if [[ -v INSTALLED_MCP[$plugin] ]]; then
echo " ✓ MCP server configured in .mcp.json" echo " ✓ MCP server configured in .mcp.json"
else else
echo " ✗ MCP server NOT in .mcp.json" # Show which MCP servers are missing
mcp_servers=$(get_mcp_servers "$plugin")
if [[ -n "$mcp_servers" ]]; then
echo " ✗ MCP server(s) NOT in .mcp.json: $mcp_servers"
fi
fi fi
if [[ -v INSTALLED_CLAUDE_MD[$plugin] ]]; then if [[ -v INSTALLED_CLAUDE_MD[$plugin] ]]; then
echo " ✓ Integration in CLAUDE.md" echo " ✓ Integration in CLAUDE.md"

View File

@@ -6,7 +6,7 @@
# Usage: ./scripts/uninstall-plugin.sh <plugin-name> <target-project-path> # Usage: ./scripts/uninstall-plugin.sh <plugin-name> <target-project-path>
# #
# This script: # This script:
# 1. Removes MCP server entry from target project's .mcp.json # 1. Removes MCP server entries from target project's .mcp.json
# 2. Removes CLAUDE.md integration section for the plugin # 2. Removes CLAUDE.md integration section for the plugin
# 3. Is idempotent (safe to run multiple times) # 3. Is idempotent (safe to run multiple times)
# #
@@ -76,6 +76,22 @@ validate_target() {
log_success "Target project found: $target_path" log_success "Target project found: $target_path"
} }
# --- Get MCP Servers for Plugin ---
# Reads the mcp_servers array from plugin.json
# Returns newline-separated list of MCP server names, or empty if none
get_mcp_servers() {
local plugin_name="$1"
local plugin_json="$REPO_ROOT/plugins/$plugin_name/.claude-plugin/plugin.json"
if [[ ! -f "$plugin_json" ]]; then
return
fi
# Read mcp_servers array from plugin.json
# Returns empty if field doesn't exist or is empty
jq -r '.mcp_servers // [] | .[]' "$plugin_json" 2>/dev/null || true
}
# --- Remove from .mcp.json --- # --- Remove from .mcp.json ---
remove_from_mcp_json() { remove_from_mcp_json() {
local plugin_name="$1" local plugin_name="$1"
@@ -89,21 +105,48 @@ remove_from_mcp_json() {
return 0 return 0
fi fi
# Check if entry exists # Get MCP servers for this plugin
if ! jq -e ".mcpServers[\"$plugin_name\"]" "$mcp_json" > /dev/null 2>&1; then local mcp_servers
log_skip "MCP server '$plugin_name' not in .mcp.json" mcp_servers=$(get_mcp_servers "$plugin_name")
SKIPPED+=(".mcp.json: $plugin_name not present")
return 0
fi
# Remove MCP server entry if [[ -z "$mcp_servers" ]]; then
# Fallback: try to remove entry with plugin name (backward compatibility)
if jq -e ".mcpServers[\"$plugin_name\"]" "$mcp_json" > /dev/null 2>&1; then
log_info "Removing MCP server '$plugin_name' from .mcp.json" log_info "Removing MCP server '$plugin_name' from .mcp.json"
local tmp_file=$(mktemp) local tmp_file=$(mktemp)
jq "del(.mcpServers[\"$plugin_name\"])" "$mcp_json" > "$tmp_file" jq "del(.mcpServers[\"$plugin_name\"])" "$mcp_json" > "$tmp_file"
mv "$tmp_file" "$mcp_json" mv "$tmp_file" "$mcp_json"
CHANGES_MADE+=("Removed $plugin_name from .mcp.json") CHANGES_MADE+=("Removed $plugin_name from .mcp.json")
log_success "Removed MCP server entry for '$plugin_name'" log_success "Removed MCP server entry for '$plugin_name'"
else
log_skip "Plugin '$plugin_name' has no MCP servers configured"
SKIPPED+=(".mcp.json: No MCP servers for $plugin_name")
fi
return 0
fi
# Remove each MCP server
local servers_removed=0
while IFS= read -r server_name; do
[[ -z "$server_name" ]] && continue
# Check if entry exists
if ! jq -e ".mcpServers[\"$server_name\"]" "$mcp_json" > /dev/null 2>&1; then
log_skip "MCP server '$server_name' not in .mcp.json"
SKIPPED+=(".mcp.json: $server_name not present")
continue
fi
# Remove MCP server entry
log_info "Removing MCP server '$server_name' from .mcp.json"
local tmp_file=$(mktemp)
jq "del(.mcpServers[\"$server_name\"])" "$mcp_json" > "$tmp_file"
mv "$tmp_file" "$mcp_json"
CHANGES_MADE+=("Removed $server_name from .mcp.json")
log_success "Removed MCP server entry for '$server_name'"
((++servers_removed))
done <<< "$mcp_servers"
} }
# --- Remove from CLAUDE.md --- # --- Remove from CLAUDE.md ---
@@ -119,8 +162,64 @@ remove_from_claude_md() {
return 0 return 0
fi fi
# Look for the plugin section header (handles multiple formats) # Try HTML comment markers first (preferred method)
# Formats: "# {plugin-name} Plugin - CLAUDE.md Integration" or "# {plugin-name} CLAUDE.md Integration" local begin_marker="<!-- BEGIN marketplace-plugin: $plugin_name -->"
local end_marker="<!-- END marketplace-plugin: $plugin_name -->"
if grep -qF "$begin_marker" "$target_claude_md" 2>/dev/null; then
log_info "Removing '$plugin_name' section from CLAUDE.md (using markers)"
# Remove everything between markers (inclusive) and preceding ---
local tmp_file=$(mktemp)
awk -v begin="$begin_marker" -v end="$end_marker" '
BEGIN { skip = 0; prev_hr = 0; buffer = "" }
{
is_hr = /^---[[:space:]]*$/
if ($0 == begin) {
skip = 1
# If previous line was ---, dont print it
if (prev_hr) {
buffer = ""
}
next
}
if (skip) {
if ($0 == end) {
skip = 0
}
next
}
# Print buffered content
if (buffer != "") {
print buffer
}
# Buffer current line (in case its --- before a marker)
buffer = $0
prev_hr = is_hr
}
END {
# Print final buffered content
if (buffer != "") {
print buffer
}
}
' "$target_claude_md" > "$tmp_file"
# Clean up multiple consecutive blank lines
awk 'NF{blank=0} !NF{blank++} blank<=2' "$tmp_file" > "${tmp_file}.clean"
mv "${tmp_file}.clean" "$target_claude_md"
rm -f "$tmp_file"
CHANGES_MADE+=("Removed $plugin_name section from CLAUDE.md")
log_success "Removed CLAUDE.md section for '$plugin_name'"
return 0
fi
# Fallback: try legacy header-based detection
local section_header local section_header
section_header=$(grep -E "^# ${plugin_name}( Plugin)? -? ?CLAUDE\.md Integration" "$target_claude_md" 2>/dev/null | head -1) section_header=$(grep -E "^# ${plugin_name}( Plugin)? -? ?CLAUDE\.md Integration" "$target_claude_md" 2>/dev/null | head -1)
@@ -130,10 +229,9 @@ remove_from_claude_md() {
return 0 return 0
fi fi
log_info "Removing '$plugin_name' section from CLAUDE.md" log_info "Removing '$plugin_name' section from CLAUDE.md (legacy format)"
# Create temp file and use awk to remove section # Create temp file and use awk to remove section
# Remove from header to next "---" divider or next plugin header
local tmp_file=$(mktemp) local tmp_file=$(mktemp)
awk -v header="$section_header" ' awk -v header="$section_header" '
@@ -155,25 +253,24 @@ remove_from_claude_md() {
is_hr = /^---[[:space:]]*$/ && !in_code_block is_hr = /^---[[:space:]]*$/ && !in_code_block
# Check if this is a new plugin section header (only outside code blocks) # Check if this is a new plugin section header (only outside code blocks)
# Patterns: "# {name} Plugin - CLAUDE.md Integration" or "# {name} CLAUDE.md Integration"
is_new_plugin_section = /^# [a-z-]+( Plugin)? -? ?CLAUDE\.md Integration/ && !in_code_block && $0 != header is_new_plugin_section = /^# [a-z-]+( Plugin)? -? ?CLAUDE\.md Integration/ && !in_code_block && $0 != header
# Check for HTML marker (new format)
is_begin_marker = /^<!-- BEGIN marketplace-plugin:/ && !in_code_block
if (skip) { if (skip) {
# Stop skipping when we hit --- (outside code block) or a new plugin section # Stop skipping when we hit --- or a new section
if (is_hr) { if (is_hr) {
# This --- ends the section we are removing, skip it too
skip = 0 skip = 0
next next
} }
if (is_new_plugin_section) { if (is_new_plugin_section || is_begin_marker) {
# New plugin section starts, stop skipping and print it
skip = 0 skip = 0
print print
} }
next next
} }
# Not skipping - print the line
print print
} }
END { if (!found) exit 1 } END { if (!found) exit 1 }