Skip to content
Back to Tutorials

Claude Code Setup in VS Code: Build a Better Multi-Stack CLAUDE.md Starter

Intermediate · 1 hour · 23 min read · Byte Smith ·

Before you begin

  • VS Code installed with the Claude Code extension authenticated
  • At least one project to test against (Laravel, Next.js, Python, Node, or Docker)
  • Basic familiarity with YAML, Bash, and JSON

What you'll learn

  • Structure a modular CLAUDE.md that acts as a lightweight router
  • Create unconditional rules for cross-cutting coding standards
  • Write conditional stack rules with paths frontmatter for on-demand loading
  • Configure subagents for focused multi-step tasks
  • Set up SessionStart and PostToolUse hooks for automation
  • Build a stack detection script that outputs JSON
  • Install the configuration into any existing project
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
On this page

Most Claude Code setups start with a single CLAUDE.md file that tries to cover everything. That works fine for small, single-stack projects. But if you work across multiple technology stacks, that file grows into a bloated instruction dump that wastes context and dilutes the guidance Claude actually needs for the task at hand.

In this tutorial, you will build a modular Claude Code configuration for VS Code that adapts to whatever stack you are working in. Instead of one massive file, you will create a lightweight CLAUDE.md router backed by conditional rules, specialized subagents, automation hooks, and reusable prompt templates. By the end, you will have a portable .claude/ directory you can install into any project.

The finished starter is available at InkByteStudio/claude-code-vscode-multistack-starter. You can follow along step by step or clone the repo and use it as a reference.

What you will build

Here is the full structure you will create:

.claude/
  rules/
    common.md          # unconditional - always loaded
    testing.md         # unconditional
    git.md             # unconditional
    security.md        # unconditional
    laravel.md         # conditional - paths frontmatter
    react-next.md      # conditional
    python-fastapi.md  # conditional
    node-express.md    # conditional
    docker-devops.md   # conditional
    sql.md             # conditional
  agents/
    laravel-architect.md
    react-ui.md
    python-api.md
    node-backend.md
    devops-reviewer.md
    sql-investigator.md
  hooks/
    session-start.sh
    post-edit-check.sh
  prompts/
    bugfix.md
    refactor.md
    code-review.md
    onboarding.md
  settings.json
CLAUDE.md
scripts/
  detect-stack.sh
  install-local.sh

The architecture follows a two-tier pattern. Unconditional rules (common, testing, git, security) load every session and provide baseline guidance. Conditional stack rules use paths frontmatter to load only when Claude reads or edits files matching specific glob patterns. This means a Python session only sees Python rules, a Laravel session only sees Laravel rules, and context stays lean.

Why modular beats monolithic

A monolithic CLAUDE.md for a six-stack project might be 400 to 600 lines. In a session where you are only editing Python files, 80 percent of those instructions are noise. Conditional loading changes the math:

  • Monolithic: approximately 400 lines loaded every session
  • Modular (Python-only session): approximately 80 lines (shared rules plus python-fastapi.md)
  • Modular (Laravel plus Docker session): approximately 120 lines (shared rules plus laravel.md plus docker-devops.md)

Less noise means better signal, which means more accurate responses. It also means each stack owner maintains their own file instead of everyone fighting over one shared document.

Step 1: Initialize Claude Code in VS Code

Open your project in VS Code and make sure the Claude Code extension is installed and authenticated. Run the /init command from the Claude Code chat to scaffold a default CLAUDE.md in your project root.

The default file will be a basic starting point. You will replace it entirely in the next step, but running /init first ensures Claude Code recognizes the project and the .claude/ directory is created.

Info

If you already have a CLAUDE.md, back it up before proceeding. You will restructure its content into the modular format.

You should now have a CLAUDE.md file in your project root and a .claude/ directory.

Step 2: Create the CLAUDE.md router

Replace the default CLAUDE.md with a lightweight project guide that acts as a router. Instead of containing all your rules, it describes the project at a high level and imports the unconditional rules using the @ syntax.

Create CLAUDE.md in your project root:

# Claude Code Project Guide

This repository is a multi-stack starter configuration for Claude Code in VS Code.

## How this repo works

- **Unconditional rules** in `.claude/rules/` (common, testing, git, security)
  load every session.
- **Stack-specific rules** in `.claude/rules/` use `paths` frontmatter to load
  only when you touch matching files.
