From f8811fab373be46db84f43e9750fd1e8a2d1128d Mon Sep 17 00:00:00 2001 From: bastien Date: Fri, 3 Apr 2026 18:08:21 +0200 Subject: [PATCH] opus version correction --- .gitignore | 19 + CHANGELOG.md | 31 ++ CLAUDE.md | 5 + Makefile | 16 + README.md | 655 ++++++++++++++++------------------- agents/git-workflow.md | 473 ------------------------- agents/plugin-advisor.md | 1 + doctor.sh | 261 ++++++++++++++ hooks/session-start.sh | 102 ++++-- install-plugins.sh | 110 +++--- lib/detect-plugins.sh | 64 ++++ link.sh | 24 +- plugins.lock.json | 24 ++ settings.json | 40 ++- skills/analyze/SKILL.md | 4 +- skills/git-pr/SKILL.md | 15 - skills/health/SKILL.md | 18 + skills/init-project/SKILL.md | 44 +-- skills/plugin-check/SKILL.md | 2 +- skills/readme/SKILL.md | 2 +- skills/refactor/SKILL.md | 4 +- skills/ship-feature/SKILL.md | 80 +---- update-all.sh | 78 +++++ version.txt | 1 + 24 files changed, 1005 insertions(+), 1068 deletions(-) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 Makefile delete mode 100644 agents/git-workflow.md create mode 100644 doctor.sh create mode 100644 lib/detect-plugins.sh create mode 100644 plugins.lock.json delete mode 100644 skills/git-pr/SKILL.md create mode 100644 skills/health/SKILL.md create mode 100644 update-all.sh create mode 100644 version.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5756794 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# ── claude-config repo ignores ── + +# Symlink created by link.sh (GStack submodule target) +skills/gstack + +# Install logs +install-*.log + +# OS +.DS_Store +Thumbs.db +desktop.ini + +# Editors +*.swp +*.swo +*~ +.idea/ +.vscode/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..32e8a20 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog + +All notable changes to claude-config will be documented in this file. + +Format follows [Keep a Changelog](https://keepachangelog.com/). + +## [Unreleased] + +## [1.0.0] — 2025-04-03 + +### Added +- 6 custom agents: analyzer, interviewer, plugin-advisor, readme-updater, refactorer, scaffolder +- 6 custom skills: analyze, init-project, plugin-check, readme, refactor, ship-feature +- 2 orchestrators with validation gates: init-project (13 steps), ship-feature (8 steps) +- Multi-OS install script (apt/dnf/pacman/brew) +- GStack as git submodule at skills-external/gstack +- Session start hook with plugin toggle status and health check +- Global settings.json with deny/ask/allow permission tiers +- Per-project templates: settings.json, settings.local.json, .claudeignore, project-CLAUDE.md +- Settings reference (SETTINGS.md) +- doctor.sh — full setup diagnostic +- update-all.sh — one-command update for all components +- plugins.lock.json — version pinning for non-marketplace dependencies +- /health skill — run doctor.sh from within Claude Code +- Makefile — unified entry point for install/link/doctor/update + +### Security +- deny rules cover: destructive commands, secrets access, privilege escalation, + code injection (eval, bash -c, xargs), pipe-to-shell, and secrets via bash (cat .env) +- disableBypassPermissionsMode enforced globally +- .claudeignore template with comprehensive exclusions diff --git a/CLAUDE.md b/CLAUDE.md index 6cf1e80..640616a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -135,6 +135,9 @@ When working on code: ## STRICT MODE +These rules are always active in orchestrator skills (/init-project, +/ship-feature) and during code review. They apply automatically. + These rules override all other instructions. - Never skip workflow steps @@ -145,6 +148,8 @@ These rules override all other instructions. ## FAIL FAST MODE +These rules are always active in every interaction. They apply automatically. + These rules override all other instructions. - Stop immediately if requirements are unclear diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9076064 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +.PHONY: help install link doctor update + +help: ## Show available commands + @grep -E '^[a-zA-Z_-]+:.*##' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*## "}; {printf " make %-12s %s\n", $$1, $$2}' + +install: link ## Full install: symlinks + prerequisites + plugins + bash install-plugins.sh + +link: ## Create symlinks into ~/.claude/ + bash link.sh + +doctor: ## Run setup diagnostic + bash doctor.sh + +update: ## Update config, submodules, plugins, and verify + bash update-all.sh diff --git a/README.md b/README.md index 9665c3a..e5288d6 100644 --- a/README.md +++ b/README.md @@ -10,77 +10,88 @@ This repo is your personal Claude Code setup, versioned and reproducible across ``` claude-config/ -├── CLAUDE.md # Global coding preferences (style, rules, workflow) -├── settings.json # Global permissions + SessionStart hook -├── install-plugins.sh # One-shot installer: prerequisites + all plugins -├── link.sh # Symlinks this repo into ~/.claude/ -├── .gitmodules # Submodule declaration (GStack) +├── CLAUDE.md # Global coding preferences (style, rules, workflow) +├── settings.json # Global permissions (77 deny / 16 ask / 57 allow rules) +├── install-plugins.sh # One-shot installer: prerequisites + all plugins (reads plugins.lock.json) +├── link.sh # Symlinks this repo into ~/.claude/ +├── doctor.sh # Setup diagnostic — checks symlinks, plugins, permissions, token budget +├── update-all.sh # One-command update for all components +├── Makefile # Unified entry point: make install / doctor / update +├── plugins.lock.json # Version pinning for non-marketplace dependencies (RTK, GSD) +├── version.txt # Semver version of this config +├── CHANGELOG.md # Release history +├── lib/ +│ └── detect-plugins.sh # Shared plugin detection — sourced by all scripts ├── hooks/ -│ └── session-start.sh # Shows toggle plugin status at every session start +│ └── session-start.sh # Health check + toggle plugin status at session start ├── skills-external/ -│ └── gstack/ # Git submodule — garrytan/gstack -│ (symlinked → ~/.claude/skills/gstack) +│ └── gstack/ # Git submodule — garrytan/gstack (symlinked to ~/.claude/skills/gstack) +├── .gitmodules # Submodule declaration ├── agents/ -│ ├── analyzer.md # Factual codebase analysis (read-only) -│ ├── git-workflow.md # Branch setup, commits, PR creation, conflict resolution -│ ├── interviewer.md # Project questionnaire → PROJECT BRIEF -│ ├── plugin-advisor.md # Detect plugin mismatches, recommend actions -│ ├── readme-updater.md # CREATE / SYNC / AUDIT README (3 modes) -│ ├── refactorer.md # Surgical refactoring with norm enforcement -│ └── scaffolder.md # Project skeleton (CLAUDE.md, settings, structure) +│ ├── analyzer.md # Factual codebase analysis (read-only) +│ ├── interviewer.md # Project questionnaire → PROJECT BRIEF +│ ├── plugin-advisor.md # Plugin check: detect mismatches, block if Superpowers missing +│ ├── readme-updater.md # Update README from git history + codebase +│ ├── refactorer.md # Surgical refactoring with norm enforcement +│ └── scaffolder.md # Full project generation (CLAUDE.md, README, code) ├── skills/ -│ ├── analyze/ # /analyze — deep factual analysis -│ ├── git-pr/ # /git-pr — commit, push, open draft PR/MR -│ ├── init-project/ # /init-project — full project initialization -│ ├── plugin-check/ # /plugin-check — check plugin config vs project needs -│ ├── readme/ # /readme — full README audit + update -│ ├── refactor/ # /refactor — improve code without changing behavior -│ └── ship-feature/ # /ship-feature — ship a feature end-to-end +│ ├── analyze/ # /analyze — deep factual analysis +│ ├── health/ # /health — run setup diagnostic +│ ├── init-project/ # /init-project — full project initialization +│ ├── plugin-check/ # /plugin-check — check plugin config vs project needs +│ ├── readme/ # /readme — update README from current state +│ ├── refactor/ # /refactor — improve code without changing behavior +│ └── ship-feature/ # /ship-feature — ship a feature end-to-end └── templates/ - ├── project-CLAUDE.md # Template for per-project CLAUDE.md + ├── project-CLAUDE.md # Template for per-project CLAUDE.md └── settings/ - ├── home-settings.json # Template for ~/.claude/settings.json - ├── settings.json # Template for project .claude/settings.json - ├── settings.local.json# Template for personal .claude/settings.local.json - ├── .claudeignore # Template for project .claudeignore - └── SETTINGS.md # Full settings reference + ├── settings.json # Template for project .claude/settings.json + ├── settings.local.json # Template for personal .claude/settings.local.json + ├── .claudeignore # Template for project .claudeignore + └── SETTINGS.md # Full settings reference ``` -**Architecture:** +**Architecture principle:** - `skills/` = entry points you invoke via `/skill-name` -- `agents/` = execution units called by skills (never invoked directly) -- Custom skills use **Superpowers** agents for implementation phases -- **Plugins** (Superpowers, GStack, GSD, etc.) install separately via `install-plugins.sh` +- `agents/` = execution units called by skills (never invoked directly by user) +- `lib/` = shared shell functions sourced by scripts (plugin detection) +- Custom skills use **Superpowers** agents for implementation phases (required — auto-detected) +- **Plugins** (Superpowers, GStack, GSD, etc.) install separately and complement custom skills --- ## Fresh install (new machine) ```bash -# 1. Clone with submodules (GStack is a git submodule) -git clone --recurse-submodules git@github.com:youruser/claude-config.git ~/claude-config +# 1. Clone with submodules — choose any location +git clone --recurse-submodules git@github.com:youruser/claude-config.git +cd claude-config # 2. Symlink into ~/.claude/ -cd ~/claude-config && bash link.sh +bash link.sh -# 3. Install prerequisites + all plugins -# Handles: git, Node.js 22, Rust/Cargo, Python 3, gh CLI, glab CLI, -# RTK, GStack (submodule), GSD, all marketplace plugins -bash ~/claude-config/install-plugins.sh +# 3. Install prerequisites + all plugins (detects OS, reads pinned versions from plugins.lock.json) +bash install-plugins.sh -# 4. Authenticate git provider CLIs (for /git-pr) -gh auth login # GitHub -glab auth login # GitLab -# Gogs/Gitea: see "Git setup" section below - -# 5. Add Context7 API key (free at context7.com) +# 4. Add Context7 API key (free at context7.com) — manual step claude mcp add --scope user context7 -- npx -y @upstash/context7-mcp --api-key YOUR_KEY -# 6. Restart Claude Code → /reload-plugins +# 5. Verify setup +bash doctor.sh + +# 6. Restart Claude Code then run /reload-plugins ``` -All `claude plugin install` calls use `--scope user` — always installs -to `~/.claude/plugins/` regardless of working directory. +All scripts use their own location to find the repo — run them from anywhere or from the repo directory. +Symlinks point to the repo's actual path, so renaming or moving the repo requires re-running `bash link.sh`. + +The install script handles: git, Node.js 22, Rust/Cargo, Python 3, RTK, GStack (submodule), GSD, +and all marketplace plugins on Linux (apt/dnf/pacman) and macOS (brew). + +RTK and GSD versions are pinned in `plugins.lock.json`. The install script reads those +versions automatically. Marketplace plugins install to `~/.claude/plugins/` (user scope). + +Install output is logged to `install-YYYYMMDD-HHMMSS.log` in the repo directory for post-mortem debugging. --- @@ -94,12 +105,15 @@ to `~/.claude/plugins/` regardless of working directory. | `/refactor` | Improve code quality without changing behavior (strict norms) | | `/readme` | Full README audit — diff vs codebase, mandatory stop, surgical updates | | `/plugin-check` | Check active plugins vs project needs — recommend enable/disable | -| `/git-pr` | Commit all changes, push, open draft PR/MR (GitHub/GitLab/Gogs/Gitea) | | `/init-project` | Initialize a complete project from scratch (full orchestrator) | | `/ship-feature` | Ship a feature end-to-end with validation gates (full orchestrator) | +| `/health` | Run setup diagnostic — check symlinks, plugins, permissions, token budget | ### Superpowers skills (auto-invoked or explicit) +> **Required dependency.** Superpowers must be active for `/init-project` and `/ship-feature`. +> The plugin-advisor (STEP 0) blocks and shows install instructions if Superpowers is missing. + | Command | When it auto-activates | |---|---| | `/superpowers:brainstorm` | When you describe something to build | @@ -109,11 +123,10 @@ to `~/.claude/plugins/` regardless of working directory. | `test-driven-development` | Auto — when implementing | | `requesting-code-review` | Auto — after a feature step | -### GStack skills (full-product projects only) +### GStack skills (Garry Tan — full-product projects only) -> Submodule at `skills-external/gstack/`, symlinked to `~/.claude/skills/gstack/`. -> **Use when:** project has UI + design + deploy + browser QA. -> Skip for backend-only, libs, CLI, scripts. +> Installed as a git submodule at `skills-external/gstack/`, symlinked to `~/.claude/skills/gstack/`. +> **Use when:** project has UI + design + deploy + browser QA. Skip for backend/lib/CLI projects. | Command | Description | |---|---| @@ -130,12 +143,12 @@ to `~/.claude/plugins/` regardless of working directory. | `/careful` | Activate safety guardrails | | `/freeze` | Lock edits to current directory | | `/retro` | Engineering retrospective | -| `/gstack-upgrade` | Self-update GStack to latest | +| `/gstack-upgrade` | Self-update GStack | -### GSD skills (multi-session large features) +### GSD skills (glittercowboy — multi-session large features) -> Installed globally via `npx get-shit-done-cc --claude --global` (done by install-plugins.sh). -> **Use when:** feature spans multiple days/sessions. +> Install: `npx get-shit-done-cc --claude --global` +> **Use when:** feature spans multiple days/sessions. Each session starts fresh with full context from previous phases. | Command | Description | |---|---| @@ -159,320 +172,135 @@ to `~/.claude/plugins/` regardless of working directory. ### `/init-project` Same rigor as `/ship-feature`. Two validation gates. Full TDD subagent pipeline for v1 features. -Scaffolder creates only the skeleton. readme-updater handles README in two passes (CREATE + SYNC). -Always works on a feature branch — never on main/master. +The Scaffolder only creates the skeleton (no features, no README). +readme-updater handles the README in two passes: CREATE then SYNC. + +STEP 0 blocks if Superpowers is not installed (required for steps 3, 6, 8, 10, 11). ``` /init-project │ - ├── STEP 0a: BRANCH SETUP (git-workflow) ← create feature branch from main - │ or sync existing branch - ├── STEP 0b: PLUGIN CHECK (plugin-advisor) ← blocks if wrong plugins + ├── STEP 0: PLUGIN CHECK (plugin-advisor) ← blocks if Superpowers missing or wrong plugins ├── STEP 1: INTERVIEWER (custom) → PROJECT BRIEF ├── STEP 2: ANALYZER (custom) → ANALYSIS REPORT ├── STEP 3: superpowers:brainstorming → VALIDATED DESIGN ├── STEP 4: VALIDATION GATE #1 → approve architecture - ├── STEP 5: SCAFFOLDER (custom) → skeleton (CLAUDE.md + settings + - │ structure + empty entry points, - │ NO features, NO README) - ├── STEP 5b: README-UPDATER create mode → CREATE README from CLAUDE.md + ├── STEP 5: SCAFFOLDER (custom) → skeleton only (CLAUDE.md + + │ settings + structure + + │ empty entry points, NO features, + │ NO README) + ├── STEP 5b: README-UPDATER create mode (custom) → CREATE README from CLAUDE.md ├── STEP 6: superpowers:writing-plans → decompose v1 features into tasks ├── STEP 7: VALIDATION GATE #2 → approve task plan - ├── STEP 8: superpowers:subagent-driven (TDD) → implement each feature + ├── STEP 8: superpowers:subagent-driven (TDD) → implement each feature (isolated) ├── STEP 9: ANALYZER (custom) → regression + deviation check ├── STEP 10: superpowers:requesting-review → full code review ├── STEP 11: superpowers:finishing-branch → cleanup + build + tests - └── STEP 12: README-UPDATER sync mode → sync README with implementation + └── STEP 12: README-UPDATER sync mode (custom) → sync README with implementation ``` ### `/ship-feature` +STEP 0 blocks if Superpowers is not installed (required for steps 1, 2, 4, 6, 7). + ``` /ship-feature │ - ├── STEP 0a: BRANCH SETUP (git-workflow) ← create/sync feature branch - ├── STEP 0b: PLUGIN CHECK (plugin-advisor) ← blocks if wrong plugins - ├── STEP 1: superpowers:brainstorming → VALIDATED DESIGN - ├── STEP 2: superpowers:writing-plans → task plan - ├── STEP 3: VALIDATION GATE → user approval required - ├── STEP 4: superpowers:subagent-driven (TDD) → implementation - ├── STEP 5: ANALYZER (custom) → regression / deviation check - ├── STEP 6: superpowers:requesting-review → code review - ├── STEP 7: superpowers:finishing-branch → cleanup - ├── STEP 8: README-UPDATER sync mode → update README - └── STEP 9: CREATE PR (optional gate) → /git-pr if user approves + ├── STEP 0: PLUGIN CHECK (plugin-advisor) ← blocks if Superpowers missing or wrong plugins + ├── STEP 1: superpowers:brainstorming → VALIDATED DESIGN + ├── STEP 2: superpowers:writing-plans → task plan + ├── STEP 3: VALIDATION GATE → user approval required + ├── STEP 4: superpowers:subagent-driven → implementation (TDD) + ├── STEP 5: ANALYZER (custom) → regression / deviation check + ├── STEP 6: superpowers:requesting-review → code review + ├── STEP 7: superpowers:finishing-branch → cleanup + └── STEP 8: README-UPDATER sync mode → sync README with new feature ``` -### `/git-pr` - -Works on any provider. Retroactive — `git diff ...HEAD` sees ALL changes since -branch creation, regardless of session count. Creates a **draft PR** — you control the merge. - -``` -/git-pr [optional title] - │ - ├── PHASE 0: Detect provider (GitHub/GitLab/Gogs/Gitea) - │ Check CLI: gh / glab / API fallback - ├── PHASE 1: Analyze branch (retroactive) - │ git diff ...HEAD — ALL changes since branch start - │ Categorize: config / model / core / api / ui / test / docs / infra - ├── PHASE 2: Propose commit plan (conventional commits) - │ [VALIDATION GATE] — user approves before any commit - ├── PHASE 3: Execute commits (staged per logical group) - ├── PHASE 4: Push (with conflict-safe rebase if rejected) - └── PHASE 5: Create draft PR/MR - GitHub → gh pr create --draft (or API) - GitLab → glab mr create --draft (or API) - Gogs → POST /api/v1/repos/{owner}/{repo}/pulls - Gitea → same API format as Gogs -``` - -**Branch → base mapping:** -| Branch prefix | Default base | Commit type | -|---|---|---| -| `feature/*`, `feat/*` | `develop` or `main` | `feat:` | -| `bugfix/*`, `fix/*` | `develop` | `fix:` | -| `hotfix/*` | `main` | `fix:` | -| `release/*` | `main` | `chore(release):` | - ### `/plugin-check` -Standalone — run before any significant work. Also auto-runs as STEP 0b -in `/init-project` and `/ship-feature`. +Standalone command you can run at any time to audit your plugin config +against what you're about to do. Also embedded as STEP 0 in both orchestrators. + +Blocks if Superpowers is not active (required by orchestrators). +Blocks if critical project-specific plugins are missing (frontend tools, Context7, GStack). ``` -/plugin-check "React + FastAPI SaaS with auth" +/plugin-check "I want to build a React + FastAPI SaaS" + → Detects active plugins → Analyzes signals: frontend? design? QA? multi-session? fast-evolving libs? → Produces recommendation table -→ Blocks if critical plugins missing, or confirms "proceed" -``` - ---- - -## Git setup for `/git-pr` - -`/git-pr` auto-detects your provider from the remote URL and uses the best available -authentication method. Here is how to set up each provider. - ---- - -### GitHub - -**Option A — gh CLI (recommended)** - -```bash -# Install (done by install-plugins.sh) -brew install gh # macOS -sudo apt install gh # Linux - -# Authenticate (interactive — opens browser) -gh auth login -# Choose: GitHub.com → HTTPS → authenticate with browser - -# Verify -gh auth status -``` - -**Option B — Personal Access Token (for CI or headless)** - -1. Go to **github.com → Settings → Developer settings → Personal access tokens → Tokens (classic)** -2. Click **Generate new token** -3. Required scopes: `repo` (full), `read:org` (if org repo) -4. Copy the token - -```bash -# Set in environment -export GITHUB_TOKEN="ghp_xxxxxxxxxxxx" -echo 'export GITHUB_TOKEN="ghp_xxxxxxxxxxxx"' >> ~/.zshrc # persist - -# Claude Code reads GITHUB_TOKEN automatically for gh CLI fallback -``` - ---- - -### GitLab - -**Option A — glab CLI (recommended)** - -```bash -# Install (done by install-plugins.sh) -brew install glab # macOS - -# Authenticate -glab auth login -# Choose: gitlab.com → Token or browser - -# For self-hosted GitLab -glab auth login --hostname gitlab.yourcompany.com - -# Verify -glab auth status -``` - -**Option B — Personal Access Token** - -1. Go to **gitlab.com → User Settings → Access Tokens** (or your instance) -2. Click **Add new token** -3. Required scopes: `api`, `read_repository`, `write_repository` -4. Copy the token - -```bash -export GITLAB_TOKEN="glpat-xxxxxxxxxxxx" -echo 'export GITLAB_TOKEN="glpat-xxxxxxxxxxxx"' >> ~/.zshrc - -# For self-hosted -export GITLAB_HOST="https://gitlab.yourcompany.com" -``` - ---- - -### Gogs - -Gogs has no official CLI. `/git-pr` uses the REST API directly. - -**Create an API token:** - -1. Log in to your Gogs instance -2. Go to **User Settings (top-right avatar) → Applications** -3. Under **Token Name**, enter `claude-code` -4. Click **Generate Token** -5. Copy the token (shown only once) - -```bash -# Set in environment -export GOGS_TOKEN="your-token-here" -echo 'export GOGS_TOKEN="your-token-here"' >> ~/.zshrc - -# Verify the API works -curl -H "Authorization: token $GOGS_TOKEN" \ - https://your-gogs-server/api/v1/user -# Should return your user JSON -``` - -**Required API permissions:** read/write on repos (tokens in Gogs have full API access by default). - ---- - -### Gitea - -Same API format as Gogs. Gitea is a Gogs fork. - -**Create an API token:** - -1. Log in to your Gitea instance -2. Go to **User Settings → Applications → Manage Access Tokens** -3. Enter token name `claude-code` -4. Select permissions: `Issues: Read/Write`, `Repository: Read/Write` -5. Click **Generate Token** and copy it - -```bash -export GITEA_TOKEN="your-token-here" -echo 'export GITEA_TOKEN="your-token-here"' >> ~/.zshrc - -# Verify -curl -H "Authorization: token $GITEA_TOKEN" \ - https://your-gitea-server/api/v1/user -``` - ---- - -### Self-hosted GitHub Enterprise - -```bash -# Configure gh for your instance -gh config set -h github.yourcompany.com git_protocol https -gh auth login --hostname github.yourcompany.com - -# Verify -gh auth status --hostname github.yourcompany.com -``` - ---- - -### Self-hosted GitLab - -```bash -glab auth login --hostname gitlab.yourcompany.com --token glpat-xxxx -``` - ---- - -### Token security - -- **Never commit tokens** to git — they go in `~/.zshrc`, `~/.bashrc`, or a secrets manager -- **Rotate tokens** when they expire or are compromised -- **Minimum scopes** — only grant what `/git-pr` needs (repo read/write) -- Claude Code reads env vars securely — tokens are never written to disk by Claude - ---- - -### Verify your setup - -```bash -# Run inside Claude Code to verify everything -/git-pr check-auth -# → Detects provider from current repo remote -# → Tests authentication -# → Reports status per provider -``` - -Or manually: -```bash -git remote get-url origin # see which provider -gh auth status # GitHub CLI status -glab auth status # GitLab CLI status -curl -s -H "Authorization: token $GOGS_TOKEN" \ - /api/v1/user | jq .login # Gogs/Gitea +→ Blocks with OPTIONS if critical plugins are missing (including Superpowers) +→ Or confirms "proceed" if config is optimal ``` --- ## Plugins reference -All plugins installed by `install-plugins.sh`. +All plugins below are installed by `install-plugins.sh`. -### At-a-glance: always on vs toggle +### Quick reference -The mechanism: Claude Code loads every active skill's **description** at session start -into a shared budget (8000 chars). Even if never invoked, the description costs tokens. -Disabling a plugin → descriptions never load. +The mechanism: Claude Code loads every active skill's **description** into a shared context budget +at session start (default 8000 chars). Even if you never invoke the skill, its description +is already consuming tokens. **Disabling a plugin prevents its descriptions from loading entirely.** -A `hooks/session-start.sh` hook shows toggle status at every session start. -Run `/plugin-check` for a full recommendation for the current project type. +A `hooks/session-start.sh` hook shows the current toggle status at the start of every session. +Run `/plugin-check` anytime to get a full recommendation for the current project type. -| Plugin | Status | Cost/session | When to enable | Installed by | +| Plugin | Status | Passive cost | When to toggle ON | Installed by | |---|---|---|---|---| -| **security-guidance** | ✅ ALWAYS ON | 0 (hook only) | — | marketplace | -| **RTK** | ✅ ALWAYS ON | 0 (hook only) | — | cargo + rtk init | -| **Superpowers** | ✅ ALWAYS ON | ~600–1000 | — auto-activates | marketplace | -| **skill-creator** | ✅ ALWAYS ON | ~100 | — | marketplace | -| **pr-review-toolkit** | ✅ ALWAYS ON | ~300 | — `/pr-review-toolkit:review-pr` | marketplace | -| **GStack** | 🔄 TOGGLE | ~2500–3000 | Full-product: UI + design + deploy + QA browser | submodule | -| **GSD** | 🔄 TOGGLE | ~500–800 | Feature spanning multiple days/sessions | npx | -| **frontend-design** | 🔄 TOGGLE | ~200 | Any project with a UI | marketplace | -| **ui-ux-pro-max** | 🔄 TOGGLE | ~400 | Design system, color/typography | marketplace | -| **Context7 MCP** | 🔄 TOGGLE | ~200 | Fast-evolving libs (Next.js, React, Prisma…) | MCP manual | +| **security-guidance** | ✅ ALWAYS ON | 0 tokens (hook only) | — | marketplace | +| **RTK** | ✅ ALWAYS ON | 0 tokens (hook only) | — | cargo (pinned in plugins.lock.json) | +| **Superpowers** | ✅ REQUIRED | ~600–1000 tokens | — required by orchestrators, auto-detected | marketplace | +| **skill-creator** | ✅ ALWAYS ON | ~100 tokens | — | marketplace | +| **pr-review-toolkit** | ✅ ALWAYS ON | ~300 tokens | — use `/pr-review-toolkit:review-pr` | marketplace | +| **GStack** | 🔄 TOGGLE | ~2500–3000 tokens | Full-product: UI + design + deploy + browser QA | git submodule | +| **GSD** | 🔄 TOGGLE | ~500–800 tokens | Feature spanning multiple days/sessions | npx (pinned in plugins.lock.json) | +| **frontend-design** | 🔄 TOGGLE | ~200 tokens | Any project with a UI | marketplace | +| **ui-ux-pro-max** | 🔄 TOGGLE | ~400 tokens | Design system, color/typography choices | marketplace | +| **Context7 MCP** | 🔄 TOGGLE | ~200 tokens | Fast-evolving libs (Next.js, React, Prisma…) | MCP manual | -Toggle plugins start **OFF**. `/plugin-check` signals when to enable them. -`/init-project` and `/ship-feature` run plugin-check automatically as STEP 0b. +**Rule:** toggle plugins are OFF by default. `/plugin-check` signals when to enable them. +If you use `/init-project` or `/ship-feature`, plugin-check runs automatically as STEP 0 +and **blocks if Superpowers is not active**. -### Disable a plugin for a specific project +### Version pinning -```bash -/plugin # inside Claude Code → toggle off +RTK and GSD versions are pinned in `plugins.lock.json`: + +```json +{ + "rtk": { "version": "v0.34.3" }, + "gsd": { "version": "1.30.0" } +} ``` -Or in `.claude/settings.json`: +`install-plugins.sh` reads these versions automatically. +To update a pinned version: edit `plugins.lock.json`, then re-run `install-plugins.sh`. +GStack is pinned via its git submodule pointer. + +### Disabling a plugin for a specific project + +```bash +# In Claude Code +/plugin +# → Find the plugin → toggle off for this scope +``` + +Or in the project's `.claude/settings.json`: ```json { "enabledPlugins": { - "gstack@gstack": false + "gstack@gstack": false, + "gsd@gsd": false } } ``` -### Enable a plugin for a project (share with teammates) +### Enabling a plugin for a specific project (so teammates can install it) ```json { @@ -481,7 +309,10 @@ Or in `.claude/settings.json`: }, "extraKnownMarketplaces": { "ui-ux-pro-max-skill": { - "source": { "source": "github", "repo": "nextlevelbuilder/ui-ux-pro-max-skill" } + "source": { + "source": "github", + "repo": "nextlevelbuilder/ui-ux-pro-max-skill" + } } } } @@ -506,81 +337,93 @@ DENY always wins over ALLOW at any level. .claudeignore applies independently of all permission rules. ``` -### Global `settings.json` (this repo) +### Global settings (this repo's `settings.json`) -Contains global deny/ask/allow rules AND a SessionStart hook: - -```json -"hooks": { - "SessionStart": [ - { "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/session-start.sh" }] } - ] -} -``` - -The hook prints toggle plugin status at every session start — zero API calls, filesystem only. +77 deny rules, 16 ask rules, 57 allow rules. | Section | Purpose | |---|---| -| `deny` — secrets | Blocks `.env`, `.pem`, `.key`, SSH keys, cloud credentials | +| `deny` — secrets (Read) | Blocks `Read` on `.env`, `.pem`, `.key`, SSH keys, cloud credentials | +| `deny` — secrets (Bash) | Blocks `cat`, `head`, `tail`, `grep`, `less`, `more` on `.env` and secret files | | `deny` — destructive | Blocks `rm -rf`, `git push --force`, `chmod 777` | | `deny` — system | Blocks `sudo`, `ssh`, `scp`, `crontab`, `systemctl` | | `deny` — injection | Blocks `curl \| bash`, `wget \| sh` | +| `deny` — escalation | Blocks `bash -c`, `eval`, `exec`, `find -delete`, `perl -e`, `ruby -e` | | `ask` — risky | Prompts before `git push`, `docker run`, package managers | -| `allow` — safe reads | Auto-approves git read-only, `ls`, `cat`, `grep`, `find` | +| `ask` — write tools | Prompts before `xargs`, `sed -i` (in-place file editing) | +| `allow` — safe reads | Auto-approves git read-only, `ls`, `cat`, `grep`, `find`, `sed` (stdout only) | | `disableBypassPermissionsMode` | Prevents YOLO mode globally | ### Per-project setup ```bash -cd your-project && mkdir -p .claude +cd your-project +mkdir -p .claude -# Project settings (commit) -cp ~/claude-config/templates/settings/settings.json .claude/settings.json +# Find the repo from any existing symlink +CONF="$(dirname "$(readlink ~/.claude/CLAUDE.md)")" -# Personal overrides (never commit) -cp ~/claude-config/templates/settings/settings.local.json .claude/settings.local.json +# Project settings (commit to project git) +cp "$CONF/templates/settings/settings.json" .claude/settings.json + +# Personal overrides (never commit — gitignore it) +cp "$CONF/templates/settings/settings.local.json" .claude/settings.local.json echo ".claude/settings.local.json" >> .gitignore -# Hard file exclusions (commit) -cp ~/claude-config/templates/settings/.claudeignore .claudeignore +# Hard file exclusions (commit to project git) +cp "$CONF/templates/settings/.claudeignore" .claudeignore -# Project CLAUDE.md (commit) -cp ~/claude-config/templates/project-CLAUDE.md .claude/CLAUDE.md +# Project CLAUDE.md (commit to project git) +cp "$CONF/templates/project-CLAUDE.md" .claude/CLAUDE.md ``` --- ## Updating -### This repo +### One-command update (recommended) + ```bash -cd ~/claude-config && git pull -# Symlinks → changes active immediately, no restart needed +# From the repo directory +bash update-all.sh +# Pulls config, updates GStack submodule, updates RTK (pinned version), refreshes symlinks, runs doctor ``` -### GStack (submodule) +### Manual updates + +#### This repo ```bash -# Option A — from Claude Code +# cd into the repo (wherever you cloned it) +git pull +# Symlinks → changes active immediately +``` + +#### GStack (submodule) +```bash +# Option A — inside Claude Code (recommended) /gstack-upgrade -# Option B — via submodule (pins version in your repo) -cd ~/claude-config +# Option B — via submodule (from the repo directory) git submodule update --remote skills-external/gstack cd skills-external/gstack && ./setup git add skills-external/gstack -git commit -m "chore: update gstack" +git commit -m "chore: update gstack to latest" ``` -### Marketplace plugins +GStack is a git submodule. Its version is pinned in your config repo — reproducible on every machine. + +#### RTK ```bash -/plugin marketplace update +# Uses the version pinned in plugins.lock.json (from the repo directory) +bash update-all.sh + +# Or manually (check latest at https://github.com/rtk-ai/rtk/releases) +cargo install --git https://github.com/rtk-ai/rtk --tag v0.34.3 --force ``` -### RTK +#### Marketplace plugins ```bash -cargo install --git https://github.com/rtk-ai/rtk --force -rtk init -g --auto-patch # re-apply hook if needed +/plugin marketplace update # inside Claude Code ``` --- @@ -596,6 +439,7 @@ name: myskill description: What this skill does — front-load the key use case (max 250 chars) argument-hint: disable-model-invocation: true +allowed-tools: Read, Write, Edit, Bash, Grep, Glob --- Load and follow strictly: @@ -612,8 +456,101 @@ $ARGUMENTS ## Per-project agent overrides +Override any global agent for a specific project: + ```bash -# Override an agent for a specific project -cp ~/claude-config/agents/refactorer.md .claude/agents/refactorer.md -# Edit — the local version takes precedence over global +CONF="$(dirname "$(readlink ~/.claude/CLAUDE.md)")" +cp "$CONF/agents/refactorer.md" .claude/agents/refactorer.md +# Edit .claude/agents/refactorer.md — the local version takes precedence ``` + +--- + +## Maintenance + +### Diagnostic + +```bash +# Quick check from terminal (from the repo directory) +bash doctor.sh + +# Or from within Claude Code +/health + +# Unified commands via Makefile (from the repo directory) +make doctor # diagnostic +make update # pull + submodules + symlinks + doctor +make install # link.sh + install-plugins.sh +``` + +`doctor.sh` checks 7 axes: symlinks, GStack submodule, prerequisites (git, Node, Cargo, Python, Claude Code), +plugins (RTK, Superpowers, Context7), permissions, token budget estimate, and config consistency +(frontmatter coherence, CRLF detection). + +`session-start.sh` runs a quick health check at every session start (filesystem only, no subprocesses) +and displays toggle plugin status with `/plugin-check` and `/health` hints. + +Both scripts source `lib/detect-plugins.sh` for consistent plugin detection logic. + +### Updating + +```bash +# One-command update (from the repo directory) +bash update-all.sh + +# Or step by step +git pull # this repo +git submodule update --remote skills-external/gstack # GStack +bash link.sh # refresh symlinks +bash doctor.sh # verify +``` + +--- + +## Troubleshooting + +### "command not found" after install +Restart your shell or run `source ~/.bashrc` / `source ~/.zshrc`. + +### Orchestrator blocks at STEP 0 — Superpowers missing +The plugin-advisor blocks `/init-project` and `/ship-feature` if Superpowers is not active. +Install: `claude plugin marketplace add obra/superpowers-marketplace && claude plugin install --scope user superpowers@superpowers-marketplace` +Then re-run the orchestrator. + +### "agent not found" or hallucinated agent content +Symlinks are broken. `cd` into your config repo and run `bash link.sh`, then verify with `bash doctor.sh`. + +### GStack skills not showing up +Run `bash link.sh` and verify: `ls -la ~/.claude/skills/gstack`. +If missing: `cd` into your config repo and run `git submodule update --init`. + +### link.sh warns "is a real directory" +If `~/.claude/agents/`, `~/.claude/skills/`, or `~/.claude/lib/` exist as real directories (not symlinks +from a previous `link.sh` run), the script skips them to avoid data loss. Rename or remove the directory, then re-run `link.sh`. + +### Token budget exceeded / skills truncated at session start +Too many plugins active. Run `/plugin-check` to optimize. +Run `bash doctor.sh` for a token budget estimate. + +### settings.json not applying +Check precedence: deny always wins over allow at any level. `.claudeignore` overrides all permission rules. +Verify deny count: `cat ~/.claude/settings.json | python3 -c "import json,sys; print(len(json.load(sys.stdin)['permissions']['deny']))"` +Expected: 77 deny rules. + +### Claude reads .env despite deny rules +The `Read(**/.env)` deny rule blocks the Read tool. `Bash(cat .env)` and similar commands have separate +deny rules (included in this config). For hard exclusion regardless of tool, use `.claudeignore` in the project root. + +### install-plugins.sh failed — where are the logs? +Check `install-YYYYMMDD-HHMMSS.log` in your config repo directory — the script logs all output to a timestamped file. + +--- + +## Known limitations + +- **Deny rules are pattern-based, not sandboxed.** Common bypass vectors (`bash -c`, `eval`, `xargs`, `cat .env`) are blocked, but novel indirect patterns are still possible. `.claudeignore` is the only hard file exclusion mechanism. +- **Superpowers is a hard dependency** for `/init-project` and `/ship-feature`. The plugin-advisor (STEP 0) auto-detects and blocks if Superpowers is missing, with install instructions. There is no manual fallback mode. +- **Marketplace plugin versions are not pinned.** They install latest. Non-marketplace tools (RTK, GSD) are pinned in `plugins.lock.json` and read by `install-plugins.sh`. +- **Token budget is finite and not directly observable.** With all toggle plugins active, the description budget can exceed 60%. Run `/health` or `bash doctor.sh` for an estimate. +- **Agent frontmatter fields** like `model` and `memory` are declared but their enforcement by Claude Code is not guaranteed. They serve as documentation more than strict runtime controls. +- **`Bash(cat *)` in allow vs `Bash(cat .env)` in deny** depends on Claude Code resolving deny-wins. This is the expected behavior but cannot be tested outside the runtime. diff --git a/agents/git-workflow.md b/agents/git-workflow.md deleted file mode 100644 index 0a42731..0000000 --- a/agents/git-workflow.md +++ /dev/null @@ -1,473 +0,0 @@ ---- -name: git-workflow -description: Analyze all changes since branch start (retroactive, session-agnostic), create logical commits, push, and open a PR/MR on GitHub, GitLab, Gogs, or Gitea. Never merges — creates a draft PR for user validation. -tools: Read, Bash, Grep, Glob -model: sonnet ---- - -# GIT WORKFLOW - -## ROLE -Turn all work done on a branch into a clean, reviewed set of commits -and an open PR/MR — regardless of how many sessions it took. - -## GOAL -- Stage and commit all uncommitted changes in logical groups -- Push the branch to the remote -- Create a PR/MR on the right platform -- Never merge — the user validates the PR - ---- - -## BRANCH SETUP (called by init-project and ship-feature) - -This procedure runs before any code is written. -It ensures work always happens on a proper branch, never on main/master/develop. - -### 1. Check current branch - -```bash -CURRENT=$(git branch --show-current) -echo "Current branch: $CURRENT" -``` - -### 2. Determine protected branches - -Protected branches (never commit directly): -- `main`, `master`, `develop`, `dev`, `staging`, `production`, `prod` - -```bash -PROTECTED="main master develop dev staging production prod" -``` - -### 3. If on a protected branch → create a feature branch - -```bash -# Derive branch name from context: -# - init-project: feature/ -# - ship-feature: feature/ -# Slugify: lowercase, replace spaces with hyphens, max 50 chars - -BRANCH_NAME="feature/" - -# Ensure main is up to date before branching -git fetch origin -git pull origin $CURRENT --ff-only 2>/dev/null || true - -# Create and checkout the feature branch -git checkout -b $BRANCH_NAME - -echo "✅ Created branch: $BRANCH_NAME (from $CURRENT)" -``` - -### 4. If already on a feature/bugfix/hotfix branch → sync with base - -```bash -BASE=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD origin/master 2>/dev/null) - -# Check if the base branch has new commits the feature branch doesn't have -git fetch origin -BEHIND=$(git rev-list HEAD..origin/ --count 2>/dev/null || echo "0") -``` - -If `BEHIND > 0`: -``` -⚠️ Your branch is behind by $BEHIND commit(s). -Rebasing to sync... -``` -Run the CONFLICT-SAFE REBASE procedure below. - -If `BEHIND = 0`: print `✅ Branch is up to date` and continue. - -### 5. Branch naming conventions - -| Context | Branch format | Example | -|---|---|---| -| New project (init-project) | `feature/` | `feature/zenquality-website` | -| New feature (ship-feature) | `feature/` | `feature/user-authentication` | -| Bug on a feature branch | `bugfix/` from feature | `bugfix/fix-login-redirect` | -| Urgent production fix | `hotfix/` from main | `hotfix/patch-csrf-vulnerability` | -| Release prep | `release/` from main | `release/v1.2.0` | - ---- - -## CONFLICT-SAFE REBASE - -Use this whenever rebasing a branch against its base. - -```bash -git rebase origin/ -``` - -**If rebase exits cleanly:** done. - -**If conflicts are detected:** - -```bash -# List conflicted files -git diff --name-only --diff-filter=U -``` - -For each conflicted file: -1. Read the file — identify the conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) -2. Analyze both versions: - - `HEAD` (ours) = the feature branch code - - `origin/` (theirs) = what's on the base branch -3. Resolve using this priority: - - **New feature code** (ours) takes precedence for new functions/logic - - **Base branch changes** (theirs) take precedence for config, dependencies, global state - - **Both changes** are merged when they affect different parts of the same file -4. Write the resolved file (no conflict markers left) -5. Stage it: `git add ` -6. Continue: `git rebase --continue` - -**If a conflict cannot be auto-resolved** (same lines modified with incompatible logic): -``` -⚠️ CONFLICT — manual resolution required -File: -Ours: -Theirs: - -How should this be resolved? - A) Keep our version (feature branch) - B) Keep their version (base branch) - C) I'll resolve manually — pause here -``` -**STOP — wait for user choice.** - -After user responds: -- A or B → apply, `git add `, `git rebase --continue` -- C → `git rebase --abort` and hand back control to user - -**After all conflicts resolved:** -```bash -git log --oneline ..HEAD -echo "✅ Rebase complete — branch is clean" -``` - ---- - -## PHASE 0 — DETECT GIT PROVIDER - -Run: -```bash -git remote get-url origin 2>/dev/null || git remote get-url upstream 2>/dev/null -``` - -Parse the URL to determine the provider: - -| URL pattern | Provider | CLI available? | -|---|---|---| -| `github.com` | GitHub | check `gh auth status` | -| `gitlab.com` or `gitlab.*` | GitLab | check `glab auth status` | -| anything else | Gogs / Gitea | API only (no official CLI) | - -Extract: -- `REMOTE_URL` — full remote URL -- `PROVIDER` — github / gitlab / gogs-gitea -- `BASE_URL` — for Gogs/Gitea: `https://hostname` (strip path) -- `OWNER` — repo owner/organization -- `REPO` — repo name -- `CLI_AVAILABLE` — gh / glab / none - -For Gogs/Gitea, check for an API token in env: -```bash -echo "${GOGS_TOKEN:-${GITEA_TOKEN:-not-set}}" -``` -If not set, print: -``` -⚠️ Gogs/Gitea detected. Set one of these env vars: - export GOGS_TOKEN="your-token" - export GITEA_TOKEN="your-token" - Then re-run /git-pr -``` -And STOP. - ---- - -## PHASE 1 — ANALYZE BRANCH STATE - -### 1a. Identify current branch and base - -```bash -git branch --show-current -``` - -Determine the base branch from the branch name: -| Branch prefix | Default base | PR type | -|---|---|---| -| `feature/*` | `develop` (or `main` if no develop) | Feature | -| `feat/*` | `develop` (or `main`) | Feature | -| `bugfix/*` | `develop` | Bug fix | -| `fix/*` | `develop` | Bug fix | -| `hotfix/*` | `main` | Hotfix | -| `release/*` | `main` | Release | -| `chore/*` | `develop` (or `main`) | Chore | -| anything else | `main` | General | - -Verify the base branch exists: -```bash -git rev-parse --verify 2>/dev/null -``` - -### 1b. Retroactive diff — ALL changes since branch start - -```bash -# All committed changes on this branch (retroactive, session-agnostic) -git log --oneline ..HEAD - -# All uncommitted changes -git status --short - -# Full diff of everything: committed + uncommitted vs base -git diff ...HEAD --name-status - -# Stats -git diff ...HEAD --stat -``` - -The `git diff ...HEAD` (three dots) shows everything since -the branch diverged from base — not just since last commit. -This is the source of truth regardless of session count. - -### 1c. Build the CHANGE MAP - -Categorize every changed file: -- `config` — package.json, Cargo.toml, go.mod, requirements.txt, docker-compose.yml, Makefile, CI files -- `model` — data models, schemas, migrations, types -- `core` — business logic, services, domain -- `api` / `routes` — endpoints, controllers, handlers -- `ui` — components, pages, styles, assets -- `test` — all test files -- `docs` — README, CLAUDE.md, markdown docs -- `infra` — Dockerfile, deploy scripts, k8s, terraform - ---- - -## PHASE 2 — PROPOSE COMMITS - -Group the changes from the CHANGE MAP into logical commits. -Order: config → model → core → api/routes → ui → test → docs → infra - -For each group, propose a commit using Conventional Commits: -``` -(): - -Types: feat, fix, chore, refactor, test, docs, style, ci, build, perf -Scope: optional, matches the module/directory -``` - -Present the proposed commit plan: -``` -================================================================ -GIT WORKFLOW — COMMIT PLAN -================================================================ - -BRANCH : -BASE : -PROVIDER : - -CHANGES SINCE BRANCH START ---------------------------- - - -PROPOSED COMMITS ----------------- - 1. chore(deps): update dependencies — [package.json, go.mod] - 2. feat(auth): add user model and migration — [models/user.go, migrations/001_users.sql] - 3. feat(auth): implement login and JWT handlers — [handlers/auth.go, services/auth.go] - 4. test(auth): add unit tests for auth service — [tests/auth_test.go] - 5. docs: update README with auth setup — [README.md] - -UNCOMMITTED CHANGES (will be staged in their respective commit) ---------------------------------------------------------------- - - -================================================================ -Approve this commit plan? (yes / modify / cancel) -================================================================ -``` - -**MANDATORY STOP — wait for user approval.** - -IF modify → user describes changes → adjust plan → re-present -IF cancel → stop, no changes made -IF yes → proceed to PHASE 3 - ---- - -## PHASE 3 — EXECUTE COMMITS - -For each proposed commit, in order: - -```bash -# Stage the specific files for this commit -git add - -# Commit with the proposed message -git commit -m "(): " -``` - -If a file has both committed and uncommitted changes: -- Stage only the uncommitted portion -- Include it in the appropriate commit group - -After all commits: -```bash -git log --oneline ..HEAD -``` -Show the final commit list for confirmation. - ---- - -## PHASE 4 — PUSH - -```bash -# Push, setting upstream if branch is new -git push --set-upstream origin -``` - -**If push is rejected** (remote has diverged): -1. Run the CONFLICT-SAFE REBASE procedure: - ```bash - git fetch origin - git rebase origin/ - ``` -2. Resolve any conflicts as described in CONFLICT-SAFE REBASE -3. Push again: `git push origin ` - -**If the push is still rejected after rebase:** -```bash -git log --oneline origin/..HEAD -``` -Show the commits that haven't been pushed and ask the user: -``` -⚠️ Push still rejected after rebase. - Local commits not on remote: - Options: - A) Force push (overwrites remote — use only if remote is yours) - B) Investigate manually -``` -**STOP — wait for user choice. Never force push without explicit approval.** - ---- - -## PHASE 5 — CREATE PR / MR - -Build the PR body from: -- `git log ..HEAD --format="- %s"` — commit list -- Modified file categories (from CHANGE MAP) -- CLAUDE.md project context - -PR body template: -```markdown -## Summary - -<2-3 sentence description derived from branch name and commit messages> - -## Changes - - - -## Modified areas - - - -## Testing - - - ---- -*Created by /git-pr — validate then merge* -``` - -### GitHub - -```bash -# With gh CLI (preferred) -gh pr create \ - --base \ - --head \ - --title ": " \ - --body "" \ - --draft - -# Without gh CLI — print URL for manual creation -echo "Create PR at: https://github.com///compare/..." -``` - -### GitLab - -```bash -# With glab CLI (preferred) -glab mr create \ - --source-branch \ - --target-branch \ - --title ": " \ - --description "" \ - --draft - -# Without glab CLI -echo "Create MR at: https://gitlab.com///-/merge_requests/new?merge_request[source_branch]=" -``` - -### Gogs / Gitea - -Use the API directly (both use GitHub API v3-compatible format): - -```bash -curl -s -X POST \ - "${BASE_URL}/api/v1/repos/${OWNER}/${REPO}/pulls" \ - -H "Authorization: token ${GOGS_TOKEN:-$GITEA_TOKEN}" \ - -H "Content-Type: application/json" \ - -d "{ - \"title\": \": \", - \"body\": \"\", - \"head\": \"\", - \"base\": \"\" - }" -``` - -If the API call returns an error, print the full response and suggest -creating the PR manually via the web UI. - ---- - -## PHASE 6 — FINAL REPORT - -``` -================================================================ -PR CREATED -================================================================ - -BRANCH : -COMMITS : commits pushed -PLATFORM : - -COMMITS -------- - - -PR / MR -------- -URL : "> -Type : Draft — awaiting your review and merge - -NEXT STEPS ----------- -1. Review the PR at the URL above -2. Request reviews if needed -3. Merge when approved -================================================================ -``` - ---- - -## RULES - -- Never merge, never rebase main/master/develop -- Never force push unless explicitly asked -- Never create commits on main, master, or develop directly -- If on main/master — STOP and ask user to checkout a feature branch first -- Draft PR by default — user controls merge -- If no CLI tool and no API token — print manual instructions, do not fail silently diff --git a/agents/plugin-advisor.md b/agents/plugin-advisor.md index 97ac66e..cbc0291 100644 --- a/agents/plugin-advisor.md +++ b/agents/plugin-advisor.md @@ -123,6 +123,7 @@ ACTION REQUIRED? [YES — resolve blocking issues first] / [NO — proceed] ## THRESHOLDS **Block and require action if:** +- Superpowers is not active (required by /init-project and /ship-feature orchestrators — install command: `claude plugin marketplace add obra/superpowers-marketplace && claude plugin install --scope user superpowers@superpowers-marketplace`) - Project has significant frontend AND frontend-design + ui-ux-pro-max are both disabled - Project uses Next.js/React/Prisma/Supabase AND context7 is not configured - Project is full-product (UI + deploy + QA) AND gstack is not installed diff --git a/doctor.sh b/doctor.sh new file mode 100644 index 0000000..447a3b7 --- /dev/null +++ b/doctor.sh @@ -0,0 +1,261 @@ +#!/usr/bin/env bash +# ============================================================ +# Claude Code — Config doctor +# Diagnoses symlinks, prerequisites, plugins, permissions, +# and token budget. Run after install or when something breaks. +# ============================================================ +set -euo pipefail + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m' +ERRORS=0; WARNS=0 + +pass() { echo -e " ${GREEN}✓${NC} $1"; } +fail() { echo -e " ${RED}✗${NC} $1"; ERRORS=$((ERRORS + 1)); } +warn() { echo -e " ${YELLOW}⚠${NC} $1"; WARNS=$((WARNS + 1)); } +info() { echo -e " ${BLUE}→${NC} $1"; } + +REPO="$(cd "$(dirname "$0")" && pwd)" +VERSION=$(cat "$REPO/version.txt" 2>/dev/null || echo "unknown") + +# Load shared detection library +# shellcheck source=lib/detect-plugins.sh +source "$REPO/lib/detect-plugins.sh" + +echo "" +echo "═══ claude-config doctor (v${VERSION}) ═══" +echo "" + +# ──────────────────────────────────────────────────────────── +# 1. Core symlinks +# ──────────────────────────────────────────────────────────── +echo "── Symlinks ──" + +check_symlink() { + local name="$1" + local target="$HOME/.claude/$name" + + if [ ! -e "$target" ] && [ ! -L "$target" ]; then + fail "~/.claude/$name — MISSING" + return + fi + + if [ -L "$target" ]; then + local real + real=$(readlink -f "$target" 2>/dev/null || readlink "$target") + if [ ! -e "$real" ]; then + fail "~/.claude/$name → $real — BROKEN SYMLINK" + else + pass "~/.claude/$name" + fi + else + warn "~/.claude/$name exists but is NOT a symlink (expected symlink to repo)" + fi +} + +check_symlink "CLAUDE.md" +check_symlink "settings.json" +check_symlink "agents" +check_symlink "skills" +check_symlink "hooks/session-start.sh" + +echo "" + +# ──────────────────────────────────────────────────────────── +# 2. GStack submodule +# ──────────────────────────────────────────────────────────── +echo "── GStack submodule ──" + +if [ -d "$REPO/skills-external/gstack" ] || [ -f "$REPO/skills-external/gstack/.git" ]; then + pass "Submodule present at skills-external/gstack" +else + warn "Submodule not initialized — run: git submodule update --init" +fi + +if [ -L "$HOME/.claude/skills/gstack" ]; then + real=$(readlink -f "$HOME/.claude/skills/gstack" 2>/dev/null || readlink "$HOME/.claude/skills/gstack") + if [ -d "$real" ]; then + pass "Symlink OK → $real" + else + fail "Symlink broken → $real" + fi +else + warn "GStack not symlinked — run: bash link.sh" +fi + +echo "" + +# ──────────────────────────────────────────────────────────── +# 3. Prerequisites +# ──────────────────────────────────────────────────────────── +echo "── Prerequisites ──" + +if command -v git &>/dev/null; then + pass "git $(git --version | awk '{print $3}')" +else + fail "git not found" +fi + +if command -v node &>/dev/null; then + NODE_VER=$(node --version | sed 's/v//' | cut -d. -f1) + if [ "$NODE_VER" -ge 18 ]; then + pass "Node.js $(node --version)" + else + warn "Node.js $(node --version) — need >=18" + fi +else + fail "Node.js not found" +fi + +if command -v cargo &>/dev/null; then + pass "Cargo $(cargo --version | awk '{print $2}')" +else + warn "Cargo not found (RTK unavailable)" +fi + +if command -v python3 &>/dev/null; then + pass "Python $(python3 --version | awk '{print $2}')" +else + warn "Python3 not found" +fi + +if command -v claude &>/dev/null; then + pass "Claude Code $(claude --version 2>/dev/null | head -1 || echo 'installed')" +else + fail "Claude Code not found — install from https://code.claude.com" +fi + +echo "" + +# ──────────────────────────────────────────────────────────── +# 4. Key plugins +# ──────────────────────────────────────────────────────────── +echo "── Plugins ──" + +if detect_rtk; then + pass "RTK installed" +else + warn "RTK not installed — run install-plugins.sh" +fi + +if detect_superpowers; then + pass "Superpowers plugin detected" +else + fail "Superpowers not detected — orchestrators (/init-project, /ship-feature) will fail" +fi + +if detect_context7; then + pass "Context7 MCP configured" +else + info "Context7 MCP not configured (optional — needed for fast-evolving libs)" +fi + +echo "" + +# ──────────────────────────────────────────────────────────── +# 5. Permissions check +# ──────────────────────────────────────────────────────────── +echo "── Permissions ──" + +SETTINGS="$HOME/.claude/settings.json" +if [ -f "$SETTINGS" ] || [ -L "$SETTINGS" ]; then + if grep -q '"disableBypassPermissionsMode"' "$SETTINGS" 2>/dev/null; then + pass "Bypass mode disabled" + else + warn "disableBypassPermissionsMode not found in settings" + fi + + DENY_COUNT=$(python3 -c " +import json +with open('$SETTINGS') as f: + d = json.load(f) +print(len(d.get('permissions',{}).get('deny',[]))) +" 2>/dev/null || echo "?") + pass "Deny rules: $DENY_COUNT" +else + fail "~/.claude/settings.json not found" +fi + +echo "" + +# ──────────────────────────────────────────────────────────── +# 6. Token budget estimate +# ──────────────────────────────────────────────────────────── +echo "── Token budget estimate ──" + +TOTAL_CHARS=0 + +# Skill descriptions +for f in "$HOME/.claude/skills/"*/SKILL.md; do + [ -f "$f" ] || continue + desc=$(sed -n 's/^description: //p' "$f" 2>/dev/null || true) + TOTAL_CHARS=$((TOTAL_CHARS + ${#desc})) +done + +# Agent descriptions +for f in "$HOME/.claude/agents/"*.md; do + [ -f "$f" ] || continue + desc=$(sed -n '/^---$/,/^---$/{ s/^description: //p }' "$f" 2>/dev/null || true) + TOTAL_CHARS=$((TOTAL_CHARS + ${#desc})) +done + +if [ "$TOTAL_CHARS" -gt 6000 ]; then + warn "Custom descriptions: ~${TOTAL_CHARS} chars (budget ~8000) — risk of truncation" +elif [ "$TOTAL_CHARS" -gt 4000 ]; then + info "Custom descriptions: ~${TOTAL_CHARS} chars (within budget, moderate margin)" +else + pass "Custom descriptions: ~${TOTAL_CHARS} chars (comfortable)" +fi + +echo "" + +# ──────────────────────────────────────────────────────────── +# 7. File consistency +# ──────────────────────────────────────────────────────────── +echo "── Consistency ──" + +# Check all skills have disable-model-invocation +MISSING_DMI=() +for f in "$HOME/.claude/skills/"*/SKILL.md; do + [ -f "$f" ] || continue + name=$(basename "$(dirname "$f")") + if ! grep -q "disable-model-invocation" "$f" 2>/dev/null; then + MISSING_DMI+=("$name") + fi +done +if [ ${#MISSING_DMI[@]} -eq 0 ]; then + pass "All skills have disable-model-invocation" +else + warn "Skills missing disable-model-invocation: ${MISSING_DMI[*]}" +fi + +# Check CRLF +CRLF_FILES=() +for f in "$REPO"/*.md "$REPO"/agents/*.md "$REPO"/skills/*/SKILL.md; do + [ -f "$f" ] || continue + if grep -qP '\r' "$f" 2>/dev/null; then + CRLF_FILES+=("$(basename "$f")") + fi +done +if [ ${#CRLF_FILES[@]} -eq 0 ]; then + pass "No CRLF line endings detected" +else + warn "CRLF detected in: ${CRLF_FILES[*]}" +fi + +echo "" + +# ──────────────────────────────────────────────────────────── +# Summary +# ──────────────────────────────────────────────────────────── +echo "═══════════════════════════════════════════" +if [ "$ERRORS" -gt 0 ]; then + echo -e "${RED} $ERRORS error(s)${NC}, ${YELLOW}$WARNS warning(s)${NC}" + echo "" + echo " Fix: cd $REPO && bash link.sh && bash install-plugins.sh" + exit 1 +elif [ "$WARNS" -gt 0 ]; then + echo -e " ${GREEN}No errors${NC}, ${YELLOW}$WARNS warning(s)${NC}" +else + echo -e " ${GREEN}All checks passed ✓${NC}" +fi +echo "" diff --git a/hooks/session-start.sh b/hooks/session-start.sh index da658fa..4162f7c 100644 --- a/hooks/session-start.sh +++ b/hooks/session-start.sh @@ -4,52 +4,88 @@ # Runs once per session. Zero API calls. Filesystem only. # ============================================================ +# ── Quick health check (filesystem only, no subprocesses) ── +BROKEN=() +for f in CLAUDE.md settings.json agents skills; do + [ ! -e "$HOME/.claude/$f" ] && BROKEN+=("$f") +done + +if [ ${#BROKEN[@]} -gt 0 ]; then + # Try to find the repo path from an existing symlink + _repo_hint="" + for _probe in CLAUDE.md settings.json; do + if [ -L "$HOME/.claude/$_probe" ]; then + _repo_hint="$(cd "$(dirname "$(readlink "$HOME/.claude/$_probe")")" 2>/dev/null && pwd)" + break + fi + done + _fix_cmd="${_repo_hint:+cd $_repo_hint && }bash link.sh" + + echo "" + echo "┌─ ⚠️ CONFIG ISSUES ────────────────────────────────┐" + for b in "${BROKEN[@]}"; do + printf "│ MISSING: ~/.claude/%-30s│\n" "$b" + done + printf "│ → %-47s│\n" "$_fix_cmd" + echo "│ → /health for full diagnostic │" + echo "└───────────────────────────────────────────────────┘" + unset _repo_hint _fix_cmd +fi + +# ── Load shared detection library ── +_lib="$(dirname "${BASH_SOURCE[0]}")/../lib/detect-plugins.sh" +if [ -f "$_lib" ]; then + # shellcheck source=../lib/detect-plugins.sh + source "$_lib" +else + # Fallback: inline detection if lib is missing + detect_gstack() { [ -d "$HOME/.claude/skills/gstack" ]; } + detect_gsd() { ls "$HOME/.claude/skills/" 2>/dev/null | grep -qi "gsd"; } + detect_uiux_pro_max() { ls "$HOME/.claude/plugins/cache/" 2>/dev/null | grep -qi "ui-ux-pro-max"; } + detect_frontend_design() { ls "$HOME/.claude/plugins/cache/" 2>/dev/null | grep -qi "frontend-design"; } + detect_context7() { claude mcp list 2>/dev/null | grep -q "context7"; } +fi +unset _lib + +# ── Toggle plugin detection ── + TOGGLE_ACTIVE=() TOGGLE_INACTIVE=() -# --- GStack --- -if [ -d "$HOME/.claude/skills/gstack" ]; then - TOGGLE_ACTIVE+=("gstack") -else - TOGGLE_INACTIVE+=("gstack") -fi +for plugin in gstack gsd uiux_pro_max frontend_design context7; do + # Map function name to display name + case "$plugin" in + uiux_pro_max) display="ui-ux-pro-max" ;; + frontend_design) display="frontend-design" ;; + *) display="$plugin" ;; + esac -# --- GSD --- -if ls "$HOME/.claude/skills/" 2>/dev/null | grep -qi "gsd"; then - TOGGLE_ACTIVE+=("gsd") -else - TOGGLE_INACTIVE+=("gsd") -fi - -# --- UI/UX Pro Max --- -if ls "$HOME/.claude/plugins/cache/" 2>/dev/null | grep -qi "ui-ux-pro-max"; then - TOGGLE_ACTIVE+=("ui-ux-pro-max") -else - TOGGLE_INACTIVE+=("ui-ux-pro-max") -fi - -# --- frontend-design --- -if ls "$HOME/.claude/plugins/cache/" 2>/dev/null | grep -qi "frontend-design"; then - TOGGLE_ACTIVE+=("frontend-design") -else - TOGGLE_INACTIVE+=("frontend-design") -fi - -# --- Context7 MCP --- -if claude mcp list 2>/dev/null | grep -q "context7"; then - TOGGLE_ACTIVE+=("context7") -else - TOGGLE_INACTIVE+=("context7") -fi + if "detect_$plugin" 2>/dev/null; then + TOGGLE_ACTIVE+=("$display") + else + TOGGLE_INACTIVE+=("$display") + fi +done # --- Format output --- ACTIVE_STR="${TOGGLE_ACTIVE[*]:-none}" INACTIVE_STR="${TOGGLE_INACTIVE[*]:-none}" +# Version detection: follow CLAUDE.md symlink back to repo, then read version.txt +_claude_real="$(readlink "$HOME/.claude/CLAUDE.md" 2>/dev/null || true)" +if [ -n "$_claude_real" ]; then + _repo_dir="$(cd "$(dirname "$_claude_real")" 2>/dev/null && pwd)" + CONFIG_VERSION=$(cat "$_repo_dir/version.txt" 2>/dev/null || echo "?") +else + CONFIG_VERSION="?" +fi +unset _claude_real _repo_dir + echo "" echo "┌─ Toggle plugins ──────────────────────────────────┐" printf "│ 🟢 ON : %-40s│\n" "$ACTIVE_STR" printf "│ ⚫ OFF : %-40s│\n" "$INACTIVE_STR" echo "│ 💡 /plugin-check before starting a new project │" +echo "│ 🩺 /health to run full diagnostic │" echo "└───────────────────────────────────────────────────┘" echo "" diff --git a/install-plugins.sh b/install-plugins.sh index a8b6567..abec3b9 100644 --- a/install-plugins.sh +++ b/install-plugins.sh @@ -16,6 +16,31 @@ err() { echo -e "${RED}✗${NC} $1"; } REPO="$(cd "$(dirname "$0")" && pwd)" +# Log to file for post-mortem debugging (terminal output unchanged) +LOG_FILE="$REPO/install-$(date +%Y%m%d-%H%M%S).log" +exec > >(tee -a "$LOG_FILE") 2>&1 + +# Load shared detection library +# shellcheck source=lib/detect-plugins.sh +source "$REPO/lib/detect-plugins.sh" + +# Read pinned version from plugins.lock.json +# Usage: pinned_version "rtk" → prints version string or "latest" +pinned_version() { + local key="$1" + if [ -f "$REPO/plugins.lock.json" ] && command -v python3 &>/dev/null; then + python3 -c " +import json, sys +with open('$REPO/plugins.lock.json') as f: + d = json.load(f) +v = d.get('$key', {}).get('version', 'latest') +print(v) +" 2>/dev/null || echo "latest" + else + echo "latest" + fi +} + # ============================================================ # DETECT OS # ============================================================ @@ -128,52 +153,9 @@ fi echo "" # ============================================================ -# STEP 2 — GIT CLI TOOLS (gh + glab for /git-pr) +# STEP 2 — GSTACK SUBMODULE # ============================================================ -echo "── Step 2: Git CLI tools ────────────────────────────────────" -echo "" - -# --- gh (GitHub CLI) --- -if command -v gh &>/dev/null; then - ok "gh $(gh --version | head -1 | awk '{print $3}')" -else - info "Installing gh (GitHub CLI)..." - case $OS in - macos) brew install gh ;; - linux-apt) type -p curl > /dev/null || sudo apt-get install -y curl - curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list - sudo apt-get update && sudo apt-get install -y gh ;; - linux-dnf) sudo dnf install -y gh ;; - linux-pacman) sudo pacman -S --noconfirm github-cli ;; - *) warn "Cannot auto-install gh on $OS — install from https://cli.github.com" ;; - esac - command -v gh &>/dev/null && ok "gh installed" || warn "gh not installed — GitHub PRs will use API fallback" -fi - -# --- glab (GitLab CLI) --- -if command -v glab &>/dev/null; then - ok "glab $(glab --version | head -1)" -else - info "Installing glab (GitLab CLI)..." - case $OS in - macos) brew install glab ;; - linux-apt) curl -s https://raw.githubusercontent.com/profclems/glab/trunk/scripts/install.sh | sudo bash ;; - linux-dnf) sudo dnf install -y glab ;; - linux-pacman) sudo pacman -S --noconfirm glab ;; - *) warn "Cannot auto-install glab on $OS — install from https://gitlab.com/gitlab-org/cli" ;; - esac - command -v glab &>/dev/null && ok "glab installed" || warn "glab not installed — GitLab MRs will use API fallback" -fi - -warn "Gogs/Gitea: set GOGS_TOKEN or GITEA_TOKEN in your shell profile" -echo " export GOGS_TOKEN="your-token" # add to ~/.zshrc or ~/.bashrc" -echo "" - -# ============================================================ -# STEP 3 — GSTACK SUBMODULE -# ============================================================ -echo "── Step 3: GStack submodule ─────────────────────────────────" +echo "── Step 2: GStack submodule ─────────────────────────────────" echo "" # Note: GStack is managed as a git submodule in this repo. # It lives at skills-external/gstack/ and is symlinked to ~/.claude/skills/gstack/ @@ -198,10 +180,12 @@ fi if [ -d "$GSTACK_DIR" ]; then info "Running GStack setup..." cd "$GSTACK_DIR" && ./setup && cd - > /dev/null - # Ensure symlink from link.sh is in place - mkdir -p "$HOME/.claude/skills" - ln -sf "$GSTACK_DIR" "$HOME/.claude/skills/gstack" 2>/dev/null || true - ok "GStack ready at ~/.claude/skills/gstack (→ submodule)" + # Symlinks are handled by link.sh — verify it was run + if [ -L "$HOME/.claude/skills/gstack" ]; then + ok "GStack ready (submodule initialized, symlink OK)" + else + warn "GStack submodule ready but not symlinked — run: bash link.sh" + fi else warn "GStack submodule directory not found after init — check .gitmodules" fi @@ -211,13 +195,19 @@ echo "" # ============================================================ # STEP 3 — RTK # ============================================================ -echo "── Step 4: RTK — Rust Token Killer ─────────────────────────" +echo "── Step 3: RTK — Rust Token Killer ─────────────────────────" echo "" if command -v rtk &>/dev/null; then ok "rtk already installed ($(rtk --version 2>/dev/null | head -1))" else - info "Installing RTK..." - cargo install --git https://github.com/rtk-ai/rtk + RTK_VER=$(pinned_version "rtk") + if [ "$RTK_VER" != "latest" ]; then + info "Installing RTK $RTK_VER (pinned in plugins.lock.json)..." + cargo install --git https://github.com/rtk-ai/rtk --tag "$RTK_VER" + else + info "Installing RTK (latest — consider pinning in plugins.lock.json)..." + cargo install --git https://github.com/rtk-ai/rtk + fi fi info "Configuring RTK PreToolUse hook (global)..." rtk init -g --auto-patch @@ -227,10 +217,17 @@ echo "" # ============================================================ # STEP 4 — GSD # ============================================================ -echo "── Step 5: GSD — get-shit-done ─────────────────────────────" +echo "── Step 4: GSD — get-shit-done ─────────────────────────────" echo "" info "Installing GSD globally..." -npx get-shit-done-cc --claude --global --auto +GSD_VER=$(pinned_version "gsd") +if [ "$GSD_VER" != "latest" ]; then + info "Version $GSD_VER (pinned in plugins.lock.json)" + npx "get-shit-done-cc@$GSD_VER" --claude --global --auto +else + info "Version: latest (consider pinning in plugins.lock.json)" + npx get-shit-done-cc --claude --global --auto +fi ok "GSD installed" echo "" @@ -239,7 +236,7 @@ echo "" # ============================================================ # All claude plugin install commands use --scope user to ensure # they install to ~/.claude/plugins/ regardless of working directory. -echo "── Step 6: Marketplace plugins (scope: user) ────────────────" +echo "── Step 5: Marketplace plugins (scope: user) ────────────────" echo "" install_plugin() { @@ -276,7 +273,7 @@ echo "" # ============================================================ # STEP 6 — CONTEXT7 MCP (manual — requires API key) # ============================================================ -echo "── Step 7: Context7 MCP ─────────────────────────────────────" +echo "── Step 6: Context7 MCP ─────────────────────────────────────" echo "" if claude mcp list 2>/dev/null | grep -q "context7"; then ok "Context7 MCP already configured" @@ -316,7 +313,6 @@ echo "" echo " All plugins installed at: user scope (~/.claude/plugins/)" echo " GStack at: ~/.claude/skills/gstack/ (symlink → submodule)" echo "" -echo " → Authenticate: gh auth login (GitHub) / glab auth login (GitLab)" echo " → Restart Claude Code" echo " → Run /reload-plugins" echo "" diff --git a/lib/detect-plugins.sh b/lib/detect-plugins.sh new file mode 100644 index 0000000..5abf35b --- /dev/null +++ b/lib/detect-plugins.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# ============================================================ +# lib/detect-plugins.sh — Single source of truth for plugin detection +# Sourced by: session-start.sh, doctor.sh, install-plugins.sh +# +# Each function returns 0 (detected) or 1 (not detected). +# No output — callers handle messaging. +# ============================================================ + +# --- Always-on plugins --- + +detect_rtk() { + command -v rtk &>/dev/null +} + +detect_superpowers() { + # Fast check: filesystem (plugin cache) + local cache_dir="$HOME/.claude/plugins/cache" + if [ -d "$cache_dir" ]; then + ls "$cache_dir" 2>/dev/null | grep -qi "superpowers" && return 0 + fi + # Slow fallback: CLI (only if fast check fails) + claude plugin list 2>/dev/null | grep -qi "superpowers" && return 0 + return 1 +} + +detect_security_guidance() { + local cache_dir="$HOME/.claude/plugins/cache" + [ -d "$cache_dir" ] && ls "$cache_dir" 2>/dev/null | grep -qi "security-guidance" +} + +detect_skill_creator() { + local cache_dir="$HOME/.claude/plugins/cache" + [ -d "$cache_dir" ] && ls "$cache_dir" 2>/dev/null | grep -qi "skill-creator" +} + +detect_pr_review_toolkit() { + local cache_dir="$HOME/.claude/plugins/cache" + [ -d "$cache_dir" ] && ls "$cache_dir" 2>/dev/null | grep -qi "pr-review-toolkit" +} + +# --- Toggle plugins --- + +detect_gstack() { + [ -d "$HOME/.claude/skills/gstack" ] +} + +detect_gsd() { + ls "$HOME/.claude/skills/" 2>/dev/null | grep -qi "gsd" +} + +detect_frontend_design() { + local cache_dir="$HOME/.claude/plugins/cache" + [ -d "$cache_dir" ] && ls "$cache_dir" 2>/dev/null | grep -qi "frontend-design" +} + +detect_uiux_pro_max() { + local cache_dir="$HOME/.claude/plugins/cache" + [ -d "$cache_dir" ] && ls "$cache_dir" 2>/dev/null | grep -qi "ui-ux-pro-max" +} + +detect_context7() { + claude mcp list 2>/dev/null | grep -q "context7" +} diff --git a/link.sh b/link.sh index 7897587..1b9b38e 100644 --- a/link.sh +++ b/link.sh @@ -9,21 +9,33 @@ CLAUDE="$HOME/.claude" mkdir -p "$CLAUDE" -# Core config files +# Core config files (plain files — ln -sf handles these correctly) ln -sf "$REPO/CLAUDE.md" "$CLAUDE/CLAUDE.md" ln -sf "$REPO/settings.json" "$CLAUDE/settings.json" -# Agents and skills -ln -sf "$REPO/agents" "$CLAUDE/agents" -ln -sf "$REPO/skills" "$CLAUDE/skills" +# Agents and skills — must handle the case where target exists +# as a real directory (ln -sf would create a link INSIDE the dir +# instead of replacing it) +for item in agents skills lib; do + target="$CLAUDE/$item" + if [ -L "$target" ]; then + # Stale symlink from a previous run — remove before recreating + rm -f "$target" + elif [ -d "$target" ]; then + echo "⚠️ ~/.claude/$item is a real directory (not a symlink)." + echo " Rename or remove it, then re-run link.sh." + echo " Skipping $item to avoid data loss." + continue + fi + ln -sf "$REPO/$item" "$target" +done # Hooks mkdir -p "$CLAUDE/hooks" ln -sf "$REPO/hooks/session-start.sh" "$CLAUDE/hooks/session-start.sh" -# GStack (submodule) — symlink from skills-external/ into ~/.claude/skills/ +# GStack (submodule) — symlink into ~/.claude/skills/ (which points to repo/skills/) # The submodule must be initialized first (done by install-plugins.sh) -mkdir -p "$CLAUDE/skills" if [ -d "$REPO/skills-external/gstack" ]; then ln -sf "$REPO/skills-external/gstack" "$CLAUDE/skills/gstack" echo "✅ GStack symlinked from submodule" diff --git a/plugins.lock.json b/plugins.lock.json new file mode 100644 index 0000000..2444a29 --- /dev/null +++ b/plugins.lock.json @@ -0,0 +1,24 @@ +{ + "_readme": "Pinned versions for reproducible installs. Update versions deliberately, then run install-plugins.sh.", + "rtk": { + "source": "https://github.com/rtk-ai/rtk", + "install_cmd": "cargo install --git https://github.com/rtk-ai/rtk --tag {version}", + "version": "v0.34.3", + "note": "Check latest at https://github.com/rtk-ai/rtk/releases before updating" + }, + "gsd": { + "source": "npm:get-shit-done-cc", + "install_cmd": "npx get-shit-done-cc@{version} --claude --global --auto", + "version": "1.30.0", + "note": "Check latest at https://www.npmjs.com/package/get-shit-done-cc before updating" + }, + "gstack": { + "source": "https://github.com/garrytan/gstack.git", + "managed_by": "git submodule", + "note": "Version controlled by submodule pointer in .gitmodules. Update: git submodule update --remote" + }, + "node": { + "minimum": "18", + "recommended": "22" + } +} diff --git a/settings.json b/settings.json index aaf1cff..aad49b8 100644 --- a/settings.json +++ b/settings.json @@ -1,5 +1,5 @@ { - "_readme": "Global user settings \u2014 place at ~/.claude/settings.json. Applies to ALL projects. Never commit this file.", + "_readme": "Global user settings — place at ~/.claude/settings.json. Applies to ALL projects. Never commit this file.", "cleanupPeriodDays": 30, "permissions": { "defaultMode": "default", @@ -53,7 +53,35 @@ "Write(**/.env.*)", "Write(**/secrets/**)", "Write(**/*.pem)", - "Write(**/*.key)" + "Write(**/*.key)", + "Bash(bash -c *)", + "Bash(eval *)", + "Bash(exec *)", + "Bash(find * -delete*)", + "Bash(find * -exec rm*)", + "Bash(find * -execdir rm*)", + "Bash(perl -e *)", + "Bash(ruby -e *)", + "Bash(cat .env)", + "Bash(cat .env.*)", + "Bash(cat */.env)", + "Bash(cat */.env.*)", + "Bash(cat */secrets/*)", + "Bash(cat */*.pem)", + "Bash(cat */*.key)", + "Bash(cat */id_rsa*)", + "Bash(cat */id_ed25519*)", + "Bash(cat */.aws/credentials)", + "Bash(head .env)", + "Bash(head .env.*)", + "Bash(tail .env)", + "Bash(tail .env.*)", + "Bash(less .env)", + "Bash(less .env.*)", + "Bash(more .env)", + "Bash(more .env.*)", + "Bash(grep * .env)", + "Bash(grep * .env.*)" ], "ask": [ "Bash(git push *)", @@ -68,7 +96,10 @@ "Bash(dnf install *)", "Bash(pacman -S *)", "WebSearch", - "WebFetch" + "WebFetch", + "Bash(xargs *)", + "Bash(sed -i *)", + "Bash(sed -i'' *)" ], "allow": [ "Bash(git status)", @@ -112,7 +143,6 @@ "Bash(awk *)", "Bash(sort *)", "Bash(uniq *)", - "Bash(xargs *)", "Bash(tr *)", "Bash(cut *)", "Bash(diff *)", @@ -144,4 +174,4 @@ } ] } -} \ No newline at end of file +} diff --git a/skills/analyze/SKILL.md b/skills/analyze/SKILL.md index cb50d72..e1181d5 100644 --- a/skills/analyze/SKILL.md +++ b/skills/analyze/SKILL.md @@ -1,7 +1,9 @@ --- name: analyze -description: Analyze code or a codebase deeply before any modification +description: Deep factual code analysis — read-only, no solutions proposed argument-hint: +disable-model-invocation: true +allowed-tools: Read, Grep, Glob, Bash --- Load and follow strictly: diff --git a/skills/git-pr/SKILL.md b/skills/git-pr/SKILL.md deleted file mode 100644 index 96aaefc..0000000 --- a/skills/git-pr/SKILL.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: git-pr -description: Analyze all changes on the current branch since it diverged from base (retroactive across sessions), create logical commits, push, and open a draft PR/MR. Works with GitHub, GitLab, Gogs, and Gitea. Never merges — creates a draft for user validation. -argument-hint: [PR title or leave empty for auto-detection] -disable-model-invocation: true -allowed-tools: Read, Bash, Grep, Glob ---- - -Load and follow strictly: -- .claude/agents/git-workflow.md - -Execute the GIT WORKFLOW on the current repository. - -User context (optional title or instructions): -$ARGUMENTS diff --git a/skills/health/SKILL.md b/skills/health/SKILL.md new file mode 100644 index 0000000..141224d --- /dev/null +++ b/skills/health/SKILL.md @@ -0,0 +1,18 @@ +--- +name: health +description: Run setup diagnostic — check symlinks, plugins, permissions, token budget +argument-hint: (no arguments needed) +disable-model-invocation: true +allowed-tools: Bash +--- + +Run the health check script and report findings to the user: + +```bash +bash ~/.claude/doctor.sh +``` + +After displaying the output: +- If errors are found, suggest the specific fix commands shown in the output. +- If only warnings, note them but confirm the setup is functional. +- If all checks pass, confirm the setup is healthy. diff --git a/skills/init-project/SKILL.md b/skills/init-project/SKILL.md index 6d062f5..d12b80a 100644 --- a/skills/init-project/SKILL.md +++ b/skills/init-project/SKILL.md @@ -1,6 +1,6 @@ --- name: init-project -description: Initialize a complete project from scratch. Plugin check → interview → analyze → design → validate → scaffold skeleton → plan v1 features → validate plan → implement (TDD, subagents) → analyze → review → finish. Same implementation rigor as ship-feature. +description: Full project init: interview → design → scaffold → implement (TDD). Two validation gates. argument-hint: disable-model-invocation: true allowed-tools: Read, Write, Edit, Bash, Grep, Glob @@ -35,42 +35,7 @@ $ARGUMENTS --- -### STEP 0a — BRANCH SETUP - -Load the BRANCH SETUP section from: `.claude/agents/git-workflow.md` - -Before anything else, ensure we are NOT on a protected branch. - -```bash -git branch --show-current -``` - -**If on `main`, `master`, `develop`, or any protected branch:** - -Derive a branch slug from the initial request: -- Take the first 3–4 meaningful words -- Lowercase, hyphen-separated -- Max 50 chars - -```bash -git fetch origin -git pull origin --ff-only 2>/dev/null || true -git checkout -b feature/ -``` - -Print: `✅ Working branch created: feature/` - -**If already on a feature branch:** -Run the CONFLICT-SAFE REBASE procedure from git-workflow.md -to sync with main before starting. - -Print: `✅ Branch: (synced with main)` - -**Do not proceed until the branch is clean and ready.** - ---- - -### STEP 0b — PLUGIN CHECK +### STEP 0 — PLUGIN CHECK Load and follow: `.claude/agents/plugin-advisor.md` @@ -88,7 +53,7 @@ B) Type "force" to proceed without them ================================================================ ``` **STOP. Wait for user response.** -- Re-run → restart from STEP 0b +- Re-run → restart from STEP 0 - "force" → note missing plugins, continue to STEP 1 **If `ACTION REQUIRED: NO`:** @@ -341,8 +306,7 @@ SYNC mode — no stop required. The readme-updater: ## RULES -- Never skip STEP 0a — branch setup is mandatory. Never commit on main/master. -- Never skip STEP 0b — plugin check is mandatory. +- Never skip STEP 0 — plugin check is mandatory. - Never skip STEP 1 — no assumptions about missing info. - Never implement without explicit user approval at STEP 4. - Never implement without explicit user approval at STEP 7. diff --git a/skills/plugin-check/SKILL.md b/skills/plugin-check/SKILL.md index 2317af4..3604814 100644 --- a/skills/plugin-check/SKILL.md +++ b/skills/plugin-check/SKILL.md @@ -1,6 +1,6 @@ --- name: plugin-check -description: Check active plugins vs current project needs. Recommends enabling or disabling based on context signals (frontend, design, QA, deployment, multi-session, fast-evolving libs). Run before init-project or ship-feature on a new project type. +description: Audit active plugins vs project needs. Recommends enable/disable actions. argument-hint: [project description or feature to build] disable-model-invocation: true allowed-tools: Read, Bash, Glob, Grep diff --git a/skills/readme/SKILL.md b/skills/readme/SKILL.md index a7b54aa..dd7332e 100644 --- a/skills/readme/SKILL.md +++ b/skills/readme/SKILL.md @@ -1,6 +1,6 @@ --- name: readme -description: Update the project README to reflect the current state of the codebase. Audits what is outdated, missing, or no longer accurate, then applies surgical updates. Preserves existing structure and style. +description: README audit — detect outdated sections, apply surgical updates argument-hint: [what changed, feature name, or leave empty for full audit] disable-model-invocation: true allowed-tools: Read, Write, Edit, Bash, Glob, Grep diff --git a/skills/refactor/SKILL.md b/skills/refactor/SKILL.md index 6d79c80..f11fe73 100644 --- a/skills/refactor/SKILL.md +++ b/skills/refactor/SKILL.md @@ -1,7 +1,9 @@ --- name: refactor -description: Improve code quality without changing behavior +description: Improve code quality without changing behavior — strict norm enforcement argument-hint: +disable-model-invocation: true +allowed-tools: Read, Write, Edit, Grep, Glob, Bash --- Load and follow strictly: diff --git a/skills/ship-feature/SKILL.md b/skills/ship-feature/SKILL.md index 4e4e34c..4a4c931 100644 --- a/skills/ship-feature/SKILL.md +++ b/skills/ship-feature/SKILL.md @@ -1,6 +1,6 @@ --- name: ship-feature -description: Ship a feature end-to-end using the Superpowers workflow. Starts with a plugin check, then Brainstorm → Plan → Implement (subagent-driven) → Review → Finish branch. +description: Ship feature end-to-end: design → plan → implement (TDD) → review → finish argument-hint: disable-model-invocation: true allowed-tools: Read, Write, Edit, Bash, Grep, Glob @@ -33,48 +33,7 @@ $ARGUMENTS --- -### STEP 0a — BRANCH SETUP - -Load the BRANCH SETUP section from: `.claude/agents/git-workflow.md` - -```bash -git branch --show-current -``` - -**If on `main`, `master`, `develop`, or any protected branch:** - -Derive a branch slug from the feature request: -- Take the first 3–4 meaningful words from $ARGUMENTS -- Lowercase, hyphen-separated, max 50 chars -- Prefix with `feature/` - -```bash -git fetch origin -git pull origin --ff-only 2>/dev/null || true -git checkout -b feature/ -``` - -Print: `✅ Working branch created: feature/` - -**If already on a feature/bugfix/hotfix branch:** -Run the CONFLICT-SAFE REBASE procedure from git-workflow.md -to sync with the base branch before implementing. - -Print: `✅ Branch: (synced)` - -**Special case — bugfix on a feature branch:** -If the user explicitly says "bugfix" or "fix" in the request AND -the current branch is a feature branch: -```bash -git checkout -b bugfix/ -``` -Creates the bugfix branch FROM the feature branch — correct hierarchy. - -**Do not proceed until the branch is clean.** - ---- - -### STEP 0b — PLUGIN CHECK (mandatory gate) +### STEP 0 — PLUGIN CHECK (mandatory gate) Load and follow: `.claude/agents/plugin-advisor.md` @@ -105,7 +64,7 @@ Options: ``` Wait for user response. -- If user re-runs `/ship-feature` → start from STEP 0a again +- If user re-runs `/ship-feature` → start from STEP 0 again - If user types "force" → note missing plugins and continue to STEP 1 **If the advisor output says `ACTION REQUIRED: NO`:** @@ -198,40 +157,9 @@ SYNC mode — no stop required. The readme-updater: --- -### STEP 9 — CREATE PR (optional gate) - -Ask the user: -``` -================================================================ -SHIP FEATURE — PR CREATION -================================================================ -Feature is implemented, tested, and README is synced. - -Create a PR/MR now? - yes → run /git-pr and open a draft PR - no → stop here, you can run /git-pr manually later -================================================================ -``` - -**STOP — wait for user response.** - -IF yes: - Load and follow: `.claude/agents/git-workflow.md` - The git-workflow agent will: - - Show all changes since branch start (retroactive) - - Propose a commit plan for approval - - Push and create a draft PR/MR on GitHub/GitLab/Gogs/Gitea - -IF no: - Print: `✅ Feature shipped. Run /git-pr when ready to open a PR.` - Stop. - ---- - ## RULES -- Never skip STEP 0a — branch setup is mandatory. Never implement on main/master. -- Never skip STEP 0b — plugin check is mandatory. +- Never skip STEP 0 — plugin check is mandatory. - Never skip brainstorming. - Never implement without explicit user approval of the plan. - Keep subagents isolated — no shared context between tasks. diff --git a/update-all.sh b/update-all.sh new file mode 100644 index 0000000..cf07690 --- /dev/null +++ b/update-all.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# ============================================================ +# Claude Code — Update all components +# Pulls latest config, updates submodules, refreshes symlinks, +# and runs doctor to verify. +# ============================================================ +set -euo pipefail + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m' +ok() { echo -e "${GREEN}✓${NC} $1"; } +warn() { echo -e "${YELLOW}⚠${NC} $1"; } +info() { echo -e "${BLUE}→${NC} $1"; } + +REPO="$(cd "$(dirname "$0")" && pwd)" +VERSION=$(cat "$REPO/version.txt" 2>/dev/null || echo "unknown") + +echo "" +echo "═══ claude-config update (v${VERSION}) ═══" +echo "" + +# ── 1. Pull latest config ── +echo "── Pulling latest config..." +cd "$REPO" +if git pull --rebase 2>/dev/null; then + ok "Config repo updated" +else + warn "git pull failed — check for uncommitted changes" +fi + +# ── 2. Update GStack submodule ── +echo "" +echo "── Updating GStack submodule..." +if git submodule update --remote skills-external/gstack 2>/dev/null; then + if [ -d "skills-external/gstack" ]; then + cd skills-external/gstack && ./setup 2>/dev/null && cd "$REPO" + ok "GStack updated" + fi +else + warn "GStack submodule update failed — run: git submodule update --init" +fi + +# ── 3. Update RTK (if pinned version available) ── +echo "" +echo "── Updating RTK..." +if command -v cargo &>/dev/null; then + RTK_VERSION="" + if [ -f "$REPO/plugins.lock.json" ] && command -v python3 &>/dev/null; then + RTK_VERSION=$(python3 -c " +import json +with open('$REPO/plugins.lock.json') as f: + d = json.load(f) +print(d.get('rtk',{}).get('version','')) +" 2>/dev/null || true) + fi + + if [ -n "$RTK_VERSION" ] && [ "$RTK_VERSION" != "latest" ]; then + info "Pinned version: $RTK_VERSION" + cargo install --git https://github.com/rtk-ai/rtk --tag "$RTK_VERSION" --force 2>/dev/null \ + && ok "RTK updated to $RTK_VERSION" \ + || warn "RTK update failed" + else + info "No pinned version — installing latest" + cargo install --git https://github.com/rtk-ai/rtk --force 2>/dev/null \ + && ok "RTK updated (latest)" \ + || warn "RTK update failed" + fi +else + warn "Cargo not available — skipping RTK" +fi + +# ── 4. Refresh symlinks ── +echo "" +echo "── Refreshing symlinks..." +bash "$REPO/link.sh" + +# ── 5. Run doctor ── +echo "" +bash "$REPO/doctor.sh" diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +1.0.0