diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index f6d83b7..098e7f9 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -25,6 +25,8 @@ uv run mcp_server.py --http # start MCP server (HTTP :8000) - Database (data/gps.db) is always opened read-only - Use uv (not pip). Use ruff (enforced by pre-commit). - `scripts/test.sh` is bash — run directly, not via `uv run` +- Use `bd` (beads) for all task tracking — not TodoWrite or TaskCreate +- Pre-commit runs: ruff, ruff-format, no-secrets check, protected-files check ## More Info See [BOOKMARKS.md](BOOKMARKS.md) for deployment docs, schema reference, and ADRs. diff --git a/.claude/hooks/guard-protected-files.sh b/.claude/hooks/guard-protected-files.sh new file mode 100755 index 0000000..027afea --- /dev/null +++ b/.claude/hooks/guard-protected-files.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# Block Claude from editing governance/ files at edit-time (pre-commit catches at commit-time) +FILE="$TOOL_INPUT_FILE_PATH" +if [[ -z "$FILE" ]]; then + FILE=$(echo "$TOOL_INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('file_path',''))" 2>/dev/null) +fi + +if [[ -z "$FILE" ]]; then + echo "ERROR: Could not determine target file path" >&2 + exit 1 +fi + +case "$FILE" in + governance/*|*/governance/*) + echo "BLOCKED: governance/ files are protected." + exit 2 + ;; +esac diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..7496f50 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,23 @@ +{ + "permissions": { + "allow": [ + "Bash(uv run *)", + "Bash(scripts/test.sh*)", + "Bash(ruff *)", + "Bash(git *)" + ] + }, + "hooks": { + "PreToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "bash .claude/hooks/guard-protected-files.sh" + } + ] + } + ] + } +} diff --git a/.claude/skills/deploy-gps/SKILL.md b/.claude/skills/deploy-gps/SKILL.md new file mode 100644 index 0000000..24c75a2 --- /dev/null +++ b/.claude/skills/deploy-gps/SKILL.md @@ -0,0 +1,20 @@ +--- +description: Build, test, and deploy GPS MCP server to OpenShift +--- + +# Deploy GPS + +Run the full deploy pipeline for the GPS MCP server. + +## Steps + +1. Verify clean git state: `git status --porcelain` must be empty +2. Run test suite: `scripts/test.sh` +3. If tests pass, run: `deploy/deploy.sh all --overlay openshift` +4. After deploy completes, verify health endpoint: `curl -sf $(oc get route gps-mcp-server -n gps-mcp-server -o jsonpath='{.spec.host}')/health` +5. Report: version deployed, pod status, health check result + +## Pre-conditions +- Must be on main branch or have explicit approval +- `oc` must be logged in (`oc whoami` succeeds) +- Docker must be running diff --git a/.claude/skills/refresh-data/SKILL.md b/.claude/skills/refresh-data/SKILL.md new file mode 100644 index 0000000..f6394de --- /dev/null +++ b/.claude/skills/refresh-data/SKILL.md @@ -0,0 +1,18 @@ +--- +description: Refresh all GPS data sources and rebuild the database +--- + +# Refresh GPS Data + +Rebuild all data sources for the GPS MCP server. + +## Steps + +1. Run these in parallel (they are independent): + - `uv run scripts/fetch_pricing.py` + - `uv run scripts/fetch_github.py --org ` (ask user for org if not obvious) + - `uv run scripts/refresh_catalog.py --refresh` +2. After all fetches complete, rebuild the main DB: `uv run scripts/build_db.py --force` +3. Run test suite: `scripts/test.sh` +4. If schema changed, show the diff and ask whether to accept: `scripts/test.sh --accept-schema` +5. Report: row counts per table, any new/removed tables, test results