[FEATURE] Autocomplete Hook or Per-Project File Include

Resolved 💬 20 comments Opened Nov 25, 2025 by mattheworiordan Closed Jan 6, 2026
💡 Likely answer: A maintainer (hackyon-anthropic, collaborator) responded on this thread — see the highlighted reply below.

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

Summary

Claude Code needs a way for plugins (or per-project configuration) to customize which files appear in @ autocomplete. Currently, the autocomplete only respects .gitignore with no way to un-ignore specific files per project.

The Problem

Many developers have files that are gitignored for valid reasons (not committed to version control) but are useful context for Claude:

  • tasks/ - Personal task tracking
  • journal/ - Development notes
  • docs/private/ - Local documentation
  • .env.example - Environment variable templates
  • TODO.md, NOTES.md - Project notes

These files are invisible to Claude Code's @ autocomplete, making it impractical to reference them.

Current "Solution" is Inadequate

The official workaround (v2.0.27+) requires:

  1. Disable "Respect .gitignore in file picker" globally in /config
  2. Create a .ignore file in every project to re-block sensitive files

Why This Doesn't Work in Practice

| Issue | Impact |
|-------|--------|
| Security risk | ALL gitignored files become visible by default, including .env, credentials, API keys |
| Backwards model | Must opt-out to protect files instead of opt-in to expose them |
| Per-project overhead | Every project needs a .ignore file to block sensitive files |
| No selective un-ignore | .ignore only blocks files - you can't say "show only tasks/ from gitignored files" |
| Global setting | Affects all projects, not just the one where you need specific files |

Real-World Example

I have a personal knowledge base repo with:

.gitignore:
  tasks/
  journal/
  me/private/
  work/
  personal/

These are gitignored because they're personal/private and shouldn't be in version control. But I want Claude to help me with these files.

With the current solution:

  1. I must disable gitignore globally (exposing .env in ALL my projects)
  2. I must create .ignore files in every other project to re-protect sensitive files
  3. I still can't selectively expose just tasks/ - it's all or nothing

What I actually want:

.claudeinclude (or .claudeignore with negation):
  !tasks/
  !journal/

This would expose only specific directories in this one project, without affecting global settings or other projects.

Plugin Attempt: claudeignore

I built a claudeignore plugin to solve this using PreToolUse hooks.

The plugin:

  • ✅ Successfully filters tool access (Read/Write/Edit/Glob/Grep)
  • ✅ Uses gitignore syntax with negation patterns (!tasks/)
  • ✅ Works per-project with zero overhead

But it cannot affect @ autocomplete because:

  • ❌ Autocomplete is implemented in Claude Code's core Rust fuzzy finder
  • ❌ No plugin hooks exist for autocomplete customization
  • ❌ No settings control autocomplete behavior

This makes the plugin impractical - users can't discover files with @, they must manually type paths.

Proposed solution

See below.

Why This Matters

  1. Security: The current "disable gitignore globally" approach is a security anti-pattern
  2. Usability: Many legitimate use cases require gitignored files as context
  3. Consistency: Cursor has .cursorignore with negation support - Claude Code should match
  4. Plugin ecosystem: Plugins can't extend Claude Code in useful ways without autocomplete hooks

Related Issues

Implementation Notes

The claudeignore plugin in this repository demonstrates:

  • Pattern matching with the ignore npm package (gitignore spec compliant)
  • Per-project activation (zero overhead when not used)
  • Hierarchical file discovery
  • Session caching for performance

This code could be adapted for a core Claude Code implementation.

---

Without this feature, the plugin ecosystem cannot solve common developer workflow problems, and the official workaround creates security risks.

Proposed Solution

Any of these would solve the problem:

Option 1: Autocomplete Hook (Most Flexible)

Add a PreAutocomplete hook that plugins can use:

{
  "hooks": {
    "PreAutocomplete": [{
      "matcher": ".*",
      "hooks": [{
        "type": "command",
        "command": "node autocomplete-filter.js"
      }]
    }]
  }
}

Input: { "query": "tasks", "files": [...] }
Output: Modified file list or additional files to include

Option 2: Per-Project Include Setting (Simplest)

Add includeGitIgnored to .claude/settings.json:

{
  "includeGitIgnored": [
    "tasks/",
    "journal/",
    "docs/private/"
  ]
}

These patterns would be added to autocomplete even if gitignored.

