AI Development Tutorial Intermediate

How to Use Claude Code on an Existing TypeScript Project

Add Claude Code to a TypeScript repo you already have: a CLAUDE.md that matches your real build, permission rules that block dangerous commands, and a typecheck loop the agent runs itself.

A terminal running Claude Code beside a TypeScript project tree

Tested with

ClaudeCode
2.x
Node
22.12.0 LTS
Typescript
5.9
Bun
1.3
OperatingSystem
Ubuntu 24.04 LTS and macOS 15

Before you start

  • A TypeScript repository that builds locally today
  • Node.js 22 or newer, or Bun 1.2 or newer
  • Git, with a clean working tree before you start
On this page

Claude Code attaches to a repository you already have. There is no migration, no index to build and no directory layout it expects.

The work is in three files: a CLAUDE.md that describes how your project actually builds, a .claude/settings.json that says which commands may run without asking, and a hook that runs your typechecker after every edit.

What this setup gives you

A session that can run bun run typecheck and bun test on its own, read and edit anything under src/, and refuse to touch .env, dist/ or your git remote without an explicit prompt.

It also gives you diffs small enough to actually review. That is the part most teams skip, and it is the part that decides whether the output is useful.

This guide does not cover CI integration, self-hosted model gateways, or running Claude Code non-interactively in a pipeline.

Requirements

  • A TypeScript repo that builds today. If tsc --noEmit already fails, fix that first — the agent will otherwise chase pre-existing errors.
  • Node.js 22.12 or newer (Claude Code ships as an npm package).
  • Git, with a clean working tree. Every session should start from a commit you can return to.

Install Claude Code and open the repo

  1. Install the CLI globally.
  2. Change into your project root — not a subdirectory. Claude Code treats the directory you launch it from as the workspace boundary.
  3. Start a session and authenticate when prompted.
bash
npm install -g @anthropic-ai/claude-code
cd ~/code/my-api
claude

Confirm the install resolved before you go further:

Expected output
bash
$ claude --version
2.0.14 (Claude Code)

Write a CLAUDE.md that matches how the repo really builds

Inside the session, run /init. It scans the repo and writes a first-draft CLAUDE.md at the root. That draft is a starting point, not the answer — it tends to describe what a project usually looks like rather than what yours does.

Rewrite it around commands you have personally run. The file is loaded into every session, so wrong information here is expensive.

CLAUDE.md
# my-api

TypeScript 5.9, Node 22, Bun as the package manager and test runner.
Strict mode is on. `any` fails lint.

## Commands
- `bun install` — install dependencies
- `bun run typecheck``tsc --noEmit -p tsconfig.json`
- `bun test` — Vitest, run from the repo root
- `bun run lint` — ESLint, flat config in `eslint.config.ts`

## Layout
- `src/routes/` — Hono route handlers, one file per resource
- `src/db/` — Drizzle schema and queries; migrations are generated, never hand-edited
- `src/lib/` — shared helpers with no framework imports

## Rules
- Never edit `src/db/migrations/**` — regenerate with `bun run db:generate`
- Every new route needs a test in `src/routes/__tests__/`
- Do not add dependencies without asking

Three properties make this file work: every command is copy-pasteable, every path is real, and the rules are things a reviewer would otherwise have to say out loud on a pull request.

Keep it short. Two hundred lines is already long; at that size the agent starts missing instructions buried in the middle.

Splitting CLAUDE.md across a monorepo

In a monorepo, put a short root CLAUDE.md describing the workspace layout and a per-package CLAUDE.md in each app directory. Claude Code reads the file in the current working directory and walks up toward the repo root, so the nearest package file wins on specifics while the root file supplies shared conventions.

Restrict what the agent is allowed to run

By default every shell command stops and waits for approval. That is safe but slow, and approval fatigue leads people to hit “always allow” on things they should not.

Pre-approve the narrow set of commands you want running unattended, and deny the rest explicitly.

.claude/settings.json
{
  "permissions": {
    "allow": [
      "Bash(bun install)",
      "Bash(bun run typecheck)",
      "Bash(bun run lint)",
      "Bash(bun test:*)",
      "Bash(git status)",
      "Bash(git diff:*)",
      "Read(./src/**)",
      "Edit(./src/**)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Edit(./dist/**)",
      "Bash(git push:*)",
      "Bash(curl:*)"
    ]
  }
}

Commit that file. Personal overrides go in .claude/settings.local.json, which should be in .gitignore.

The trailing :* is a prefix match: Bash(bun test:*) allows bun test, bun test src/routes, and bun test --watch. Without it, only the exact string matches. Run /permissions inside a session to see what is actually in effect — that is faster than reasoning about rule precedence.

Give the agent a typecheck loop it can run itself

The single change that most improves output quality on a TypeScript repo is letting the agent see compiler errors without you relaying them.

A PostToolUse hook runs a command after every file edit:

.claude/settings.json
{
  "permissions": { "allow": ["Bash(bun run typecheck)"] },
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "bun run typecheck" }
        ]
      }
    ]
  }
}

On a large codebase tsc --noEmit on every edit is too slow. Turn on incremental builds so repeat runs are cheap:

tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "incremental": true,
    "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo"
  }
}

Add node_modules/.cache to .gitignore if it is not already covered.

Work in slices small enough to review

Give one task per session and end it. A session that has run for two hours has accumulated context you cannot audit, and its later edits are worse than its early ones.

A loop that holds up:

  1. git switch -c feat/rate-limit — branch before you start.
  2. State the task in one sentence, including where the code should live.
  3. Let the agent write and typecheck.
  4. Read git diff yourself. Not the agent’s summary of it — the diff.
  5. Commit, then /clear before the next task.

Step 4 is not optional. The agent’s summary of its own change is a description of intent, and intent and diff diverge quietly.

Use /clear rather than /compact between unrelated tasks. Compaction keeps a lossy summary of the previous task, which shows up as the agent “remembering” decisions you already reverted.

Common errors

claude: command not found after a successful global install. The npm global bin directory is not on your PATH. Check where it went and add it:

bash
npm config get prefix
# /home/you/.npm-global  ->  add /home/you/.npm-global/bin to PATH

Error: Cannot find module 'typescript' when the agent runs typecheck. It launched the command from a subdirectory, or dependencies are not installed. State the working directory rule in CLAUDE.md: all commands run from the repo root.

The agent edits a file, then edits it back. Two instructions in CLAUDE.md contradict each other, or a lint rule fights a formatting rule. Run bun run lint and your formatter manually — if they disagree with each other, the agent is stuck in the same loop you are.

Claude requested permissions to use Bash, but you haven't granted it yet. The command does not match an allow rule. Add the prefix form rather than approving one-off — repeated approval prompts are how overly broad rules get added later.

Typecheck passes but the build fails. tsc --noEmit and your bundler do not always resolve the same way, particularly with paths aliases. Add the real build command to CLAUDE.md so the agent runs it before claiming done.

Security and production notes

Treat agent-authored commits as untrusted input until reviewed. Branch protection and required review apply exactly as they do for human commits.

Deny network commands (curl, wget, package publish) unless a task genuinely needs them. A denied rule that occasionally costs you an approval prompt is cheaper than an exfiltration path.

Never grant Bash(git push:*). Pushing is the one action that leaves your machine, and it takes two seconds to do yourself.

Keep .claude/settings.local.json gitignored, and audit .claude/settings.json in code review like any other config. A widened allow list is a security change.

Frequently asked questions

Does Claude Code need the whole repository in context?

No. It reads files on demand through search and read tools rather than loading the tree up front. What is always in context is your CLAUDE.md, so keep that file short and factual — under roughly 200 lines — and let the agent discover everything else.

Should CLAUDE.md be committed to git?

Yes. CLAUDE.md is shared project knowledge and belongs in version control next to the code it describes. Keep personal overrides in `.claude/settings.local.json`, which should be gitignored.

Can I stop the agent from touching generated or vendored code?

Add deny rules for those paths in `.claude/settings.json`, for example `Edit(./dist/**)` and `Edit(./node_modules/**)`, and state the same rule in CLAUDE.md so the agent does not waste turns trying.

Why does the agent keep asking to run the same command?

The command does not match any allow rule. Run `/permissions` inside the session to see the active rules, then add a prefix rule such as `Bash(bun test:*)` so the whole family of commands is pre-approved.

Sources

  1. Claude Code overview Anthropic Primary source Accessed 2 Jul 2026
  2. Manage Claude's memory (CLAUDE.md) Anthropic Primary source Accessed 2 Jul 2026
  3. Claude Code settings and permissions Anthropic Primary source Accessed 2 Jul 2026
  4. Claude Code hooks Anthropic Primary source Accessed 2 Jul 2026
  5. tsc CLI options Microsoft Primary source Accessed 2 Jul 2026
5 min read

Part of a learning path

  • Agents intermediate

    An AI Coding Agent Workflow That Keeps Diffs Reviewable

    A working loop for running a coding agent on a real codebase — task scoping, branch isolation, tests as the contract, and the review discipline that stops unreviewed code reaching main.

    5 min
  • Cloudflare intermediate

    How to Deploy an Astro Site to Cloudflare Workers

    Ship a static or server-rendered Astro site to Cloudflare Workers with Wrangler — adapter setup, wrangler.jsonc, nodejs_compat, secrets, custom domains and the errors you hit on the first deploy.

    4 min
  • DevOps intermediate

    How to Build a GitHub Actions Deployment Pipeline

    Test, build a container image, push it to GHCR and deploy it to a VPS over SSH — with the concurrency guard, environment approval and token scoping that keep the pipeline from becoming the weak point.

    4 min AI-assisted