- **Subagents** in `.claude/agents/` handle focused tasks like architecture
  review, UI work, or SQL investigation.
- **Hooks** in `.claude/settings.json` run lightweight automation on session
  start and after edits.
- **Prompts** in `.claude/prompts/` provide reusable workflow templates for
  common tasks.

## Stack detection

Identify the active stack from the repository structure before making changes.

Common stack indicators:
- Laravel/PHP: `composer.json`, `artisan`, `routes/`, `app/`, `config/`
- React/Next.js: `package.json`, `next.config.*`, `app/`, `pages/`,
  `src/components/`
- Python/FastAPI: `pyproject.toml`, `requirements.txt`, `app/`, `main.py`
- Node/Express: `package.json`, `server.js`, `src/`, `routes/`
- Docker/DevOps: `Dockerfile`, `docker-compose.yml`, `.github/workflows/`
- SQL/Data: migration files, schema dumps, reporting queries, ETL scripts

## Default behavior

- Explain the plan before major edits.
- Prefer the smallest useful diff.
- Reuse existing patterns before adding new abstractions.
- Do not add dependencies unless there is a clear reason.
- Update docs when behavior changes.

## Imported rules (always loaded)

@.claude/rules/common.md
@.claude/rules/testing.md
@.claude/rules/git.md
@.claude/rules/security.md

## Prompt helpers (available on demand)

@.claude/prompts/bugfix.md
@.claude/prompts/refactor.md
@.claude/prompts/code-review.md
@.claude/prompts/onboarding.md
Tip

The @.claude/rules/common.md syntax tells Claude Code to import that file’s content every session. This is how unconditional rules get loaded without putting their content directly in CLAUDE.md.

You should now have a clean, readable CLAUDE.md that describes the project structure and imports shared rules without containing stack-specific details.

Step 3: Add unconditional rules

Create four rule files in .claude/rules/ that load every session. These cover cross-cutting concerns that apply regardless of which stack you are working in.

Create .claude/rules/common.md:

# Common Rules

- Match the project's existing naming, folder, and formatting conventions.
- Prefer explicit code over clever code.
- Do not rewrite unrelated files.
- Preserve comments that explain business logic.
- Keep functions and classes cohesive and focused.
- Prefer configuration over hardcoding where the project already supports it.
- When changing behavior, note user-visible impact.
- Use 2-space indentation for JS/TS/JSON, 4-space for Python/PHP unless the
  project uses something else.
- Follow the existing import style and module organization.

Create .claude/rules/testing.md:

# Testing Rules

- Run only the most relevant tests first.
- Prefer one file, one suite, or one command over full-suite runs.
- If adding logic, add or update tests when the project already has test
  coverage in that area.
- If tests are missing, suggest the smallest useful test addition.
- Report what was run, what passed, and what was not verified.
- Use the stack-appropriate test runner:
  - PHP/Laravel: `php artisan test --filter=...` or `./vendor/bin/phpunit`
  - React/Next.js: `npm test -- <name>` or `npx vitest run <name>`
  - Python: `pytest -k ...` or `uv run pytest`
  - Node/Express: `npm test -- <name>` or `npm run test:unit`

Create .claude/rules/git.md:

# Git Rules

- Keep changes scoped to the task.
- Summarize changed files in plain language.
- Use conventional commit style: `type(scope): description`
  - Types: feat, fix, refactor, test, docs, chore, ci, perf
- Call out migrations, config changes, and any manual deployment steps.
- Suggest focused branch names: `type/short-description`
  (e.g., `fix/auth-redirect`, `feat/user-export`).
- Never force-push to main or shared branches without explicit approval.

Create .claude/rules/security.md:

# Security Rules

- Never expose secrets, tokens, or private keys in code or logs.
- Never commit `.env`, credentials, or key files.
- Avoid logging sensitive values (passwords, tokens, PII).
- Validate input boundaries on API and DB changes.
- Sanitize user input before rendering (prevent XSS) or querying
  (prevent SQL injection).
- Flag auth, permission, or data exposure risks clearly.
- For destructive shell or SQL operations, explain scope before execution.
- Run `npm audit`, `pip audit`, or `composer audit` when adding dependencies.

These four files have no paths frontmatter, which means Claude loads them unconditionally in every session.

