# Setup Project

> Configure Claude Code for this project - detects languages and sets up rules, skills, and validators

- Skill: `peteski22/setup-project` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add peteski22/setup-project`
- Raw SKILL.md: https://api.skillmd.com/api/skills/peteski22/setup-project/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: peteski22 (https://skillmd.com/u/peteski22)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/peteski22/setup-project

---


# Project Setup

Automatically configure Claude Code for the current project. Supports monorepos with multiple languages in subdirectories.

## Step 0: Resolve plugin root

The skill loader provides the base directory in the header: `Base directory for this skill: <path>`. Derive the plugin root from that:

```text
PLUGIN_ROOT = <base directory>/../../
```

For example, if the skill base directory is `~/.claude/plugins/cache/pragma/skills/setup-project`, then `PLUGIN_ROOT` is `~/.claude/plugins/cache/pragma`.

Verify the plugin root is valid by checking for the plugin manifest:

```bash
PLUGIN_ROOT="<base directory>/../.."
[[ -f "$PLUGIN_ROOT/.claude-plugin/plugin.json" ]] && echo "OK" || echo "ERROR: Plugin root invalid - .claude-plugin/plugin.json not found at $PLUGIN_ROOT"
true
```

**Note:** Replace `<base directory>` with the actual path from the skill loader header above. For example, if the header says `Base directory for this skill: /home/user/.claude/plugins/cache/pragma/skills/setup-project`, then `PLUGIN_ROOT="/home/user/.claude/plugins/cache/pragma/skills/setup-project/../.."`.

**If the check fails**, stop and show:
```text
Plugin root could not be resolved. The pragma plugin may not be installed correctly.

Reinstall:
  /plugin marketplace add peteski22/agent-pragma
  /plugin install pragma@agent-pragma
```

**STOP if check fails. Do not proceed.**

## Step 1: Detect project metadata

Get org and repo name from git remote:

```bash
git remote get-url origin 2>/dev/null | sed -E 's|.*[:/]([^/]+)/([^/]+)(\.git)?$|\1 \2|'
```

Store `{org}` and `{repo}` for templating.

## Step 2: Scan for languages (root and subdirectories)

Check root directory and subdirectories (up to two levels deep) for language markers. Two-level scanning catches nested layouts like `sdk/go/` or `services/auth/`.

```bash
# Root level
[[ -f go.mod ]] && echo "root:go"
( [[ -f pyproject.toml ]] || [[ -f setup.py ]] ) && echo "root:python"
[[ -f package.json ]] && echo "root:javascript"
[[ -f tsconfig.json ]] && echo "root:typescript"
[[ -f Cargo.toml ]] && echo "root:rust"

# Subdirectories (up to two levels deep, e.g. backend/ or sdk/python/).
# Prune heavy dirs so find never descends into them; skip the root marker
# (depth 1) since it's already covered by the root-level checks above.
find . -maxdepth 3 -type d \( -name node_modules -o -name .git -o -name vendor \) -prune -o \
  -type f \( -name go.mod -o -name pyproject.toml -o -name setup.py \
     -o -name package.json -o -name tsconfig.json -o -name Cargo.toml \) -print 2>/dev/null \
  | while IFS= read -r marker; do
  dir=$(dirname "$marker" | sed 's|^\./||')
  [ "$dir" = "." ] && continue
  case "$(basename "$marker")" in
    go.mod)          echo "${dir}:go" ;;
    pyproject.toml)  echo "${dir}:python" ;;
    setup.py)        echo "${dir}:python" ;;
    package.json)    echo "${dir}:javascript" ;;
    tsconfig.json)   echo "${dir}:typescript" ;;
    Cargo.toml)      echo "${dir}:rust" ;;
  esac
