diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 93507f82..ad923f3f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,8 +67,8 @@ jobs: if ! git diff --quiet; then echo "Release changes detected, creating PR..." - # Get the new version from the updated files - VERSION=$(node -p "require('./plugins/ralph-loop/.claude-plugin/plugin.json').version") + # Get the new version from marketplace.json (all plugins have the same version) + VERSION=$(node -p "require('./.claude-plugin/marketplace.json').plugins[0].version") # Commit the changes git add . diff --git a/.releaserc.js b/.releaserc.js index 5d266ca4..9d6a2daa 100644 --- a/.releaserc.js +++ b/.releaserc.js @@ -1,6 +1,26 @@ -import { readFileSync, writeFileSync } from 'fs'; +import { readFileSync, writeFileSync, readdirSync, existsSync } from 'fs'; import { resolve } from 'path'; +/** + * Dynamically discover all plugins from the plugins directory. + * A plugin is identified by the presence of .claude-plugin/plugin.json. + */ +function discoverPlugins() { + const pluginsDir = resolve(process.cwd(), 'plugins'); + const plugins = []; + + for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) { + if (entry.isDirectory()) { + const pluginJsonPath = resolve(pluginsDir, entry.name, '.claude-plugin/plugin.json'); + if (existsSync(pluginJsonPath)) { + plugins.push(entry.name); + } + } + } + + return plugins; +} + /** * Custom plugin to update version in plugin.json files * These files don't have a "version" field in standard JSON array format, @@ -9,7 +29,7 @@ import { resolve } from 'path'; function updatePluginJsons() { return { async prepare(_pluginContext, { nextRelease: { version } }) { - const plugins = ['auto-updater', 'git-guard', 'jira', 'me', 'ralph-loop']; + const plugins = discoverPlugins(); for (const plugin of plugins) { const pluginJsonPath = resolve( diff --git a/CLAUDE.md b/CLAUDE.md index af4ab678..b31f4b60 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -48,13 +48,13 @@ git commit -m "type(scope): description" - `fix`: 버그 수정 (patch 버전 증가) - `docs`, `style`, `refactor`, `test`, `build`, `ci`, `chore`: 버전 증가 없음 -**스코프 (scope):** 플러그인 이름 (`ralph-loop`, `git-guard`, `me`, `example-plugin`) +**스코프 (scope):** 플러그인 이름 (`example-plugin`) **예시:** ``` -feat(ralph-loop): add new iteration feature -fix(git-guard): prevent commit bypass -docs(me): update TDD instructions +feat(example-plugin): add new feature +fix(example-plugin): fix bug in component +docs(example-plugin): update documentation ``` #### 자동 릴리스 흐름 @@ -139,16 +139,13 @@ claude-plugins/ ├── .claude-plugin/ │ └── marketplace.json # Marketplace 설정 ├── plugins/ -│ ├── ralph-loop/ # Ralph Wiggum 반복 개발 -│ │ ├── commands/ # Slash commands -│ │ ├── hooks/ # SessionStart, Stop hooks -│ │ ├── scripts/ # Setup & cancel scripts -│ │ └── .claude-plugin/plugin.json -│ ├── git-guard/ # Git 워크플로우 보호 -│ │ ├── commands/ # Slash commands -│ │ ├── hooks/ # Git hooks (pre-commit, pre-push) -│ │ └── .claude-plugin/plugin.json -│ └── me/ # 개인용 개발 워크플로우 자동화 +│ └── example-plugin/ # 예시 플러그인 구조 +│ ├── commands/ # Slash commands +│ ├── agents/ # 자율 전문가 에이전트 +│ ├── skills/ # 컨텍스트 인식 가이드 +│ ├── hooks/ # Git hooks, SessionStart/Stop hooks +│ ├── scripts/ # 유틸리티 스크립트 +│ └── .claude-plugin/plugin.json ├── .github/workflows/ # CI/CD workflows ├── tests/ # BATS 테스트 ├── schemas/ # JSON 스키마 @@ -176,6 +173,8 @@ claude-plugins/ 6. 테스트 실행: `bats tests/` +> **참고:** `.releaserc.js`는 플러그인을 자동으로 발견하므로 수정할 필요가 없습니다. + ### 새 컴포넌트 추가 자세한 내용은 `docs/DEVELOPMENT.md` 참조: diff --git a/README.md b/README.md index 12c1ee16..11dec3f3 100644 --- a/README.md +++ b/README.md @@ -4,25 +4,14 @@ Claude Code plugin collection by baleen, featuring useful tools for AI-assisted ## Available Plugins -### Ralph Loop -Implementation of the Ralph Wiggum technique for iterative, self-referential AI development loops in Claude Code. +Plugins are automatically discovered from the `plugins/` directory. For detailed information about each plugin, see the respective plugin's README.md file. -- **Description**: Continuous self-referential AI loops for interactive iterative development -- **Commands**: `/ralph-loop`, `/cancel-ralph`, `/help` -- **Use Case**: Well-defined tasks requiring iteration and refinement - -### Git Guard -Git workflow protection hooks that prevent commit and PR bypasses. - -- **Description**: Prevents `--no-verify` commit bypass and enforces pre-commit checks -- **Commands**: `/install-git-guard`, `/uninstall-git-guard` -- **Use Case**: Teams requiring strict git workflow compliance - -### My Workflow -Personal Claude Code configuration for development workflow automation. - -- **Description**: TDD, systematic debugging, git workflow, code review, and development automation -- **Use Case**: Personal development environment setup +- **Ralph Loop**: Implementation of the Ralph Wiggum technique for iterative, self-referential AI development loops +- **Git Guard**: Git workflow protection hooks that prevent commit and PR bypasses +- **My Workflow**: Personal development workflow automation (TDD, debugging, git, code review) +- **Jira**: Jira integration for issue tracking and project management +- **Auto Updater**: Automatic plugin updates from marketplace +- **Strategic Compact**: Strategic content compaction and organization tools ## Quick Start @@ -66,21 +55,13 @@ claude claude-plugins/ ├── .claude-plugin/ │ └── marketplace.json # Marketplace configuration (baleen-plugins) -├── plugins/ -│ ├── ralph-loop/ # Ralph Wiggum iterative development -│ │ ├── commands/ # Slash commands -│ │ ├── hooks/ # SessionStart, Stop hooks -│ │ ├── scripts/ # Setup and cancel scripts -│ │ └── tests/ # BATS tests -│ ├── git-guard/ # Git workflow protection -│ │ ├── commands/ # Slash commands -│ │ ├── hooks/ # Git hooks (pre-commit, pre-push) -│ │ └── tests/ # BATS tests -│ └── me/ # Personal workflow automation +├── plugins/ # Plugins (auto-discovered) +│ └── example-plugin/ # Example plugin structure │ ├── commands/ # Slash commands │ ├── agents/ # Autonomous agents │ ├── skills/ # Context-aware skills -│ ├── hooks/ # Session hooks +│ ├── hooks/ # Git hooks, SessionStart/Stop hooks +│ ├── scripts/ # Utility scripts │ └── tests/ # BATS tests ├── .github/workflows/ # CI/CD workflows ├── docs/ # Development and testing documentation diff --git a/plugins/auto-updater/commands/update-all-plugins.md b/plugins/auto-updater/commands/update-all-plugins.md index 2c90902d..2951f1b6 100644 --- a/plugins/auto-updater/commands/update-all-plugins.md +++ b/plugins/auto-updater/commands/update-all-plugins.md @@ -1,34 +1,29 @@ --- name: update-all-plugins -description: Manually check and update all plugins from marketplace +description: Manually install or update all plugins from marketplace invocableByUser: true --- -You are about to run a manual plugin update check. +You are about to run a manual plugin update for all marketplace plugins. -Execute the update checker script and report the results to the user. +Execute the update-all-plugins script and report the results to the user. **Steps:** -1. Run the update checker script: +1. Run the update script: ```bash - "${CLAUDE_PLUGIN_ROOT}/scripts/update-checker.sh" + "${CLAUDE_PLUGIN_ROOT}/scripts/update-all-plugins.sh" ``` 2. The script will: - Download the latest marketplace.json from GitHub - - Compare installed plugin versions with marketplace versions - - Apply updates based on the configured policy (patch/minor/major) - - Update the timestamp file + - Get list of all plugins from marketplace (baleen-plugins) + - Install or update each plugin using `claude plugin install @` + - Report success/failure for each plugin 3. Report to the user: - - Which plugins were updated (if any) - - Which plugins have updates available but were blocked by policy + - How many plugins were successfully installed/updated + - Which plugins failed (if any) - Any errors that occurred -4. Show the current update policy: - ```bash - cat ~/.claude/auto-updater/config.json - ``` - -Note: This command ignores the 6-hour interval check and runs immediately. +Note: This command ignores any previous update interval checks and runs immediately. diff --git a/plugins/auto-updater/scripts/update-all-plugins.sh b/plugins/auto-updater/scripts/update-all-plugins.sh new file mode 100755 index 00000000..c8783f76 --- /dev/null +++ b/plugins/auto-updater/scripts/update-all-plugins.sh @@ -0,0 +1,221 @@ +#!/usr/bin/env bash +# +# update-all-plugins.sh +# Update all plugins from marketplace using claude plugin install +# + +set -o pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Marketplace URL +MARKETPLACE_URL="https://raw.githubusercontent.com/baleen37/claude-plugins/main/.claude-plugin/marketplace.json" + +# Temporary directory +TMP_DIR=$(mktemp -d) +trap 'rm -rf "$TMP_DIR"' EXIT + +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" >&2 +} + +# Download marketplace.json +download_marketplace() { + log_info "Downloading marketplace.json from $MARKETPLACE_URL" + if ! curl -fsSL "$MARKETPLACE_URL" -o "$TMP_DIR/marketplace.json"; then + log_error "Failed to download marketplace.json" + return 1 + fi + log_success "Downloaded marketplace.json" +} + +# Get list of plugins from marketplace (simple grep/sed approach) +get_marketplace_plugins() { + # Extract plugin names from JSON using grep and sed + # Matches: "name": "plugin-name" + grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' "$TMP_DIR/marketplace.json" 2>/dev/null | \ + sed 's/.*"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' || echo "" +} + +# Get marketplace for a plugin (parse and find matching entry) +get_marketplace_for_plugin() { + local plugin_name="$1" + local json_file="$TMP_DIR/marketplace.json" + + # Read the file and find the marketplace for the given plugin name + # This handles JSON by looking for name/marketplace pairs in plugin objects + while IFS= read -r line; do + # Look for "name": "plugin-name" pattern + if echo "$line" | grep -q '"name"[[:space:]]*:[[:space:]]*"'"$plugin_name"'"'; then + # Found the name, now look ahead for marketplace in the next few lines + local remaining=$(tail -n +$((current_line + 1)) "$json_file" | head -n 5) + local marketplace=$(echo "$remaining" | grep -o '"marketplace"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' | head -n 1) + if [[ -n "$marketplace" ]]; then + echo "$marketplace" + return 0 + fi + fi + done < "$json_file" + + echo "" + return 1 +} + +# Alternative: Parse all plugins at once into a simpler format +parse_plugins_to_cache() { + local json_file="$TMP_DIR/marketplace.json" + local cache_file="$TMP_DIR/plugins.cache" + + # Get marketplace name from the JSON (look for the top-level name) + local marketplace=$(grep -E '^\s*"name"' "$json_file" | head -n 1 | sed 's/.*"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/') + + if [[ -z "$marketplace" ]]; then + marketplace="baleen-plugins" + fi + + # Create a simple "name|marketplace" format cache + > "$cache_file" + + # Use sed to extract plugin names only within "plugins": [ ... ] section + # Strategy: Extract content between plugins array brackets, then find plugin names + sed -n ' + # Start capturing when we find "plugins": [ + /"plugins"[[:space:]]*:[[:space:]]*\[/ { + # Start loop + :loop + # Read next line + n + # If we found the closing bracket, we are done + /^\s*\]/ q + # Look for "name": "value" pattern + /"name"[[:space:]]*:[[:space:]]*"[^"]*"/ { + # Extract the plugin name + s/.*"name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1|'"$marketplace"'/ + p + } + # Continue loop + b loop + } + ' "$json_file" > "$cache_file" +} + +# Get marketplace from cache +get_marketplace_from_cache() { + local plugin_name="$1" + local cache_file="$TMP_DIR/plugins.cache" + + local result=$(grep "^${plugin_name}|" "$cache_file" 2>/dev/null | cut -d'|' -f2 | head -n 1) + echo "${result:-}" +} + +# Install/update a plugin +install_plugin() { + local plugin_name="$1" + local marketplace="$2" + + log_info "Installing/updating $plugin_name from $marketplace" + + if claude plugin install "${plugin_name}@${marketplace}" --scope user >/dev/null 2>&1; then + log_success "$plugin_name installed/updated successfully" + return 0 + else + log_warning "Failed to install/update $plugin_name" + return 1 + fi +} + +# Main execution +main() { + echo "==========================================" + echo " Update All Plugins from Marketplace" + echo "==========================================" + echo "" + + # Download marketplace.json + if ! download_marketplace; then + log_error "Cannot proceed without marketplace data" + exit 1 + fi + + # Parse plugins into cache file + log_info "Parsing marketplace data" + if ! parse_plugins_to_cache; then + log_error "Failed to parse marketplace data" + exit 1 + fi + + # Get list of plugins from cache + marketplace_plugins=$(cut -d'|' -f1 "$TMP_DIR/plugins.cache") + + if [[ -z "$marketplace_plugins" ]]; then + log_error "No plugins found in marketplace.json" + exit 1 + fi + + # Count plugins + plugin_count=$(echo "$marketplace_plugins" | wc -l | tr -d ' ') + log_info "Found $plugin_count plugins in marketplace" + echo "" + + # Track results + success_count=0 + failed_count=0 + failed_plugins=() + + # Install/update each plugin + while IFS= read -r plugin_name; do + [[ -z "$plugin_name" ]] && continue + + marketplace=$(get_marketplace_from_cache "$plugin_name") + + if [[ -z "$marketplace" ]]; then + log_warning "No marketplace found for $plugin_name, skipping" + continue + fi + + if install_plugin "$plugin_name" "$marketplace"; then + ((success_count++)) + else + ((failed_count++)) + failed_plugins+=("$plugin_name") + fi + done <<< "$marketplace_plugins" + + echo "" + echo "==========================================" + echo " Summary" + echo "==========================================" + echo "" + log_success "Successfully installed/updated: $success_count plugins" + + if [[ $failed_count -gt 0 ]]; then + log_warning "Failed to install/update: $failed_count plugins" + echo "" + log_info "Failed plugins:" + for plugin in "${failed_plugins[@]}"; do + echo " - $plugin" + done + fi + + echo "" + log_success "Done!" +} + +main "$@"