You should now have four rule files in .claude/rules/ covering coding standards, testing philosophy, git conventions, and security guardrails.

Step 4: Add conditional stack rules

Conditional rules are where the modular approach pays off. Each stack rule file includes a paths block in its YAML frontmatter that tells Claude to load it only when reading or editing files matching those glob patterns.

Here is the full Laravel rule file as the primary example. Create .claude/rules/laravel.md:

---
paths:
  - "**/*.php"
  - "**/routes/**"
  - "**/app/**"
  - "**/database/migrations/**"
  - "**/resources/views/**"
---

# Laravel / PHP Guidance

## Architecture
- Prefer existing Laravel conventions before custom patterns.
- Keep controllers thin.
- Put reusable business logic in services, actions, or domain classes
  if that pattern already exists.
- Use Form Requests for request validation when appropriate.
- Prefer Eloquent scopes, relationships, and query builders over
  duplicated query logic.

## Database
- Treat migrations as review-sensitive changes.
- Prefer additive schema changes unless a breaking change is intentional.
- For large updates, think about indexing, chunking, locking, and
  rollback impact.

## Commands
- `php artisan test --filter=...`
- `php artisan route:list`
- `php artisan migrate --pretend`
- `composer test`
- `./vendor/bin/phpunit`

## Review checklist
- Validation correct?
- Auth/authorization preserved?
- N+1 or query performance issues?
- Migration safety reviewed?
- API response shape consistent?
Warning

If you forget the paths frontmatter on a conditional rule, Claude will load it unconditionally every session. Always double-check that stack rules include the paths block.

Now create the remaining five stack rule files. Each follows the same pattern: paths frontmatter at the top, then stack-specific guidance.

Create .claude/rules/react-next.md with these paths:

---
paths:
  - "**/*.tsx"
  - "**/*.jsx"
  - "**/components/**"
  - "**/pages/**"
  - "**/next.config.*"
---

Include guidance on component design, state management, Next.js server/client boundaries, styling conventions, and a review checklist covering accessibility, loading states, and type correctness.

Create .claude/rules/python-fastapi.md with these paths:

---
paths:
  - "**/*.py"
  - "**/requirements*.txt"
  - "**/pyproject.toml"
---

Include guidance on API design, validation, async patterns, and a review checklist covering type hints, schema alignment, and error handling.

Create .claude/rules/node-express.md with these paths:

---
paths:
  - "**/server/**"
  - "**/routes/**/*.js"
  - "**/routes/**/*.ts"
  - "**/middleware/**"
  - "**/server.js"
  - "**/app.js"
---

Include guidance on route structure, middleware, error handling, and a review checklist covering validation, auth, and logging safety.

Create .claude/rules/docker-devops.md with these paths:

---
paths:
  - "**/Dockerfile*"
  - "**/docker-compose*.yml"
  - "**/compose*.yml"
  - "**/.github/workflows/**"
  - "**/nginx/**"
---

Include guidance on container changes, CI/CD pipelines, safety for production-impacting changes, and a review checklist covering build reproducibility, secrets handling, and rollback paths.

Create .claude/rules/sql.md with these paths:

---
paths:
  - "**/*.sql"
  - "**/migrations/**"
  - "**/seeds/**"
  - "**/seeders/**"
---

Include guidance on query design, safety for UPDATE and DELETE statements, performance considerations, and a review checklist covering null handling, join duplication, and rollback strategy.

You should now have six conditional rule files in .claude/rules/, each with paths frontmatter that limits when it loads.

Step 5: Create subagents

Subagents are specialized agents defined in .claude/agents/ with their own system prompts, tool access, and model selection. They are useful for multi-step tasks where a focused perspective helps.

Create .claude/agents/laravel-architect.md:

---
name: laravel-architect
description: Laravel architecture specialist for Eloquent models, migrations,
  API resources, Form Requests, service patterns, and routing.
tools: Read, Edit, Write, Bash, Grep, Glob
model: sonnet
maxTurns: 25
---

You are a Laravel-focused engineering subagent.

## Priorities
- Follow Laravel conventions already present in the repo.
- Keep controllers thin. Push logic into services, actions, or domain classes.
- Recommend safe migration patterns. Prefer additive schema changes.
- Call out validation, auth, and query performance risks.
- Prefer focused diffs and small follow-up steps.