done | sort -u
true
```

This produces output like:
- `root:go` (single-language project)
- `backend:python`, `frontend:typescript` (monorepo)
- `sdk/go:go`, `sdk/python:python` (nested monorepo)

## Step 3: Check for existing configs

### 3a: Check for modular rule files

Check for rule files generated by a previous run:

```bash
[[ -d .claude/rules ]] && find .claude/rules -maxdepth 1 -name "*.md" 2>/dev/null | while IFS= read -r f; do echo "rules:$f"; done
true
```

If any exist, read them. If they have `<!-- Assembled by /setup-project` comment, safe to overwrite. Otherwise, ask before overwriting.

### 3b: Clean up legacy `.claude/CLAUDE.md` files

Check for old-style assembled CLAUDE.md files from previous versions of `/setup-project`:

```bash
[[ -f .claude/CLAUDE.md ]] && echo "legacy:.claude/CLAUDE.md"
# maxdepth 4: a two-level-nested project's config is at sdk/go/.claude/CLAUDE.md
# (depth 4) — one deeper than its language marker sdk/go/go.mod (depth 3).
find . -mindepth 2 -maxdepth 4 -name "CLAUDE.md" -path "*/.claude/CLAUDE.md" 2>/dev/null | while IFS= read -r f; do
  echo "legacy:${f#./}"
done
true
```

If any are found, read them. If they contain the `<!-- Assembled by /setup-project` marker, delete them — the new modular rules in `.claude/rules/` replace them. If they do not contain the marker (user-authored), warn the user and ask before removing.

### 3c: Check for existing AGENTS.md and CLAUDE.md

Check for existing top-level files:

```bash
[[ -f AGENTS.md ]] && echo "agents-md:exists" || echo "agents-md:missing"
[[ -f CLAUDE.md ]] && echo "claude-md:exists" || echo "claude-md:missing"
true
```

If either exists, read it and check for the `<!-- pragma:start -->` and `<!-- pragma:end -->` markers. If both markers are present, the managed block will be replaced in-place. If only the start marker is present without a matching end marker, skip the file and warn the user (same as Step 6's upsert rules). Content outside the markers is never modified.

## Step 4: Create .claude/rules/ files

Create the directory:
```bash
mkdir -p .claude/rules
```

Generate modular rule files. Each file is standalone with its own header comment.

### 4a: Universal rules

Write `.claude/rules/universal.md` with:
1. Header comment
2. Content from `$PLUGIN_ROOT/claude-md/universal/base.md`

**Header:**
```markdown
<!-- Assembled by /setup-project from agent-pragma -->
<!-- Org/Repo: {org}/{repo} -->
<!-- Re-run /setup-project to regenerate -->
```

### 4b: Local supplements rules

Write `.claude/rules/local-supplements.md` with content describing how `CLAUDE.local.md` works:

**Header:**
```markdown
<!-- Assembled by /setup-project from agent-pragma -->
<!-- Re-run /setup-project to regenerate -->
```

**Content:**
````markdown
<!-- This file is documentation only. It does not contain executable validation commands. -->
<!-- Validators: skip this file when searching for "Validation Commands" sections. -->

## Local Supplements

`CLAUDE.local.md` at the project root contains per-user, per-project instructions. Claude Code auto-loads it and adds it to `.gitignore`; if you create the file manually, verify it is in your `.gitignore`.

### Validation Command Overrides

Rules from `CLAUDE.local.md` are generally additive, but can override validation commands. Add a "Validation Commands" section to `CLAUDE.local.md` to specify custom lint/test scripts:

```markdown
## Validation Commands

- **Lint:** `./scripts/backend-lint.sh`
- **Test:** `./scripts/backend-test.sh`
```

These override the defaults in the language rules. Precedence (highest → lowest): CLAUDE.local.md > path-scoped rules > universal rules > built-in defaults.

**Common scenarios for overriding validation commands:**

- **Wrapper scripts:** Your project has `./scripts/lint.sh` that runs multiple tools (ruff + mypy + security scans) in sequence
- **Tool versioning:** Use a specific linter version not available in the default environment
- **Integration tests:** Run integration tests as part of the validation process before commits
- **Security scanning:** Add custom vulnerability checks (e.g., `bandit`, `semgrep`) before commits
- **CI/CD parity:** Match the exact validation commands used in your CI pipeline
- **Monorepo isolation:** Different validation commands for different subdirectories

### Other Uses

- Custom environment setup notes.
- Personal workflow preferences.
- Machine-specific paths or configurations.

In git worktrees, use `@import` (a Claude Code directive that includes another CLAUDE.md file) in `CLAUDE.local.md` to reference a shared local rules file rather than duplicating it per worktree (e.g., `@import ../shared-local-rules.md`).
````

**Create local supplements file and ensure it is gitignored:**
```bash
test -f CLAUDE.local.md || touch CLAUDE.local.md
grep -qxF 'CLAUDE.local.md' .gitignore 2>/dev/null || echo 'CLAUDE.local.md' >> .gitignore
```

## Step 5: Create language-specific rule files

For each detected language (whether at root or in a subdirectory), create a path-scoped rule file in `.claude/rules/`.

**File naming:** `.claude/rules/{lang}.md` (e.g., `go.md`, `python.md`, `typescript.md`).

**Always add `paths` frontmatter** to scope language rules to matching file extensions. This prevents language-specific rules from applying to unrelated files (e.g., Go rules applying to `.py` files when both languages are detected at root).

**If the language was detected at root** (e.g., `root:go`), scope to the extension at the repo root:

```markdown
---
paths:
  - "**/*.go"
