Post #6 in the Claude Code Toolkit series. Earlier posts: nf-agents, nf-dream, nf-git-workflow, nf-memory, nf-cc-sync. This post is about the smallest skill in the toolkit, the one that fixes a .gitignore you have already written wrong twice.

.gitignore is one of those files where the cost of being slightly wrong compounds silently. A single broad pattern blocks the team-shared config you wanted everyone to see, for a month, until somebody notices. A strict-when-it-should-be-loose env policy means your team starts exchanging .env files through chat.

The two failure modes I see most often:

  • AI coding tools. A repo has .claude/ in .gitignore, which silently blocks the team-shared .claude/settings.json, .claude/skills/, and .claude/agents/ that should be tracked. Same shape with .cursor/, .windsurf/, .continue/. The pattern is too broad. It ignores personal files plus shared config.
  • Env files. A repo uses Vite and the developer adds .env.* to .gitignore. Now .env.development and .env.production (which Vite explicitly tracks as shared defaults) are blocked. The convention falls back to “everyone has to share .env files via chat.”

nf-ignore audits an existing .gitignore for these two failure modes and patches in the right patterns.

The problem this skill solves

The right .gitignore is not generic. It depends on:

  • Which AI coding tools you use (or might use). Each has its own split between team-shared files and personal-local files.
  • What framework the repo uses. Env convention is different for Vite/Next/Nuxt/Astro/SvelteKit/Remix versus plain Node.js (Express, NestJS, scripts).
  • Whether the repo is a monorepo with workspaces that mix conventions.

Writing the full matrix from memory is fine if you do it once a year. Doing it for every new repo, while also not nuking the team-shared .claude/skills/ you added yesterday, is the kind of task you want to delegate to a tool that has the matrix memorized.

The skill encodes the matrix. It detects the framework, picks the correct env policy, applies AI tool patterns that only block personal files, and reports what it changed.

What it does

Two parts, applied in order.

Part 1, AI tool patterns (always applied). Add gitignore entries for personal files only:

.local/
CLAUDE.local.md
.claude/settings.local.json
.cursorignore
.cursorindexingignore
.codeiumignore
.aider*
.codex/
.continue/config.yaml
.continueignore
.cody/ignore
.tabnine*
.amazonq/
.supermaven/

