diff --git a/README.md b/README.md index 290d84d..57debd2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# hub-guide — Comprehensive Programming Best-Practice Plugin +# code-guide — Comprehensive Programming Best-Practice Plugin A Claude Code plugin serving as a complete engineering guide for **all programming situations** — monorepo, standalone, any language, any framework. ## Features -### 🧠 24 Best-Practice Skills +### 24 Best-Practice Skills | Category | Skills | |----------|--------| @@ -13,27 +13,21 @@ A Claude Code plugin serving as a complete engineering guide for **all programmi | **Frameworks** | react-frontend, elysiajs, hono-backend, drizzle-database, nextjs | | **Infrastructure** | docker, ci-cd, monitoring | | **Monorepo** | monorepo (patterns + submodules + workspace tooling) | -| **Hub-specific** | hub-guide (Asepharyana Hub monorepo infra & workflow) | Skills activate automatically when Claude detects relevant context (language, framework, topic). -### ⚡ Hooks for Auto-Detection +### Hooks for Auto-Activation | Hook | Trigger | What It Does | |------|---------|--------------| -| **SessionStart** | Session begins | Detects project type from files (package.json, Cargo.toml, go.mod, etc.) and activates relevant skills | -| **PreToolUse** | Before Write/Edit | Detects file extension and injects language-specific best-practice rules | +| **Stop** | Before stopping | Verifies mandatory skills are being applied (engineering-principles, clean-code, clean-architecture, testing, error-handling, security, git-workflow, api-design) | ## Installation ### Quick Install ```bash -# Clone -git clone https://github.com/asepharyana/asepharyana-hub-hub-guide.git -cd asepharyana-hub-hub-guide - -# Install as one unit (recommended) +# From anywhere with access to the plugin directory ./install.sh # Or symlink (edits in this repo are live) @@ -49,37 +43,19 @@ Example triggers: - *"Write a test for this"* → `testing` activates - *"Design an API endpoint"* → `api-design` activates - Working with `.ts` files → `typescript` activates -- Project with `Cargo.toml` → `rust` activates (SessionStart hook) - -## Auto-Detection (Hooks) - -When you start a session in a project, the SessionStart hook scans for: - -| File | Skills Activated | -|------|-----------------| -| `tsconfig.json` | typescript, react-frontend, elysiajs, hono-backend, drizzle-database | -| `Cargo.toml` | rust | -| `go.mod` | go | -| `pyproject.toml` | python | -| `pnpm-workspace.yaml` | monorepo | -| `Dockerfile` | docker | -| `.github/workflows/` | ci-cd | +- Project with `Cargo.toml` → `rust` activates ## Structure ``` -hub-guide/ +code-guide/ ├── .claude-plugin/ -│ └── plugin.json # Plugin manifest +│ └── plugin.json # Plugin manifest ├── hooks/ -│ ├── hooks.json # Hook configuration -│ └── scripts/ -│ ├── detect-project.sh # SessionStart auto-detection -│ └── detect-file-type.sh # PreToolWrite language detection +│ └── hooks.json # Hook configuration (Stop prompt) ├── skills/ -│ ├── clean-code/ # +23 more skill directories -│ ├── ... -│ └── hub-guide/ # Existing hub monorepo guide +│ ├── clean-code/ # 23 more skill directories +│ └── ... └── README.md ``` @@ -87,11 +63,7 @@ hub-guide/ Skills are in `skills//SKILL.md` format (modern Claude Code plugin convention). Each skill includes: -- **Frontmatter** — `name` and `description` with specific trigger phrases -- **Lean body** — key rules, examples, and anti-patterns (~1500-2000 words) +- **Frontmatter** — `name` and `description` with trigger context +- **Lean body** — key rules, examples, and anti-patterns -Hooks use bash scripts. Edits to hooks/scripts/ take effect immediately when installed as a symlink. - -## Related - -Based on patterns from [kana-best-practice-engineering](https://github.com/asepharyana/kana-best-practice-engineering). +The Stop hook injects reminders when mandatory skills aren't being applied. diff --git a/install.ps1 b/install.ps1 index f4456ba..f44abe4 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1,82 +1,33 @@ -# install.ps1 — hub-guide plugin installer for Claude Code (Windows) +# install.ps1 — code-guide plugin installer for Claude Code (Windows) # # Usage: -# .\install.ps1 # Install to %USERPROFILE%\.claude\plugins\ -# .\install.ps1 -Project # Install to .claude\plugins\ (project-scoped) -# .\install.ps1 -Link # Symlink (PowerShell Admin/Dev mode) -# .\install.ps1 -NoHooks # Skills only -# .\install.ps1 clean-code testing # Install specific skills +# .\install.ps1 # Install to %USERPROFILE%\.claude\skills\ +# .\install.ps1 -Link # Symlink (PowerShell Admin/Dev mode) param( - [switch]$Project, - [switch]$Link, - [switch]$NoHooks, - [Parameter(Position=0, ValueFromRemainingArguments=$true)] - [string[]]$SkillFilter + [switch]$Link ) -$PluginName = "hub-guide" +$PluginName = "code-guide" $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path - -# Determine target -if ($Project) { - $TargetDir = Join-Path (Get-Location) ".claude\plugins" -} else { - $TargetDir = Join-Path $env:USERPROFILE ".claude\plugins" -} - +$TargetDir = Join-Path $env:USERPROFILE ".claude\skills" $PluginDir = Join-Path $TargetDir $PluginName -Write-Host "📐 hub-guide installer" -ForegroundColor Cyan +Write-Host "code-guide installer" -ForegroundColor Cyan -# Ensure target exists -New-Item -ItemType Directory -Force -Path $PluginDir | Out-Null +New-Item -ItemType Directory -Force -Path $TargetDir | Out-Null -# Install manifest -Copy-Item -Path (Join-Path $ScriptDir ".claude-plugin") -Destination $PluginDir -Recurse -Force - -# Install hooks -if (-not $NoHooks) { - Write-Host " hooks/ → $PluginDir\hooks\" - if ($Link) { - New-Item -ItemType SymbolicLink -Path "$PluginDir\hooks" -Target (Join-Path $ScriptDir "hooks") -Force | Out-Null - } else { - Copy-Item -Path (Join-Path $ScriptDir "hooks") -Destination $PluginDir -Recurse -Force - } -} - -# Install skills -if ($SkillFilter.Count -gt 0) { - $SkillDir = Join-Path $PluginDir "skills" - New-Item -ItemType Directory -Force -Path $SkillDir | Out-Null - foreach ($skill in $SkillFilter) { - $skillName = Split-Path $skill -Leaf - $src = Join-Path $ScriptDir "skills" $skillName - if (Test-Path $src) { - Write-Host " skills/$skillName/ → $SkillDir" - if ($Link) { - New-Item -ItemType SymbolicLink -Path (Join-Path $SkillDir $skillName) -Target $src -Force | Out-Null - } else { - Copy-Item -Path $src -Destination $SkillDir -Recurse -Force - } - } else { - Write-Host " ⚠️ Skill '$skillName' not found at $src" -ForegroundColor Yellow - } - } -} else { - Write-Host " skills/ (all) → $PluginDir\skills\" - if ($Link) { - New-Item -ItemType SymbolicLink -Path (Join-Path $PluginDir "skills") -Target (Join-Path $ScriptDir "skills") -Force | Out-Null - } else { - Copy-Item -Path (Join-Path $ScriptDir "skills") -Destination $PluginDir -Recurse -Force - } -} - -Write-Host "" -Write-Host "✅ hub-guide installed to $PluginDir" -ForegroundColor Green if ($Link) { - Write-Host " (symlink — edits in this repo are live)" + Write-Host " symlink mode" + Remove-Item -Path $PluginDir -Recurse -Force -ErrorAction SilentlyContinue + New-Item -ItemType SymbolicLink -Path $PluginDir -Target $ScriptDir -Force | Out-Null + Write-Host " (symlink — edits in this repo are live)" +} else { + Write-Host " copy mode" + Remove-Item -Path $PluginDir -Recurse -Force -ErrorAction SilentlyContinue + Copy-Item -Path $ScriptDir -Destination $PluginDir -Recurse -Force } + Write-Host "" -Write-Host " Restart Claude Code or run /reload to activate." -Write-Host " Skills auto-trigger when you work — no commands needed." +Write-Host "Done. Restart Claude Code or run /reload." +Write-Host "Skills auto-trigger. Hooks auto-load from hooks/hooks.json." diff --git a/install.sh b/install.sh index d5b588f..68ac74b 100755 --- a/install.sh +++ b/install.sh @@ -1,9 +1,9 @@ #!/bin/bash -# install.sh — hub-guide installer for Claude Code -# Installs the entire hub-guide directory as one unit into ~/.claude/skills/. +# install.sh — code-guide installer for Claude Code +# Installs the entire code-guide directory as one unit into ~/.claude/skills/. # Skills auto-discover, hooks auto-load from hooks/hooks.json. # Usage: -# ./install.sh # Copy hub-guide to ~/.claude/skills/ +# ./install.sh # Copy code-guide to ~/.claude/skills/ # ./install.sh --link # Symlink (edits live) # Requires: Claude Code @@ -25,7 +25,7 @@ echo " target: ${SKILLS_DIR}/code-guide/" echo " mode: $([ "$LINK_MODE" = true ] && echo 'symlink' || echo 'copy')" mkdir -p "$SKILLS_DIR" -DST="${SKILLS_DIR}/hub-guide" +DST="${SKILLS_DIR}/code-guide" if [ "$LINK_MODE" = true ]; then rm -rf "$DST" diff --git a/setup-hooks.sh b/setup-hooks.sh index 6280dac..c1c1cb5 100755 --- a/setup-hooks.sh +++ b/setup-hooks.sh @@ -1,7 +1,7 @@ #!/bin/bash -# setup-hooks.sh — Configure hub-guide hooks in Claude Code +# setup-hooks.sh — Configure code-guide hooks in Claude Code # -# Adds hub-guide hooks to ~/.claude/settings.local.json +# Adds code-guide hooks to ~/.claude/settings.local.json # This allows Claude to auto-detect your project and suggest relevant skills. # # Usage: ./setup-hooks.sh @@ -11,7 +11,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SETTINGS_FILE="${HOME}/.claude/settings.local.json" -echo "📐 hub-guide hooks setup" +echo "code-guide hooks setup" python3 << PYEOF import json, os @@ -51,8 +51,8 @@ os.makedirs(os.path.dirname(settings_file), exist_ok=True) with open(settings_file, 'w') as f: json.dump(cfg, f, indent=2) -print(f"✅ Hooks configured in {settings_file}") +print(f"Hooks configured in {settings_file}") PYEOF echo "" -echo " Restart Claude Code or run /reload to activate hooks." +echo "Restart Claude Code or run /reload to activate hooks." diff --git a/skills/ci-cd/SKILL.md b/skills/ci-cd/SKILL.md index bb69c89..dd16727 100644 --- a/skills/ci-cd/SKILL.md +++ b/skills/ci-cd/SKILL.md @@ -55,16 +55,16 @@ jobs: build: strategy: matrix: - service: [scraper, hub] + service: [frontend, api] steps: - uses: actions/checkout@v4 - run: | docker build \ -f infra/docker/${{ matrix.service }}.Dockerfile \ - -t ghcr.io/.../${{ matrix.service }}:sha-${{ github.sha }} \ - -t ghcr.io/.../${{ matrix.service }}:latest \ + -t ghcr.io/myorg/myproject/${{ matrix.service }}:sha-${{ github.sha }} \ + -t ghcr.io/myorg/myproject/${{ matrix.service }}:latest \ . - - run: docker push --all-tags ghcr.io/.../${{ matrix.service }} + - run: docker push --all-tags ghcr.io/myorg/myproject/${{ matrix.service }} ``` **Deploy (after build)** @@ -101,7 +101,7 @@ jobs: build: strategy: matrix: - service: [scraper, hub, api] + service: [frontend, api, worker] fail-fast: false # Let others complete even if one fails ``` @@ -149,10 +149,10 @@ secrets: 6. **Security scan** (<5 min) — CodeQL, dependency audit, secret scan 7. **Deploy** (<2 min) — SSH, pull, restart -## Deployment (this repo's pattern) +## Deployment Pattern -1. SSH to VPS (`orangevps`) -2. Pull latest images from GHCR +1. SSH to deployment target +2. Pull latest images from container registry 3. Restart specific container (not all) 4. Health check after restart 5. Rollback if health check fails diff --git a/skills/docker/SKILL.md b/skills/docker/SKILL.md index b66c04e..d0ada0e 100644 --- a/skills/docker/SKILL.md +++ b/skills/docker/SKILL.md @@ -76,12 +76,11 @@ networks: external: true ``` -### Typical Services Layout (this repo) -- **Redis:** `shared.yml` — always first -- **NATS:** `nats.yml` — JetStream-enabled -- **Dapr:** `dapr.yml` — placement service -- **Traefik:** `traefik.yml` — reverse proxy -- **App + Sidecar:** `app.yml` — service + daprd sidecar +### Typical Docker Compose Order +- **Data layer:** `db.yml`, `redis.yml` — stateful services first +- **Messaging:** `nats.yml`, `rabbitmq.yml` — message brokers +- **Infrastructure:** `traefik.yml`, `nginx.yml` — reverse proxy +- **Application:** `app.yml` — service containers ## Security @@ -99,17 +98,16 @@ networks: sha- # Immutable — for deterministic rollbacks latest # Mutable — convenience -# Example (from this repo's CI) -ghcr.io/asepharyana/asepharyana-hub/:sha-a1b2c3d -ghcr.io/asepharyana/asepharyana-hub/:latest +# Example +ghcr.io/myorg/myproject/:sha-a1b2c3d +ghcr.io/myorg/myproject/:latest ``` ## Networking -- **All containers** join `app-shared-net` (external Docker bridge). +- **All containers** join the same Docker network (external bridge). - **DNS resolution** via Docker DNS (container name = hostname). -- **Cross-VPS** via Tailscale (`100.64.0.0/10`). -- **Expose only needed ports** — Traefik handles external traffic on port 443. +- **Expose only needed ports** — reverse proxy handles external traffic on port 443. ## Debugging diff --git a/skills/engineering-principles/SKILL.md b/skills/engineering-principles/SKILL.md index 2a21e7e..472981d 100644 --- a/skills/engineering-principles/SKILL.md +++ b/skills/engineering-principles/SKILL.md @@ -312,4 +312,4 @@ Never guess, speculate, or assume. **Every claim, suggestion, or piece of code y ✅ "I searched for .env and didn't find one. There's a .env.example — maybe that's the template. Could you check?" ❌ "Dockerfiles are usually in the root" -✅ "I found the Dockerfile at: infra/docker/hub.Dockerfile" +✅ "I found the Dockerfile at: infra/docker/app.Dockerfile" diff --git a/skills/hub-guide/SKILL.md b/skills/hub-guide/SKILL.md deleted file mode 100644 index f435d92..0000000 --- a/skills/hub-guide/SKILL.md +++ /dev/null @@ -1,107 +0,0 @@ ---- -name: hub-guide -description: Guide for the Asepharyana Hub monorepo — submodule workflow, infrastructure stack (Traefik, Dapr, NATS, Redis), CI/CD pipelines, adding new services, and debugging tips. Use when working in the asepharyana-hub monorepo, managing submodules, dealing with Docker/infra setup. Detects from code context and project files — not dependent on specific language keywords." ---- - -# Hub Guide — Asepharyana Hub Monorepo - -## Submodule Workflow - -- Code changes go in the submodule repo, not here. The hub monorepo only tracks submodule pointers. -- After pushing changes to a submodule repo, update the pointer here: - ```bash - cd apps/ && git checkout main && git pull - cd ../.. && git add apps/ && git commit -m "chore(deps): update submodule" - ``` -- CI/CD auto-updates submodule pointers via `repository_dispatch`. Manual updates are fine for dev. - -### Typical Submodule State - -| State | Meaning | -|-------|---------| -| `(HEAD)` | Detached HEAD — submodule is at the committed pointer | -| `(main)` | On the default branch — you've done `cd apps/name && git checkout main` | -| Dirty | Uncommitted changes inside submodule | - -To reset a submodule to its committed pointer: -```bash -git submodule update --init --recursive apps/ -``` - -## Development Quickstart - -```bash -make init-submodules # After fresh clone — fetches all submodules -make dev # Start Redis for local dev -docker compose -f infra/compose/shared.yml up -d # Full infra stack -``` - -## Local vs Production - -| Aspect | Local | Production (VPS) | -|--------|-------|------------------| -| DB | None (or local) | PostgreSQL on `imrnes` via Tailscale | -| Redis | `make dev` | Container on `orangevps` | -| Traefik | Not running | TLS-terminated on `orangevps` | -| DNS | `localhost` | `*.asepharyana.my.id`, `*.asepharya.web.id` | - -## Debugging Tips - -### Docker compose validation -```bash -for f in infra/compose/*.yml; do docker compose -f "$f" config >/dev/null && echo "OK $f"; done -``` - -### YAML syntax check -```bash -python -c "import pathlib, yaml; [yaml.safe_load(open(p)) for p in pathlib.Path('infra').rglob('*.yml')]" -``` - -### Check submodule pointers -```bash -git submodule status - # Leading `-` = not initialized, `+` = different from committed hash, ` ` = matches -``` - -### Traefik route not working? -1. Check `infra/traefik/dynamic/apps.yaml` — router rule + service definition present? -2. Container labels in compose file include Traefik config? -3. Container on `app-shared-net`? - -## Adding a New Service — Checklist - -1. [ ] Create separate repo for app code -2. [ ] `git submodule add apps/` -3. [ ] Create Dockerfile in `infra/docker/` -4. [ ] Create compose file in `infra/compose/` (app + Dapr sidecar) -5. [ ] Add Traefik router in `infra/traefik/dynamic/apps.yaml` -6. [ ] Add build job in `.github/workflows/docker-build-push.yml` -7. [ ] Verify: `docker compose -f infra/compose/.yml config` - -See `docs/add-new-app.md` for full guide. - -## Monitoring - -- **Dashboard**: `/dashboard` on the hub site (auto-refresh 15s) -- **Dashboard API**: `/api/dashboard` — JSON with containers, traces, metrics -- **Prometheus**: Auto-discovers containers with `prometheus.io/scrape=true` label via Docker SD -- **Jaeger**: Traces via OTLP — check for cross-service latency - -## Infrastructure Files Map - -| Path | Purpose | -|------|---------| -| `infra/compose/*.yml` | One Docker Compose file per service | -| `infra/dapr/components/` | Dapr pub/sub, state store component configs | -| `infra/docker/*.Dockerfile` | Build files per service | -| `infra/traefik/dynamic/apps.yaml` | Traefik route definitions | -| `infra/traefik/traefik.yml` | Traefik static config (entrypoints, providers) | -| `.github/workflows/` | CI/CD pipelines | -| `docs/` | ADRs, deployment guide, new-app guide | - -## Git Hook Scripts - -Located in `scripts/`: -- `scripts/cleanup.sh` — prune old Docker images, clean temp files -- `scripts/update-deps.sh` — bump dependencies across submodules -- `scripts/setup-hooks.sh` — install local git hooks diff --git a/skills/monorepo/SKILL.md b/skills/monorepo/SKILL.md index 7010bb6..98c4d71 100644 --- a/skills/monorepo/SKILL.md +++ b/skills/monorepo/SKILL.md @@ -19,8 +19,8 @@ description: Monorepo best practices — tooling, workspace configuration, share ``` ├── apps/ -│ ├── hub/ # Next.js app (submodule) -│ └── scraper/ # Rust API (submodule) +│ ├── app1/ # Application (submodule) +│ └── app2/ # Another application (submodule) ├── packages/ # Shared libraries (when not submodules) ├── infra/ # Shared infra config ├── pnpm-workspace.yaml @@ -61,13 +61,13 @@ pnpm -r run build - **Explicit `dependencies`** — never rely on hoisting. - **Lock file** (`pnpm-lock.yaml`) committed — immutable installs. -## Git Submodules (this repo's pattern) +## Git Submodules ``` -asepharyana-hub/ +my-monorepo/ ├── apps/ -│ ├── hub/ → asepharyana/asepharyana-hub-hub -│ └── scraper/ → asepharyana/asepharyana-hub-scraper +│ ├── app1/ → org/app1-repo +│ └── app2/ → org/app2-repo ``` ### Submodule Workflow @@ -79,8 +79,8 @@ git submodule update --init --recursive git submodule foreach git pull origin main # Update one submodule -cd apps/hub && git checkout main && git pull -cd ../.. && git add apps/hub && git commit -m "chore(deps): update hub submodule" +cd apps/app1 && git checkout main && git pull +cd ../.. && git add apps/app1 && git commit -m "chore(deps): update app1 submodule" git push ``` @@ -107,8 +107,8 @@ on: push: branches: [main] paths: - - 'apps/hub/**' - - 'infra/docker/hub.Dockerfile' + - 'apps/app1/**' + - 'infra/docker/app1.Dockerfile' ``` ### Affected Commands (Nx/Turborepo/Moon)