---
```

**If the language was detected in a subdirectory** (e.g., `backend:python`), scope to the subdirectory and extension:

```markdown
---
paths:
  - "backend/**/*.py"
---
```

**If the same language appears at root and in subdirectories, or in multiple subdirectories**, combine the paths:

```markdown
---
paths:
  - "backend/**/*.py"
  - "scripts/**/*.py"
---
```

**File extension mapping:**
- `go` → `*.go`
- `python` → `*.py`
- `typescript` → `*.ts` and `*.tsx` (use two separate `paths` entries, not brace expansion)
- `javascript` → `*.js` and `*.jsx` (use two separate `paths` entries, not brace expansion)
- `rust` → `*.rs`

Assemble each language rule file with the YAML frontmatter as the very first bytes of the file, so path scoping is never silently disabled by a leading comment:
1. Paths frontmatter (always — scoped by extension, and by subdirectory when applicable). Must be the first content in the file, with `---` as line 1.
2. Header comment
3. Language-specific rules from `$PLUGIN_ROOT/claude-md/languages/{lang}/{lang}.md`

**Header:**
```markdown
<!-- Assembled by /setup-project from agent-pragma -->
<!-- Language: {lang} -->
<!-- Re-run /setup-project to regenerate -->
```

**Assembled file shape** (example for `root:go`):
```markdown
---
paths:
  - "**/*.go"
---
<!-- Assembled by /setup-project from agent-pragma -->
<!-- Language: go -->
<!-- Re-run /setup-project to regenerate -->