## When reviewing code
- Check for N+1 queries and suggest eager loading where needed.
- Verify Form Request validation covers edge cases.
- Confirm authorization checks are in place.
- Look for missing indexes on frequently queried columns.

## When writing code
- Use Eloquent relationships and scopes over raw queries.
- Follow existing factory and seeder patterns for test data.
- Keep route definitions clean and grouped logically.
- Use Laravel's built-in helpers before adding custom utilities.

Create the remaining five agents in .claude/agents/ following the same pattern:

  • react-ui.md — React and Next.js UI specialist. Focus on component structure, state flow, accessibility, and styling. Set model: sonnet, maxTurns: 20.
  • python-api.md — Python and FastAPI specialist. Focus on endpoint design, Pydantic models, async patterns, and type safety. Set model: sonnet, maxTurns: 25.
  • node-backend.md — Node.js and Express specialist. Focus on middleware, error handling, route organization, and async patterns. Set model: sonnet, maxTurns: 25.
  • devops-reviewer.md — Docker and DevOps specialist. Focus on Dockerfiles, CI/CD pipelines, infrastructure review, and security. Set model: sonnet, maxTurns: 20.
  • sql-investigator.md — SQL and database specialist. Focus on query optimization, schema review, migration safety, and data analysis. Set model: sonnet, maxTurns: 20.

Each agent includes a description field that tells Claude when to suggest delegating to it. The tools field controls which tools the agent can use. The maxTurns field limits how many steps the agent can take before returning control.

Tip

Use agents for multi-step focused tasks like architecture reviews, component scaffolding, or query optimization. Use rules for ambient guidance that shapes how Claude approaches code in general.

You should now have six agent files in .claude/agents/, one per stack.

Step 6: Configure hooks

Hooks are lightweight shell scripts that run automatically at specific Claude Code lifecycle events. Create the hooks configuration and scripts.

Create .claude/settings.json:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-start.sh",
            "timeout": 30
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/post-edit-check.sh",
            "timeout": 60
          }
        ]
      }
    ]
  },
  "permissions": {
    "allow": [],
    "deny": []
  }
}

The $CLAUDE_PROJECT_DIR variable resolves to the project root, so paths work regardless of the current working directory.

Create .claude/hooks/session-start.sh:

#!/usr/bin/env bash
# Runs on SessionStart via .claude/settings.json
# Detects active stacks and checks environment readiness.

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DETECT_SCRIPT="$SCRIPT_DIR/../../scripts/detect-stack.sh"

if [ -x "$DETECT_SCRIPT" ] || [ -f "$DETECT_SCRIPT" ]; then
  stacks_json=$(bash "$DETECT_SCRIPT" 2>/dev/null || echo '{"stacks": []}')
  stacks_list=$(echo "$stacks_json" | grep -o '"[^"]*"' \
    | grep -v stacks | grep -v detected_at | tr -d '"' \
    | paste -sd', ' -)
  if [ -n "$stacks_list" ]; then
    echo "Detected stacks: $stacks_list"
  else
    echo "No specific stack detected. Check project files manually."
  fi
else
  echo "No specific stack detected. Check project files manually."
fi

# Check for missing .env
if [ -f .env.example ] && [ ! -f .env ]; then
  echo "Warning: .env.example exists but .env is missing. Copy and configure it."
fi

exit 0

Create .claude/hooks/post-edit-check.sh:

#!/usr/bin/env bash
# Runs after Edit/Write tool use via .claude/settings.json
# Performs lightweight lint checks based on the edited file type.

INPUT=$(cat)
if command -v jq &>/dev/null; then
  FILE=$(echo "$INPUT" | jq -r '.file_path // empty' 2>/dev/null)
else
  FILE=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' \
    | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"//;s/"$//')
fi

if [ -z "$FILE" ]; then
  exit 0
fi

EXT="${FILE##*.}"

case "$EXT" in
  php)
    if command -v php &>/dev/null; then
      php -l "$FILE" 2>&1 || true
    fi
    ;;
  py)
    if command -v ruff &>/dev/null; then
      ruff check "$FILE" 2>&1 || true
    elif command -v python3 &>/dev/null; then
      python3 -m py_compile "$FILE" 2>&1 || true
    fi
    ;;
  ts|tsx|js|jsx)
    if command -v npx &>/dev/null; then
      npx eslint "$FILE" --no-error-on-unmatched-pattern 2>&1 || true
    fi
    ;;
  sql)
    echo "SQL file edited: $FILE -- review before executing."
    ;;