Each entry is the personal-only file for that tool. The shared files (.claude/settings.json, .cursor/rules/*.mdc, .github/copilot-instructions.md, AGENTS.md) stay committed.

Part 2, env file patterns (smart detection). Three scenarios:

  • Framework project (Vite/Next/Nuxt/Astro/SvelteKit/Remix detected): .env*.local plus .envrc. Shared defaults like .env.development stay tracked.
  • Plain Node.js: .env, .env.*, !.env.example, .envrc. All env files are secrets; only the example is tracked.
  • Mixed monorepo: per-workspace rules. Frontend workspaces use the framework pattern; plain Node.js workspaces use the strict pattern.

For the ambiguous case (no detectable config), the skill asks rather than guessing.

How to invoke

/nf-ignore

No arguments. The skill scans the repo, generates a four-section report (Detected, Fix, Add, OK), asks for confirmation, then patches .gitignore. It is invocation-only (the SKILL.md sets disable-model-invocation: true) so it never auto-triggers.

A walkthrough

You just cloned a repo with .gitignore containing .claude/ and .env.*. The repo is an Astro site.

$ /nf-ignore

Detection:
  Framework configs: astro.config.mjs found
  package.json deps: astro, @astrojs/mdx
  Workspaces: none
  Verdict: Astro (framework)

Current .gitignore audit:
  .claude/        too broad: blocks team-shared .claude/settings.json, .claude/skills/
  .env.*          wrong policy: Astro tracks .env.development as shared default

Plan:
  Fix:
    Replace .claude/ with .claude/settings.local.json
    Replace .env.* with .env*.local

  Add:
    .local/
    CLAUDE.local.md
    .cursorignore, .cursorindexingignore
    .codeiumignore
    .aider*
    .codex/
    .continue/config.yaml, .continueignore
    .envrc

  Already OK:
    node_modules
    dist/
    .DS_Store

Apply? Yes / No
> Yes

Patched .gitignore.

Reminder:
  If .claude/settings.json was previously tracked, it still is.
  If .env.development was tracked, run git status to verify it remained tracked.

The before-and-after shows you what changed and why. The reminder is the “go double-check git status” nudge that experienced developers do automatically.

Decision 1: Framework detection is a decision tree, not heuristics

The early prototype tried to guess the env convention from filenames alone. If .env.development exists in the working tree, treat the repo as framework-style. That worked roughly 70 percent of the time and produced strange edits the other 30. A plain Node.js repo with a single misnamed file would get the wrong policy.

The fix was a short ordered decision tree:

1. Framework config files: vite.config.*, next.config.*, nuxt.config.*,
   astro.config.*, svelte.config.*, remix.config.* / react-router.config.*
2. package.json deps/devDeps:
   vite, next, nuxt, astro, @sveltejs/kit, @remix-run/dev, react-scripts
3. Monorepo workspaces (root package.json workspaces or pnpm-workspace.yaml):
   recurse into each workspace dir, apply checks 1 and 2.

If checks 1 and 2 agree on a framework, that is the answer. If they disagree (rare; usually a stale config), prefer the framework that appears in dependencies. If neither check returns a hit, treat the repo as plain Node.js. If detection is ambiguous, ask the user explicitly.

The general lesson: when a tool’s behavior depends on context, derive the context from authoritative signals (the files the framework’s build tool itself reads), not from incidental artifacts (whether a developer happens to have created a particular file by hand). Three checks in order, decidable per check, no fuzzy weighting.

Decision 2: Personal-only patterns, never tool-wide

The first version of the AI tool list had .claude/, .cursor/, .windsurf/ as the entries. One pattern per tool. Simple to maintain.

It was wrong on every line. .claude/ blocks .claude/skills/ which the team should share. .cursor/ blocks .cursor/rules/*.mdc which is the whole point of using Cursor at scale. .windsurf/ blocks .windsurf/rules/.

The fix was to ignore only the personal-local file for each tool, with a comment naming the team-shared file that stays committed:

# Claude Code, personal only
CLAUDE.local.md
.claude/settings.local.json
# Shared: CLAUDE.md, .claude/settings.json, .claude/skills/, .claude/agents/

This pattern (personal vs shared) is now standard across AI coding tools. Most tools split their config the same way: a team-shared rules file or rules folder that should be in source control, plus a personal-overrides file or per-user settings file that should not. A .gitignore rule that does not respect that split is the single most common way teams accidentally hide AI configuration from each other.

The skill’s tool list is current as of 2026-05. New AI editors arrive every quarter; the same convention usually applies (personal file goes in .gitignore, shared file does not). If you adopt a new tool, add its personal-only pattern to the list yourself.

Gotchas

  • Existing patterns are never removed without asking. If your .gitignore already has something the skill does not understand, it surfaces it in the report and asks before removing or replacing. Silent deletion of a rule is exactly the failure mode the skill exists to avoid.
  • git rm --cached is your job, not the skill’s. If .claude/settings.local.json was committed before someone added it to .gitignore, the skill cannot un-track it. You run git rm --cached <file> yourself, then commit the deletion. The skill’s report reminds you.
  • Monorepo workspaces use scoped paths. For apps/web (Vite) plus apps/api (plain Node.js), the skill writes apps/api/.env and !apps/api/.env.example rather than a single repo-wide rule. That is what you want; it makes the .gitignore longer than a single-framework repo’s.
  • The skill is gitignore only. It does not create .env.example, does not move env files, does not run git rm --cached. It edits one file and tells you what else to do. Narrow scope is what makes it safe to re-run after every framework change.

What you should keep even if you never use this skill

Three patterns generalize cleanly:

  1. Personal vs team-shared is the right axis for .gitignore AI tool rules. Never write a tool-wide directory ignore. Always pick the personal file.
  2. Framework env convention is per-framework, not per-developer-preference. Vite, Next, Nuxt, Astro, SvelteKit, Remix all track shared .env* and ignore .env*.local. Plain Node.js ignores everything except .env.example. Pick the rule from the framework’s own docs, not from a generic template.
  3. Detect via authoritative signals. Config files the framework reads, dependencies in package.json. Not “does this file exist in the working tree.”

Get the skill

White-labeled in the claude-skills-toolkit repo alongside the rest of this series.

git clone https://github.com/llawliet11/claude-skills-toolkit.git
cp -r claude-skills-toolkit/nf-ignore ~/.claude/skills/

Folder reference: claude-skills-toolkit/nf-ignore (publishes alongside this post).

Verify in a new session: invoke /nf-ignore in any repo. The first run on an existing repo will probably surface a broad pattern that has been there for a while. After that, run it on every new repo as part of the bootstrap and forget about gitignore for the rest of the project.