C Claude Cert Prep All Claude Certifications

Configuring Claude Code for Teams

How to set up CLAUDE.md, permissions, slash commands, skills, hooks, and CI so Claude Code behaves predictably across a team.

Claude Code·12 min

Claude Code has a handful of configuration surfaces that, once you understand how they interact, let you run it safely and consistently across a team instead of relying on every engineer remembering the right instructions each session. This article covers CLAUDE.md scopes, permission design in settings.json, when to reach for a slash command versus a skill versus a hook, plan mode, and headless CI/CD usage. Getting the mental model wrong in any one produces either a setup too dangerous to trust with real access, or one so locked down nobody bothers using it agentically at all.

CLAUDE.md and how the memory hierarchy actually merges

CLAUDE.md is a memory file Claude Code loads into context automatically at the start of a session. It's where you put project conventions, build and test commands, architectural constraints, and anything else you want Claude to already know — persistent context, not a one-off instruction typed into a session.

Claude Code looks for CLAUDE.md at four scopes:

The detail that trips people up: these scopes are merged, not layered as overrides. A more specific file — say, project-level — does not replace or override what an enterprise-level or user-level CLAUDE.md says. It adds to it. All applicable content ends up concatenated into context together, with no precedence resolution where the closest file wins.

This has a concrete consequence. If your enterprise CLAUDE.md says "never commit directly to main" and your project CLAUDE.md says nothing about branching, that rule is still active in every session inside that project. You can't override an inherited instruction by staying quiet about it at a lower scope, and contradicting it doesn't cleanly override it either — if a project-level file says "direct commits to main are fine here," both statements land in context together, and Claude has to reconcile the contradiction rather than pick the more specific one. If you need an exception, build it into the policy at the scope that owns it. Claude Code does have a defined load order across scopes, but that affects how instructions are sequenced, not which ones are honored — if two files genuinely disagree, that's an authoring problem to fix directly, not something load order resolves for you.

A realistic project-level CLAUDE.md looks something like this:

## Project: billing-service

Build & test:
- Run tests: npm run test
- Lint: npm run lint (must pass before any commit)

Conventions:
- Currency values are stored as integer cents, never floats.
- New API routes go in src/routes/ with a matching contract
  test in tests/contracts/.
- Check package.json before adding a new dependency.

Known gotchas:
- Staging DB is shared. Never run destructive migrations
  against it without posting in #billing-eng.
- No ORM in this repo. Raw SQL in src/db/queries/ is the
  pattern; keep it that way.

Good candidates: exact test and lint commands, naming conventions, directory notes, known gotchas, pointers to other docs. Bad candidates: anything that changes often, secrets, or content so long it crowds out useful context — CLAUDE.md is loaded every session, a real recurring token cost. Keep it current; a stale command from two refactors ago is worse than no CLAUDE.md at all.

settings.json and permission design

Where CLAUDE.md tells Claude what it should know, settings.json tells Claude Code what it's allowed to do — permissions, hooks, and environment variables, at the user or project level.

Permission rules fall into three categories — allow, deny, and ask — targeting a tool or a specific command pattern, not just a whole tool category. Allowing all of Bash is very different from allowing only npm test. When Claude Code is about to use a tool, it checks these rules to decide whether to proceed silently, block outright, or pause for confirmation.

The goal is the narrowest set that still lets the workflow complete — not "deny everything," not "allow everything for convenience":

A permissions block for a team that wants Claude to run tests and linters, read freely, and propose commits, but never push to a remote or silently install packages, looks like this:

{
  "permissions": {
    "allow": [
      "Bash(npm test:*)",
      "Bash(npm run lint:*)",
      "Read",
      "Grep",
      "Glob"
    ],
    "ask": [
      "Bash(npm install:*)",
      "Bash(npm uninstall:*)"
    ],
    "deny": [
      "Bash(git push:*)",
      "Bash(rm -rf:*)",
      "Bash(curl:*)"
    ]
  }
}

Notice git push is denied, not asked — "never push directly" means never, and an ask rule still lets a distracted reviewer approve it out of habit. Package installation is ask because it depends on which package and why.

settings.json can exist at the user level and the project level (checked into the repo, shared with the team). Project-level settings.json standardizes what an agent may do in a codebase without depending on every engineer configuring it themselves. It's also where hooks and environment variables get set — permissions get the most attention, but this file is the general behavior-configuration surface.

Slash commands vs. skills vs. hooks

These three get reached for in overlapping situations, and picking the wrong one produces something that technically works but is annoying to use or unreliable. For slash commands versus skills, the question is who decides when this runs. For hooks, it's different: does this need to happen every time, unconditionally?

Slash commands

A slash command is a reusable prompt template the user invokes explicitly by typing /name — say, a /review-pr command that expands into a prompt checking a diff against your team's style guide. The user is the trigger; Claude never decides on its own to run one. Slash commands fit repeated workflows you always know in advance you want: a release checklist, a changelog draft, a standard review pass.

Skills