# Go Language Rules
...
```

## Step 6: Upsert AGENTS.md and CLAUDE.md blocks

Insert or update a **marker-delimited block** in each file. Content outside the markers is never touched — other tools and the user can add their own content freely.

**Marker pair:**
```
<!-- pragma:start -->
...managed content...
<!-- pragma:end -->
```

**Upsert rules:**

| File state | Action |
|---|---|
| File missing | Create with the block as the full body |
| File exists, no `<!-- pragma:start -->` | Append block after a blank-line separator |
| Block present but changed | Replace from start marker through end marker |
| Block present and identical | No-op |
| Start marker present without matching end marker | Skip and warn the user |

### 6a: AGENTS.md

Upsert the pragma block into `AGENTS.md` at the project root. The block contains one `@` import per rules file created in Steps 4–5. List universal first, then local-supplements, then language-specific files alphabetically.

**Block content** (example for a Python + TypeScript project):

```markdown
<!-- pragma:start -->
@.claude/rules/universal.md
@.claude/rules/local-supplements.md
@.claude/rules/python.md
@.claude/rules/typescript.md
<!-- pragma:end -->
```

Include only the files that were actually created — do not include imports for languages that were not detected.

### 6b: CLAUDE.md

Upsert the pragma block into `CLAUDE.md` at the project root. The block contains a single `@` import of AGENTS.md.

**Block content:**

```markdown
<!-- pragma:start -->
@./AGENTS.md
<!-- pragma:end -->
```

## Step 7: Clean up opencode.json

OpenCode reads `AGENTS.md` natively, so explicit `instructions` entries are no longer needed. This step only cleans up entries left by previous `/setup-project` runs.

**Check for existing opencode.json:**
```bash
test -f opencode.json && echo "opencode-json:exists" || echo "opencode-json:missing"
```

**If missing**, skip this step — do not create the file.

**If it exists**, read it and check for stale instructions from a previous run:

1. **Invalid JSON:** Leave it alone. Warn the user that `opencode.json` is not valid JSON.
2. **`instructions` contains `.claude/rules/*.md`:** Remove that entry. If the `instructions` array is now empty, remove the `instructions` field entirely.
3. **No `.claude/rules/*.md` in `instructions`:** No changes needed.

**Do not overwrite** other fields in an existing `opencode.json` (e.g., `model`, `provider`, `agent`).

## Step 8: Verify plugin skills and agents

All skills and agents are provided by the pragma plugin — no symlinks needed.

**Check star-chamber prerequisites:**
```bash
command -v uv >/dev/null 2>&1 && echo "uv:ok" || echo "uv:missing"
```

Store the result - if `uv:missing`, include a warning in Step 10 output.

**Build go-structural (ONLY if Go was detected in Step 2):**

Skip this entirely if no `go` language was detected in Step 2 output (e.g. if the only output was `root:python`, there is no Go — do not build go-structural).

If and only if Go was detected (any line matching `*:go` in Step 2 output):

```bash
cd "$PLUGIN_ROOT/tools/go-structural" && go build -o go-structural . && echo "go-structural:ok" || echo "go-structural:build-failed"
```

If `go` is not available or the build fails, note in Step 10 output that go-structural is unavailable.

## Step 9: Offer reference configs

For each language detected in Step 2, read `$PLUGIN_ROOT/claude-md/languages/{lang}/setup.md` if it exists. This file defines:

- **Lint Config Detection** — which config files to check for (any match means the project already has lint tooling configured).
- **Reference Config** — which reference config to offer and where to copy it from (paths are relative to `$PLUGIN_ROOT`).

For each language that has a `setup.md`:

1. Read the file and extract the config filenames from the "Lint Config Detection" section.
2. Check if any of those files exist in the project. Use glob matching for wildcard patterns (e.g., `.eslintrc.*`, `.prettierrc*`).
3. If any config file exists, skip — the project already has lint tooling.
4. If none exist, extract the reference config path from the "Reference Config" section and offer to copy from `$PLUGIN_ROOT/<path>`, replacing `{org}` and `{repo}` template variables with values from Step 1.

Skip languages that have no `setup.md` file.

## Step 10: Output summary

```
## Setup Complete

**Project:** {org}/{repo}

**Structure detected:**
  - Root: [languages or "none"]
  - backend/: Python
  - frontend/: TypeScript

**Created:**
  - .claude/rules/universal.md
  - .claude/rules/local-supplements.md
  - .claude/rules/python.md (scoped to backend/**)
  - .claude/rules/typescript.md (scoped to frontend/**)
  - AGENTS.md (pragma block imports .claude/rules/*.md)
  - CLAUDE.md (pragma block imports AGENTS.md)

**Skills available (via pragma plugin):**
  - /implement - implement with auto-validation
  - /review - review changes against all validators
  - /validate - run all validators
  - /star-chamber - multi-LLM advisory council

**Agents available (via pragma plugin):**
  - security - auto-invokes on trust boundary changes
  - star-chamber - auto-invokes on architectural decisions

**Usage:**
  /implement <task>    - implement with validation loop
  /review              - validate current changes

**Star-Chamber:** Run `/star-chamber` — first invocation launches interactive provider setup.
```

**If uv is missing**, include this warning:
```text
⚠️  **Warning:** uv is not installed. /star-chamber requires uv to run.

Install uv:
  curl -LsSf https://astral.sh/uv/install.sh | sh

Or see: https://docs.astral.sh/uv/getting-started/installation/
```

Then continue with:
```
**Recommended:**
  - **Optional:** If you want other contributors to benefit from the same rules, commit `AGENTS.md`, `CLAUDE.md`, and the generated `.claude/rules/` files. Otherwise, add them to `.gitignore` to keep them local.
  - `CLAUDE.local.md` has been created for personal/machine-specific rules (custom validation commands, local environment notes, personal workflow preferences). It is auto-loaded by Claude Code and gitignored.
```