Option 3: Negation Support in .ignore

Allow .ignore to use negation patterns:

# Block sensitive files
.env
credentials.json

# But show these gitignored directories
!tasks/
!journal/

This would make .ignore work like proper gitignore syntax.

Option 4: .claudeinclude File

A dedicated file for files to include:

# .claudeinclude
tasks/
journal/
docs/private/
*.example

Alternative Solutions

_No response_

Priority

High - Significant impact on productivity

Feature Category

Interactive mode (TUI)

Use Case Example

Scenario: I maintain a personal "second brain" repository for notes, tasks, and knowledge management. I use Claude Code to help me organize, search, and work with this content.

My Repository Structure

matt-os/
├── .git/
├── .gitignore          # Contains: tasks/, journal/, me/private/, work/, personal/
├── me/
│   ├── profile.md      # Public - checked into git
│   ├── goals.md        # Public
│   └── private/        # GITIGNORED - personal notes
│       ├── health.md
│       └── finances.md
├── tasks/              # GITIGNORED - my todo lists
│   ├── today.md
│   ├── work.md
│   └── personal.md
├── journal/            # GITIGNORED - daily notes
│   ├── 2025-01-15.md
│   └── 2025-01-16.md
└── docs/               # Public documentation
    └── README.md

Step 1: I Open Claude Code in My Repo

cd ~/Projects/matt-os
claude

Step 2: I Want to Reference Today's Tasks

I type @tasks hoping to autocomplete to tasks/today.md.

What happens now: Nothing appears. The tasks/ directory is gitignored, so Claude Code's file picker ignores it entirely.

What I want: tasks/today.md, tasks/work.md, etc. should appear in autocomplete.

Step 3: The Current Workaround is Painful

To use the official solution, I would need to:

  1. Run /config and disable "Respect .gitignore in file picker" globally
  2. Now every project I work on exposes all gitignored files (including .env files with API keys!)
  3. Go to every other project and create a .ignore file to re-block sensitive files
  4. Remember to do this for every new project I clone or create

This is completely impractical for someone who works across dozens of repositories.

Step 4: What I Actually Want

Create a .claudeinclude or .claude/settings.json in just this one repo:

{
  "includeGitIgnored": [
    "tasks/",
    "journal/",
    "me/private/"
  ]
}

Now when I type @tasks, I see my task files. When I type @journal, I see my journal entries. Only in this repo. No global settings changed. No security risk in other projects.

Step 5: Working with Claude on Private Content

With the feature working, my workflow becomes:

> @tasks/today.md what's my top priority today?

Claude: Looking at your tasks/today.md, your top priority is...

> Move the "Review PR #123" task from @tasks/work.md to @tasks/today.md

Claude: I'll move that task for you...

This is the natural workflow that gitignored files currently break.

Why This Can't Be Solved with Plugins

I built a [claudeignore plugin](./README.md) that successfully filters tool access using PreToolUse hooks. The plugin works perfectly for blocking/allowing Read/Write/Edit operations.

But it cannot affect @ autocomplete because:

  • The autocomplete is implemented in Claude Code's core Rust fuzzy finder
  • No plugin hooks exist for autocomplete customization
  • The plugin only intercepts tool execution, not the UI file picker

So even with my plugin installed and configured with !tasks/:

  • Typing @tasks shows nothing (autocomplete still ignores it)
  • Manually typing "read tasks/today.md" works (plugin allows the tool call)

The UX is broken - users can't discover files, they must already know the exact paths.

Additional Context

_No response_

View original on GitHub ↗

20 Comments

github-actions[bot] · 7 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/5105
  2. https://github.com/anthropics/claude-code/issues/1248
  3. https://github.com/anthropics/claude-code/issues/4904

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

mattheworiordan · 7 months ago

It's very similar to #4904. I am not sure this is a duplicate as I am proposing a very different approach. Very open to merging this too.

JuanCS-Dev · 7 months ago

Feature Analysis: .gitignore Autocomplete for File Tools

Smart feature request. File tools should respect .gitignore by default.

Current Behavior

Problem: Claude reads/lists files that should be ignored:

> List files in project
- node_modules/ (15,000 files)
- .git/ (2,000 files)
- dist/ (500 files)
- src/ (50 files) ← what you actually want

Impact:

  • Context bloat (listing 17K files instead of 50)
  • Slow performance (scanning ignored directories)
  • Irrelevant suggestions (autocomplete shows node_modules)