A skill is a packaged, discoverable capability with its own instructions and possibly its own resources or scripts, that Claude invokes autonomously when it judges the current task matches the skill's purpose. Claude reads the task, considers the skills available, and decides for itself whether one applies. Skills fit situations where you can't predict in advance exactly when a capability is needed, or where the user shouldn't have to remember a command name — "how to write database migrations in this repo's style" fits a skill better than a slash command, because Claude should reach for it any time a migration task comes up, even described in plain language.

These aren't competing for the same job — a mature setup often has both: a skill that lets Claude autonomously recognize "this looks like a security-sensitive change," plus an explicit /security-review command for forcing that scrutiny on demand. Write for the audience that has to recognize it: a user scanning a command list needs a memorable name; Claude scanning skill descriptions needs a specific description of when it applies, or it'll fire when it shouldn't or get ignored when it should.

Hooks

A hook is a shell command that fires automatically on a lifecycle event — before a tool runs, after a tool runs, when the session stops — configured in settings.json and executed deterministically, with no chance of being skipped because Claude reasoned its way around it.

The distinction that matters: prompted instructions are probabilistic, hooks are deterministic. "Always run the linter before committing" in CLAUDE.md usually works, but it's still natural language interpreted by a model, and under competing instructions or a long context window it can be skipped. If a requirement must happen every time with no exceptions, a hook removes the ambiguity — the harness executes a fixed command rather than the model deciding whether to comply.

Common lifecycle events:

A PreToolUse hook that blocks a dangerous Bash pattern before it executes, even one that slipped past your permission rules, might look like this:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "scripts/check-dangerous-command.sh"
          }
        ]
      }
    ]
  }
}

check-dangerous-command.sh inspects the proposed command, exits non-zero with a reason if it matches a pattern like a force-push, and exits zero otherwise. Hooks and deny rules can look similar — both can block an action — but hooks can also run arbitrary logic beyond a fixed check: validating file contents, checking external state, injecting context back into the conversation. Permissions are a rulebook keyed on tool and command pattern; hooks are a more general enforcement mechanism for anything that must happen unconditionally at a defined lifecycle point, including logic that has nothing to do with permissions, like reformatting a file after every edit.

Plan mode for risky or hard-to-reverse work

Plan mode is a read-only mode: while active, Claude can research the codebase — read files, search, run non-mutating commands — but cannot make edits or run mutating commands. Instead it produces a proposed plan, which a human reviews and explicitly approves before any change happens, rather than discovering what happened after the fact by reading a diff.

Reach for it on changes that are risky, broad in scope, or hard to reverse: a schema migration touching a production database, a refactor spanning dozens of files, changes to authentication or billing logic, or anything where the best approach is genuinely ambiguous. It's not the default for routine work — using it to fix a typo adds friction without adding safety. The judgment call tracks consequence and reversibility, not diff size.

Plan mode is a different mechanism from permissions and hooks. Permissions gate individual tool calls against static rules; hooks deterministically enforce lifecycle behavior; plan mode front-loads a human review of Claude's overall approach for a whole unit of risky work. It doesn't replace good permissions or hooks — those still apply during execution after a plan is approved. Plan mode answers "is this the right approach," not "is this specific command safe to run."

The read-only constraint is about mutation, not information-gathering: Claude can still read files, search, or query a schema while planning — it just can't edit files, run migrations, or commit. Plans produced this way tend to be better grounded than one proposed without investigation, but the checkpoint only has value if approval is a real evaluation — skimming and reflexively approving gets the added latency with none of the safety benefit.

CI/CD and headless usage

Headless mode runs Claude Code non-interactively, with no human watching to answer prompts. This is what embeds it into CI/CD pipelines: a build step that reviews a diff, generates release notes, fixes a failing test, or performs some other bounded task, triggered by a pull request or scheduled job.

The core fact to design around: in an interactive session, an ask rule pauses and waits for you to answer. In headless mode there's no one there to answer. Permissions must be fully configured before the run starts — an ask rule that would have paused politely in your terminal instead stalls or fails the pipeline step.

Practically, a CI-specific settings.json needs allow and deny rules exhaustive enough that the pipeline's task can complete without hitting an unresolved ask. Fixing a stalling pipeline by allowing everything just reintroduces the blanket-allow problem in a higher-stakes environment — unattended write access to your repo and infrastructure. The right fix is precise upfront scoping: convert exactly the ask rules that workflow needs into allow rules, and leave genuinely dangerous operations denied.

Headless mode fits bounded, well-defined tasks with a clear success condition: automated PR review comments, changelog generation, a fix-and-verify loop against a failing test, or triaging an issue and proposing a labeled response. Open-ended, judgment-heavy tasks are a worse fit, since nothing can redirect Claude mid-task if its approach goes sideways — the checkpoint has to be replaced by tight upfront scoping instead.

CI/CD is also a natural place for subagents — separately configured agents with their own system prompt and restricted tool access, delegated a scoped piece of work by a main agent. A pipeline might use one purely for running and interpreting test output, with no permission to touch deployment configuration, keeping that piece's blast radius small even within an already-headless run.

Common mistakes

Studying for the CCA-F exam? The Claude Code curriculum covers this with exam-trap callouts and scenario practice.

← Designing Tools and MCP Integrations Prompt Engineering Patterns That Actually Work →