esac

exit 0

Make both hook scripts executable:

chmod +x .claude/hooks/session-start.sh .claude/hooks/post-edit-check.sh
Note

The post-edit hook reads the file path from Claude Code’s tool input JSON via stdin. It uses jq if available, falling back to grep-based parsing. Both approaches work, but installing jq gives cleaner results.

You should now have two hook scripts and a settings.json that wires them to the SessionStart and PostToolUse lifecycle events.

Step 7: Build the stack detection script

The stack detection script scans for marker files in the project root and outputs a JSON object listing the detected stacks. The session-start hook calls this script to give Claude immediate project context.

Create scripts/detect-stack.sh:

#!/usr/bin/env bash
# Standalone stack detection script.
# Outputs detected stacks as JSON for use by other tools.

stacks=()

{ [ -f artisan ] || [ -f composer.json ]; } && stacks+=("laravel")
{ [ -f next.config.js ] || [ -f next.config.mjs ] || [ -f next.config.ts ]; } \
  && stacks+=("react-next")
[ -f package.json ] && grep -q '"react"' package.json 2>/dev/null \
  && stacks+=("react")
{ [ -f pyproject.toml ] || [ -f requirements.txt ]; } \
  && stacks+=("python-fastapi")
[ -f package.json ] && { [ -f server.js ] || [ -f app.js ]; } \
  && stacks+=("node-express")
{ [ -f Dockerfile ] || [ -f docker-compose.yml ] || [ -f compose.yml ]; } \
  && stacks+=("docker-devops")

# Check for SQL-heavy projects
sql_count=$(find . -maxdepth 3 -name "*.sql" 2>/dev/null | head -5 | wc -l)
[ "$sql_count" -gt 0 ] && stacks+=("sql")

# Output as JSON
json="["
for i in "${!stacks[@]}"; do
  [ "$i" -gt 0 ] && json+=","
  json+="\"${stacks[$i]}\""
done
json+="]"

echo "{\"stacks\": $json, \"detected_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"

Make it executable:

chmod +x scripts/detect-stack.sh

Here is how the script maps marker files to stack identifiers:

Stack IDMarker FilesDetection Logic
laravelartisan, composer.jsonEither file exists
react-nextnext.config.js, .mjs, .tsAny config file exists
reactpackage.json with "react"React in dependencies
python-fastapipyproject.toml, requirements.txtEither file exists
node-expresspackage.json + server.js/app.jsPackage file plus entry point
docker-devopsDockerfile, docker-compose.yml, compose.ymlAny config exists
sql*.sql files within 3 levelsAt least one SQL file found

Running the script produces output like this:

{
  "stacks": ["laravel", "docker-devops", "sql"],
  "detected_at": "2026-04-01T12:00:00Z"
}
Info

Some detections are intentionally broad. For example, composer.json triggers the laravel stack even for non-Laravel PHP projects, because the Laravel rules contain general PHP guidance that is useful for any Composer-based project. Similarly, pyproject.toml triggers python-fastapi for all Python projects.

You should now have a working stack detection script that outputs clean JSON.

Step 8: Add prompt templates

Prompt templates are reusable workflows stored in .claude/prompts/. They are available on demand when you need a structured approach to a common task.

Create .claude/prompts/bugfix.md:

# Bugfix Workflow

Use this prompt when diagnosing and fixing a bug.

## Steps

1. **Describe the bug**: What is the expected behavior vs. actual behavior?
2. **Reproduce**: Find or write a minimal reproduction. If tests exist,
   write a failing test first.
3. **Diagnose**: Trace the code path. Check logs, error messages, and recent
   changes. Narrow down to the root cause before proposing a fix.
4. **Fix**: Apply the smallest change that addresses the root cause.
   Do not refactor unrelated code.
5. **Verify**: Run the failing test (or the narrowest relevant test suite)
   to confirm the fix. Run lint and typecheck.
6. **Document**: Note what caused the bug, what the fix does, and any
   follow-up work needed.

## Checklist before committing
- [ ] Root cause identified (not just symptoms addressed)
- [ ] Fix is scoped to the bug (no unrelated changes)
- [ ] Test covers the failure case
- [ ] Existing tests still pass
- [ ] No regressions introduced