Proposed Solution

Add respect_gitignore parameter:

interface ReadParams {
  path: string;
  respect_gitignore?: boolean;  // Default: true
}

interface ListDirectoryParams {
  path: string;
  recursive?: boolean;
  respect_gitignore?: boolean;  // Default: true
}

Implementation using ignore library:

// src/tools/filesystem/FileFilter.ts

import ignore from 'ignore';
import fs from 'fs/promises';

class GitignoreFilter {
  private ig: ReturnType<typeof ignore>;
  
  async load(projectRoot: string) {
    const gitignorePath = path.join(projectRoot, '.gitignore');
    
    if (await fs.exists(gitignorePath)) {
      const content = await fs.readFile(gitignorePath, 'utf8');
      this.ig = ignore().add(content);
    } else {
      // Default ignores if no .gitignore
      this.ig = ignore().add([
        'node_modules',
        '.git',
        'dist',
        'build',
        '.DS_Store'
      ]);
    }
  }
  
  shouldInclude(filepath: string): boolean {
    const relative = path.relative(projectRoot, filepath);
    return !this.ig.ignores(relative);
  }
}

Integration in Read tool:

async function read(params: ReadParams): Promise<string> {
  const filter = await GitignoreFilter.load(params.path);
  
  if (params.respect_gitignore !== false) {  // Default true
    if (!filter.shouldInclude(params.path)) {
      throw new ToolError(
        `File ${params.path} is gitignored. ` +
        `Use respect_gitignore=false to override.`
      );
    }
  }
  
  return fs.readFile(params.path, 'utf8');
}

Integration in List Directory:

async function listDirectory(params: ListDirectoryParams): Promise<FileInfo[]> {
  const filter = await GitignoreFilter.load(params.path);
  const allFiles = await fs.readdir(params.path, { recursive: params.recursive });
  
  if (params.respect_gitignore !== false) {
    return allFiles.filter(f => filter.shouldInclude(f.path));
  }
  
  return allFiles;
}

Configuration

// ~/.claude/config.json
{
  "tools": {
    "Read": {
      "respect_gitignore": true,
      "custom_ignores": [
        "*.log",
        "tmp/",
        "cache/"
      ]
    },
    "ListDirectory": {
      "respect_gitignore": true,
      "max_files": 1000  // Safety limit
    }
  }
}

UI Feedback

When files are filtered:

> List files in project

Found 17,523 files
Filtered by .gitignore: 17,473
Showing: 50 files

src/
  index.ts
  utils/
    helper.ts
  ...

💡 Tip: Use respect_gitignore=false to include ignored files

Performance Optimization

Cache gitignore patterns:

class GitignoreCache {
  private cache = new Map<string, GitignoreFilter>();
  
  async get(projectRoot: string): Promise<GitignoreFilter> {
    if (this.cache.has(projectRoot)) {
      return this.cache.get(projectRoot);
    }
    
    const filter = new GitignoreFilter();
    await filter.load(projectRoot);
    this.cache.set(projectRoot, filter);
    return filter;
  }
  
  // Invalidate when .gitignore changes
  watchGitignore(projectRoot: string) {
    fs.watch(path.join(projectRoot, '.gitignore'), () => {
      this.cache.delete(projectRoot);
    });
  }
}

Alternative: Smart Defaults

Instead of parameter, use smart detection:

async function shouldIgnoreFile(filepath: string): Promise<boolean> {
  // 1. Check .gitignore
  if (gitignoreFilter.ignores(filepath)) return true;
  
  // 2. Check common patterns
  if (filepath.includes('node_modules')) return true;
  if (filepath.includes('.git/')) return true;
  
  // 3. Check file size
  const stats = await fs.stat(filepath);
  if (stats.size > 10 * 1024 * 1024) return true;  // >10MB
  
  return false;
}

Related: Add .claudeignore

Custom ignore file for Claude-specific exclusions:

# .claudeignore
*.min.js
*.bundle.js
*.map
test/fixtures/
docs/generated/

