---
name: iterative-retrieval
description: Find the right local context for a sub-agent through short search, review, and refine loops.
---

# Iterative Retrieval

## Credit

**Original pattern by Affaan Mustafa.**

This version keeps the same core idea and adds clearer steps and safety checks.

## Purpose

Use this skill when a sub-agent needs local project context.

A sub-agent may not know:

- Which files matter
- What names the project uses
- Where a feature starts
- Which tests or types support it

Do not send the whole project. Do not guess once and stop. Search in small rounds.

Do not use the web or make outside calls. Search only the local project data that the user allowed you to read.

## The Four-Step Loop

Run this loop up to three times:

1. **Search** for likely files.
2. **Review** each result.
3. **Refine** the search.
4. **Repeat** until the context is enough.

## Step 1: Search

Start with the task words and likely file paths.

```javascript
const query = {
  patterns: ["src/**/*.ts", "lib/**/*.ts"],
  keywords: ["auth", "user", "session", "token"],
  excludes: ["node_modules/**", "dist/**", "*.test.ts"]
};

const files = await retrieveLocalFiles(query);
```

Use project search tools when they exist. Follow the project rules in `AGENTS.md`, `CLAUDE.md`, or similar files.

Start broad, but set clear limits:

- Search likely source folders first.
- Skip build output and vendor code.
- Limit the number and size of results.
- Search file names, code names, and task words.
- Do not read secret files, key files, or hidden login data.

## Step 2: Review

Score each file from `0` to `1`.

```javascript
function reviewFiles(files, task) {
  return files.map(file => ({
    path: file.path,
    score: scoreFile(file, task),
    reason: explainScore(file, task),
    gaps: findMissingContext(file, task)
  }));
}
```

Use these score bands:

- `0.8` to `1.0`: Directly handles the task
- `0.5` to `0.79`: Has a needed type, call, rule, or test
- `0.2` to `0.49`: May help, but is not central
- Below `0.2`: Not useful for this task

Give a short reason for each kept file. A name match alone is not enough. Check what the file does.

Look for gaps such as:

- The caller is missing
- A shared type is missing
- A config value is missing
- The matching test is missing
- The code uses another name for the same idea
- A generated file points to a real source file

## Step 3: Refine

Use what you learned to make the next search better.

```javascript
function refineQuery(review, oldQuery) {
  return {
    patterns: unique([
      ...oldQuery.patterns,
      ...findRelatedPaths(review)
    ]),
    keywords: unique([
      ...oldQuery.keywords,
      ...findProjectTerms(review)
    ]),
    excludes: unique([
      ...oldQuery.excludes,
      ...findSafeExcludes(review)
    ]),
    focus: unique(
      review.flatMap(item => item.gaps)
    )
  };
}
```

Refine with facts from the code:

- Add names of called functions.
- Add imported type names.
- Add route, event, table, or config names.
- Search for callers and tests.
- Search for the project term when it differs from the task term.
- Exclude a whole path only when it is clearly not useful.

Do not keep adding the same words. If a new search would be the same as the last one, stop.

## Step 4: Repeat or Stop

Stop when the files answer these questions:

- Where does the behavior start?
- Where is the main behavior defined?
- What data, type, or config does it use?
- Is there a test or example?
- Is any key link still missing?

Three good files may be enough. Do not require a fixed file count.

```javascript
async function iterativeRetrieve(task, maxRounds = 3) {
  let query = createInitialQuery(task);
  let best = [];
  let lastQuery = null;

  for (let round = 1; round <= maxRounds; round++) {
    if (sameQuery(query, lastQuery)) break;

    const files = await retrieveLocalFiles(query);
    const review = reviewFiles(files, task);
    best = mergeWithoutCopies(
      best,
      review.filter(item => item.score >= 0.7)
    );

    if (hasEnoughContext(best, review, task)) {
      return makeContextReport(best, review, round);
    }

    lastQuery = query;
    query = refineQuery(review, query);
  }

  return makeContextReport(best, [], maxRounds);
}
```

Never hide missing context. If three rounds are not enough, return the best files and list the open gaps.

## Edge Cases

### No Results

Check for:

- A different word used by the project
- A different file type
- A different source folder
- A symbol name from an error or stack trace
- A route, test, import, or config that points to the code

If there are still no results, say so. Do not make up file names.

### Too Many Results

Narrow by:

- Source folder
- Exact function or type name
- Caller or import link
- File type
- Test name
- Route or config key

Read small parts first. Do not load every full file.

### Very Large Files

Read the matching function and nearby code. Then read its imports, callers, or tests as needed.

### Generated or Vendor Files

Do not use them as the main source when a real source file exists. Use them only to find that source.

### Duplicate Files

Keep one copy of the same content. Note if files are mirrors or generated copies.

### Mixed Languages

Search both the task word and the terms found in code. Keep the final report in English.

### Secrets or Private Data

Do not read or return secrets, tokens, cookies, private keys, or login files. A matching secret file is not valid task context.

### Broken Links

If an import, file path, or named symbol does not exist, report the broken link. Do not guess what it should contain.

### Unclear Task

Start with the exact words in the task. If two very different meanings remain after one round, ask one short question before going deeper.

## Concrete Example

Task:

```text
Fix the bug where an expired login token is not renewed.
```

Round 1:

```text
Search:
  Paths: src/**
  Words: token, auth, expired, renew

Found:
  src/auth/auth.ts                 0.90
  src/auth/token-store.ts          0.82
  src/users/user.ts                0.25

Learned:
  The project uses "refresh" instead of "renew".
  auth.ts calls refreshSession().
  The caller and tests are still missing.
```

Round 2:

```text
Search:
  Words: refreshSession, refresh token, session
  Focus: callers and tests

Found:
  src/session/session-manager.ts   0.95
  test/session-refresh.test.ts     0.88

Stop:
  The start, main code, token store, and test are known.
```

Return:

```markdown
## Context Found

- `src/auth/auth.ts`: Checks token age and starts refresh.
- `src/auth/token-store.ts`: Reads and saves token data.
- `src/session/session-manager.ts`: Defines `refreshSession`.
- `test/session-refresh.test.ts`: Tests expired token refresh.

## Open Gaps

None.
```

## Sub-Agent Prompt

Use this prompt when you send the task:

```markdown
Find the local files needed for this task.

1. Search with task words and likely paths.
2. Score each file from 0 to 1.
3. State why each kept file matters.
4. List any missing caller, type, config, or test.
5. Refine and search again.
6. Stop when the code path is clear, or after three rounds.
7. Return files with a score of 0.7 or more.
8. If context is still missing, say what is missing.
9. Do not read secrets or use the web.
```

## Final Output

Return a short report with:

- The files to give the sub-agent
- One reason per file
- The number of rounds used
- Any open gaps
- Any files skipped for safety or because they were made by a build step

Do not return low-score files just to make the list longer.

## Related Work

- [Affaan Mustafa's Longform Guide](https://x.com/affaanmustafa/status/2014040193557471352), in the sub-agent coordination part
- The `continuous-learning` skill
- Agent files in `~/.claude/agents/`