Create three more prompt templates in .claude/prompts/:

  • refactor.md — A structured refactoring workflow: identify the target, scope the impact, plan incremental steps, execute one step at a time, verify tests pass at each step, and document the motivation in the commit message.
  • code-review.md — A code review checklist covering security (secrets, injection, auth), correctness (edge cases, error paths), performance (N+1, async, pagination), readability (naming, focused diffs), and testing (coverage, meaningful assertions). Rate findings as Critical, Warning, or Suggestion.
  • onboarding.md — A project onboarding workflow: explore the directory structure, identify the stack, find entry points, document build and run commands, map the architecture, check for conventions, and produce a summary document.

You can invoke any prompt template during a Claude Code session with /prompt-name or by referencing it with @.claude/prompts/name.md.

You should now have four prompt templates covering the most common development workflows.

Step 9: Create the install script

The install script copies the entire configuration into an existing project. This makes the starter portable across all your repos.

Create scripts/install-local.sh:

#!/usr/bin/env bash
# Install this Claude Code starter config into an existing project.
# Usage: ./scripts/install-local.sh [target-directory]

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$(dirname "$SCRIPT_DIR")"
TARGET="${1:-.}"

# Resolve to absolute path
TARGET="$(cd "$TARGET" && pwd)"

echo "Claude Code Multi-Stack Starter Installer"
echo "==========================================="
echo ""
echo "Source: $REPO_DIR"
echo "Target: $TARGET"
echo ""

# Check if .claude/ already exists
if [ -d "$TARGET/.claude" ]; then
  echo "Warning: $TARGET/.claude/ already exists."
  read -rp "Overwrite existing config? (y/N): " confirm
  if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
    echo "Aborted."
    exit 0
  fi
fi

# Copy .claude/ directory
echo "Copying .claude/ config..."
mkdir -p "$TARGET/.claude"
cp "$REPO_DIR/.claude/settings.json" "$TARGET/.claude/settings.json"
cp -r "$REPO_DIR/.claude/rules" "$TARGET/.claude/"
cp -r "$REPO_DIR/.claude/agents" "$TARGET/.claude/"
cp -r "$REPO_DIR/.claude/hooks" "$TARGET/.claude/"
cp -r "$REPO_DIR/.claude/prompts" "$TARGET/.claude/"

# Copy CLAUDE.md if it doesn't exist
if [ -f "$TARGET/CLAUDE.md" ]; then
  echo "CLAUDE.md already exists in target. Skipping (review manually)."
else
  cp "$REPO_DIR/CLAUDE.md" "$TARGET/CLAUDE.md"
  echo "Copied CLAUDE.md"
fi

# Make hook scripts executable
chmod +x "$TARGET/.claude/hooks/"*.sh 2>/dev/null || true

# Detect stacks
echo ""
echo "Detecting stacks in target project..."
cd "$TARGET"
stacks_output=$("$REPO_DIR/scripts/detect-stack.sh" 2>/dev/null \
  || echo '{"stacks": []}')
echo "$stacks_output"

# Summary
echo ""
echo "Installation complete."
echo ""
echo "Next steps:"
echo "  1. Review CLAUDE.md and adjust for your project"
echo "  2. Remove stack rules you don't need from .claude/rules/"
echo "  3. Remove agents you don't need from .claude/agents/"
echo "  4. Customize .claude/settings.json hooks"
echo "  5. Open the project in VS Code and start Claude Code"

Make it executable:

chmod +x scripts/install-local.sh

To install the starter into an existing project:

./scripts/install-local.sh /path/to/your/project

The script copies the .claude/ directory, preserves any existing CLAUDE.md, makes hooks executable, and runs stack detection on the target project.

You should now have a working install script that can deploy the configuration to any project.

Step 10: Test the setup

Pick an existing project that uses at least one of the supported stacks. Install the configuration using the install script or by copying the files manually. Then verify each component works.

Verify unconditional rules load. Start a new Claude Code session and ask Claude to describe its active rules. The four unconditional rules (common, testing, git, security) should be present.

Verify conditional rules trigger. Ask Claude to read or edit a file matching one of the stack patterns. For example, open a .php file in a Laravel project. The Laravel rules should activate. Open a .tsx file and the React rules should activate instead.

Verify subagents are available. Ask Claude to list available agents or try delegating a task to a specific agent like laravel-architect.