Why useful: Separate from .gitignore (git tracks minified files, Claude shouldn't read them)

---

Verdict: HIGH value feature, reduces noise significantly.
Recommend: Implement with respect_gitignore=true as default.

cansar · 7 months ago

Strong +1.

edruder-block · 7 months ago

Note: the .git/info/exclude file in any project acts similarly to .gitignore but is not under source control. It's useful for ignoring personal files & directories in a way that doesn't "pollute" the committed .gitignore file. The .git/info/exclude file should be treated like the .gitignore file, I think.

NikkeTryHard · 7 months ago

+1

PaulRBerg · 7 months ago

Thanks for opening this feature @mattheworiordan! I've bumped into the same problem.

Just a favor request from a fellow human - could you tweak your slash command/skill that produces these verbose issues to create shorter issue bodies/contents? Or maybe nest the verbose content under <details> collapsible blocks.

Humans review these issues, too 😁

See my /create-issue and /create-cc-issue slash commands.

---

My use case - I put the files generated by claude under a git-ignored ./ai directory, which I want to reference in my Claude chats, but which I never want to be part of version control.

eXamadeus · 7 months ago

Uggh, the github-actions bot on this repo is INSANELY overzealous. It locked https://github.com/anthropics/claude-code/issues/5105 which is wild.

Hell, it tried to shut this one down. Anthropic, leash your auto-destroy bot a bit. It's feral and foaming at the mouth.

mattheworiordan · 7 months ago
could you tweak your slash command/skill that produces these verbose issues to create shorter issue bodies/contents? Or maybe nest the verbose content under <details> collapsible blocks.

Sure. Honestly didn't see this as an issue, but happy to improve it. Which bits do you think are not human friendly but still needed OOI?

PaulRBerg · 7 months ago

thanks @mattheworiordan - I was referring to the sheer length of the issue.

mattheworiordan · 6 months ago
Uggh, the github-actions bot on this repo is INSANELY overzealous. It locked https://github.com/anthropics/claude-code/issues/5105 which is wild.

I know 🤷

I am still struggling with Claude Code ignoring critical files that are not git committed, such as a local .working folder, which I use for ephemeral prompts/task tracking. Claude Code has decided if it's not git committed it's not important enough for it to know about it. WTF.

mattheworiordan · 6 months ago

@hackyon-anthropic you mentioned in https://github.com/anthropics/claude-code/issues/5105#issuecomment-3712861033

Just submitted a change to add per-project setting for this one, so you can add "respectGitignore": false to settings.json or settings.local.json and have this setting be applied locally. Should be available later in the week!

This is great, but AFAICT, the .ignore file does not allow negative matches for @ matching files, see https://github.com/anthropics/claude-code/issues/5105#issuecomment-3560120763. I've tried this too and have the same issue.

So in spite of this, this is all still a bit half-baked.

Are you able to look at that issues so that .gitignore + .ignore works as expected for access and @ mentions?

P.S. Why are issues closed so aggressively (7 days after the last comment https://github.com/anthropics/claude-code/issues/5105#issuecomment-3591700728) and then remain closed even when you've just added a note to it? Surely you should allow people to reply at least instead of polluting the system with nosie in other tickets (i.e. this post)?

PaulRBerg · 6 months ago
Why are issues closed so aggressively (7 days after the last comment

Seconded. Issues should remain open for longer

hackyon-anthropic collaborator · 6 months ago

@mattheworiordan have u tried doing something like this in your .ignore?

node_modules/*
!node_modules/donotignore/
hackyon-anthropic collaborator · 6 months ago

I just added support for allowing respectGitignore in settings.json: https://github.com/anthropics/claude-code/issues/5105#issuecomment-3712861033

we also understand that some folks have custom setups and want to have their own autocomplete script, so we have recently added that functionality using the file suggestion setting here: https://code.claude.com/docs/en/settings#file-suggestion-settings

mattheworiordan · 6 months ago
have u tried doing something like this in your .ignore? node_modules/* !node_modules/donotignore/

Yes, ! is ignored, that is my point @hackyon-anthropic.

hackyon-anthropic collaborator · 6 months ago

@mattheworiordan might have been a separate issue/bug, but that pattern for exclusion should work once my latest changes roll out later this week! (tested it and it works!)

mattheworiordan · 6 months ago

Ok, great, thanks @hackyon-anthropic. If you are able to confirm when it's available for download, I'd love to test it

hackyon-anthropic collaborator · 6 months ago

The fix should have been pushed with the latest versions.

Note that we now have stable vs latest release channels, so if you're on the stable channel, it may take longer for it to update. I expect version 2.1.5 to have the fix.

github-actions[bot] · 5 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.