Verify the session-start hook runs. Start a new session and check whether Claude reports detected stacks. You should see output like “Detected stacks: laravel, docker-devops”.

Verify the post-edit hook fires. Ask Claude to edit a file and check whether the lint check runs automatically after the edit.

If everything works, the configuration is ready for daily use.

Customization

The starter is a foundation, not a finished product. Here is how to adapt it:

Remove unused stacks. Delete rule and agent files for stacks you do not use. A Python-only team can remove laravel.md, react-next.md, node-express.md, and their corresponding agents.

Customize unconditional rules. Adjust common.md, testing.md, git.md, and security.md to match your team’s conventions. These load every session, so keep them lean and relevant.

Add new stacks. Copy an existing conditional rule file, update the paths frontmatter to match your new stack’s file patterns, and update the guidance. For example, to add Rust support:

---
paths:
  - "**/*.rs"
  - "**/Cargo.toml"
---

Then add a matching detection block in detect-stack.sh:

[ -f Cargo.toml ] && stacks+=("rust")

And optionally create a rust-systems.md agent in .claude/agents/.

Adjust hook timeouts. If your lint tools are slow, increase the timeout values in settings.json. The session-start hook defaults to 30 seconds and the post-edit hook to 60 seconds.

Common Setup Problems

Stack rules load every session

Symptoms: Claude references Laravel rules when you are editing Python files, or all stack rules appear active regardless of what you are working on.

Likely cause: The paths frontmatter is missing from the stack rule file, or the YAML syntax is invalid.

Fix: Open the rule file and verify it starts with a valid paths block between --- delimiters. Check for YAML syntax errors like missing quotes, incorrect indentation, or a missing closing ---.

Hooks do not run

Symptoms: No stack detection output on session start. No lint check after edits.

Likely cause: The hook scripts are not executable, the path in settings.json is incorrect, or the $CLAUDE_PROJECT_DIR variable is not resolving correctly.

Fix: Run chmod +x .claude/hooks/*.sh. Verify that settings.json uses the correct path format with $CLAUDE_PROJECT_DIR. Check that the scripts directory exists and contains detect-stack.sh.

Stack not detected

Symptoms: The session-start hook reports “No specific stack detected” even though the project clearly uses a supported stack.

Likely cause: The marker files are not at the project root. In a monorepo, the Laravel artisan file might be in backend/ instead of the root.

Fix: Either move marker files to the root, add symlinks, or modify detect-stack.sh to check common monorepo layouts like packages/, apps/, or services/.

Post-edit hook reports errors

Symptoms: Lint errors appear after every edit, even for unrelated issues.

Likely cause: The linter tool is reporting pre-existing issues in the file, not issues caused by the edit.

Fix: Fix the pre-existing lint issues, or adjust the post-edit hook to only report errors on the changed lines. For a quick workaround, you can also comment out specific linter checks in post-edit-check.sh.

CLAUDE.md is too detailed

Symptoms: The CLAUDE.md file is growing large again, defeating the purpose of the modular setup.

Likely cause: Stack-specific or detailed guidance is being added to CLAUDE.md instead of the appropriate rule file.

Fix: Move detailed guidance into the relevant .claude/rules/ file. CLAUDE.md should stay as a lightweight router: project overview, stack hints, default behavior, and @ imports. Everything else belongs in a rule file.

Wrap-Up

You now have a modular Claude Code configuration that adapts to whatever stack you are working in. Unconditional rules provide consistent baseline guidance. Conditional rules load only when relevant. Subagents offer focused expertise. Hooks automate routine checks. And the install script makes the entire setup portable across projects.

The key principle is simple: load only what matters for the current task. That gives Claude more context for the code it is actually working on, and gives your team a maintainable configuration that scales with the project.

The complete starter is available at InkByteStudio/claude-code-vscode-multistack-starter. Clone it, customize it, and strip out what you do not need.

For a higher-level look at why this modular approach matters, see the companion blog post: Why Most CLAUDE.md Files Are Too Big. To understand how Claude Code fits into the broader AI coding agent landscape, read AI Coding Agents in 2026: How MCP Is Changing Software Development and Building Custom MCP Servers. For teams thinking about security and governance around agentic development, pair this with Securing AI Coding Agent Workflows and the Agentic AI Security Playbook.