prepare-repo-context · v1.9.0 · 2026-09-05 · sha256 7d47738f4391f2ca
prepare-repo-context v1.9.0A
Immutable. This exact content is served forever at /api/v1/blob/7d47738f4391f2ca.
---
name: prepare-repo-context
description: Use when a developer wants to inspect, prepare, refresh, query, or understand bounded repository context without loading or indexing the full codebase into the model context.
license: MIT
metadata:
author: The Agentic Fieldbook
version: "1.9.0"
---
# Prepare Repo Context
Prepare reusable local repository context with deterministic tooling while keeping the model context small.
Let `<skill-dir>` be this skill's directory. Resolve the repository, then resolve `<python>` to `python3` or a Python 3 `python`. If Git or Python 3 is unavailable, report the missing prerequisite; do not install anything.
0. If the `repo-context` MCP tools (`inspect`, `build`, `repository_map`, `search_symbols`, `search_docs`, `source_snippets`, `related_symbols`, `changed_symbols`, `impact_candidates`, `repository_overview`, exposed by this plugin's bundled server) are available in this session, use them for the inspect, build, and query steps below instead of the script: the tool arguments mirror the script's flags and the results are the same objects. The consent rules do not change: pass `confirm_state_write: true` to `build` only after the user approved writing state in this conversation. `activate`, `gc`, `remove`, and `hook` are script-only; run them as described here.
1. Run `<python> <skill-dir>/scripts/prepare_repo_context.py inspect --repo <repo>` exactly once. This read-only inspection is authorized by invoking the skill. Do not scan source files yourself.
2. Summarize only the returned engine availability, freshness, eligible/excluded path counts, state usage, required authorizations, and `next_safe_action`. Do not put an index, repository-wide file list, or source content into the model context.
3. If `next_safe_action` is `build-index`, explain the estimated scope and ask for state-write authorization. After approval, run `<python> <skill-dir>/scripts/prepare_repo_context.py build --repo <repo> --confirm-state-write` exactly once and report its compact result. The initial `build --confirm-state-write` is standing consent for incremental refresh and for pruning superseded index generations of that repository; that pruning runs after every `build`, `activate`, and query, not only after a refresh, so the repository converges to one referenced generation once each superseded one has aged past its grace period.
4. If it is `install-native-engine`, explain the estimated scope and ask once for network plus state-write authorization. After approval, run `<python> <skill-dir>/scripts/prepare_repo_context.py activate --repo <repo> --confirm-network --confirm-state-write` exactly once. This downloads the matching release runtime, verifies its SHA-256 checksum, installs it in user-local TAF state, and prepares the index. Report only the compact result.
5. Once `next_safe_action` is `use-index`, stop if the user asked only to prepare context. If they asked a repository question, choose the operation, query text, and filters with `<skill-dir>/references/query-routing.md`, then run exactly one read-only query: `query --operation repository-overview`, `query --operation repository-map`, `query --operation search-symbols --query <term>`, or `query --operation search-docs --query <term>`. An unfamiliar repository ("how is this organized", "where is the code", "where do I start") starts with `query --operation repository-overview`: it needs no query text and no identity, and one call returns `groups`, one row per directory prefix with its file, definition, entry-point, document, and configuration counts, its languages, and a representative identity; `overview`, naming the described root, the counted files, and how many directories the `*` row folds together; and a ranked file layer in `findings` that leads with entry points and well-known entry file names. Narrow it to one subtree with `--path-prefix D/`; that value must name whole directory segments, so a file path or a partial segment answers with an empty table and the warning `overview-root-not-a-directory`, and naming several prefixes describes only the first in sorted order with the warning `overview-root-first-prefix`. It accepts `--path-prefix` and `--language` and rejects `--query`, `--result-id`, `--direction`, `--base`, `--symbol-kind`, and `--source-type`. Its group table has no fixed width; the output budget sizes it, and the table and the file layer take at most half of it each: a table over its half folds its tail into the `*` row until it fits, a table inside its half is kept whole, and the file layer keeps whatever the table did not spend. So a wider budget buys a wider table as well as more files: this operation defaults to 8000 characters rather than the 4000 the others use, `--maximum-output-characters 12000` widens the table further, and `--path-prefix D/` is how to go deeper into one subtree rather than wider over all of them. A relationship question ("who calls X", "what does X depend on", "who uses module M", "what does X import") is a two-step flow: first run `search-symbols` (or `repository-map`) to get the anchor's `result_identity`, then run `query --operation related-symbols --result-id <identity> --direction callers|callees|importers|imports` with that identity; do not guess an identity or reuse one from a different repository or a stale query. A change question ("what did I change on this branch", "what could my change break") needs no identity and is one step: `query --operation changed-symbols` returns the definitions, entry points, and modules whose lines a changed hunk touches, and `query --operation impact-candidates` returns their one-hop callers and importers, each candidate carrying in `anchors` the changed symbols it depends on. Both compare the working tree (committed, staged, unstaged, and untracked changes together) with a base resolved as the branch's upstream main, then `origin/HEAD`, then a local `main`/`master`; add `--base <ref>` only for a base the user named. "What am I about to commit / what does my staged change affect" is `changed-symbols --staged` or `impact-candidates --staged` instead: it measures the index against `HEAD` exactly as `git commit` would record it, excluding unstaged and untracked edits; `--staged` is exclusive with `--base`, and over MCP the same request is `staged: true`. `--base` is rejected by every other operation, and both reject `--query`, `--result-id`, and `--direction`. `impact-candidates` follows at most 64 changed symbols and asks the engine one relationship question per changed symbol and direction (`callers` for a changed definition or entry point, `importers` for a changed module or definition), so its cost grows with the change set; narrow a large change set with `--path-prefix` (the filters apply to the changed set of both operations) before widening `--maximum-results` — `--path-prefix` narrows the changed side, not the affected side, so name the directory you changed (the library) and the candidates show where it is used (the app); narrowing to a leaf directory such as `app/` yields no candidates. `impact-candidates` answers in two layers, the change set and the candidates, so it defaults to 8000 output characters rather than 4000 as well. A `query` on a repository whose bound index is behind the working tree refreshes the index incrementally inside the same call (the `refresh` block in the result says whether it did and how many paths changed); do not run `build` after edits or commits. Run `build --confirm-state-write` only when `inspect` reports `rebuild-index` (a runtime upgrade, or a change the broker cannot express as a delta), or when `query` says `incremental refresh failed`. Read the result with `<skill-dir>/references/result-contract.md`. Fetch source only when the one-line `preview` is not enough, with `query --operation source-snippets --result-id <identity>` using identities returned by an earlier query; a `related-symbols` finding synthesized at module scope (`record_kind: module` with no enclosing function) refuses `source-snippets` by design, so report its `path`/`start_line` directly instead. Keep the default output budget unless the user explicitly needs more evidence. Over MCP the same rules apply to `repository_map`, `search_symbols`, `search_docs`, `source_snippets`, `related_symbols`, `changed_symbols`, `impact_candidates`, and `repository_overview` (which takes only `path_prefixes`, `languages`, `allow_inferred`, and the two budgets); `result_ids` takes the identities that `--result-id` would and `base` takes what `--base` would, while `impact_candidates` accepts no filters, only `base`, `allow_inferred`, and the two budgets.
6. Offer the commit-time impact hook when the user asks to be warned at commit time, to "not forget dependents", or is about to commit right after a change question. Run `<python> <skill-dir>/scripts/prepare_repo_context.py hook status --repo <repo>` once; it writes no launcher, and like `inspect` it performs the standing-consent incremental refresh of the bound index. Report its `hook` state (`installed`, `foreign`, `absent`, or `redirected`) and its `readiness.next_safe_action`. Explain what installing writes before asking: a `pre-commit` launcher inside the repository's own hooks directory, never tracked by Git, advisory by default — a header, at most five indented file lines, and a trailer on stderr, it never blocks a commit, and it stays silent whenever the index is not ready — and, when TAF's user-local state exists, the small `hook/launcher-target` pointer under it. Ask for hook-write authorization; after approval, run `<python> <skill-dir>/scripts/prepare_repo_context.py hook install --repo <repo> --confirm-hook-write` exactly once. Never install without `--confirm-hook-write`. The flag records the user's approval given in this conversation; a request to set up the warning starts this procedure and is not that approval. Add `--chain` only when `status` reported `foreign` and the user wants that existing hook kept: it is moved aside to run before TAF's report, and its exit code still blocks the commit; a chained hook that cannot be run at all (deleted, or no longer executable) is skipped instead, and `install --chain` refuses a foreign hook that is not executable, since git was not running it either. `redirected` means `core.hooksPath` points elsewhere; TAF installs only under the repository's own hooks directory, so report this instead of installing. The hook speaks only while `inspect` reports `use-index`; `build` remains its own separate consent, never triggered by the hook. Its report is one header line - `TAF impact: N file(s) depend(s) on this change and are not in this commit`, `N` counting the untouched production files, or the test files instead when no production file depends but a test does - then at most five indented detail lines, each `<path>:<reference_line>` padded so every `<-` column aligns, then `<- <qualified name>` naming the changed symbol; production files print first, and test files take a detail line only when no production file depends. A trailing indented line, present whenever something was left out, names the remaining files and points at the agent instead of a command: `... and <n> more, plus <t> test files (ask your agent to list TAF impact for this commit)`, trimmed to whichever count is nonzero, `<n>+` when the engine itself omitted candidates in some direction, or `... and possibly more (...)` when nothing exact remains to name. The header is bold on a real TTY stderr with `NO_COLOR` unset and `TERM` not `dumb`; every other line, and every line on a non-TTY stderr (GUI clients, CI, pipes), is plain ASCII. The report is written with one blank line above and below it, and a chained hook runs first, so the block is the last thing the commit writes. A commit that was checked and came back clean says so in one line without those blank lines - `TAF impact: no untouched dependents (N changed symbols)`, or `TAF impact: no indexed symbols changed` when the staged change touched no indexed symbol at all - while every outcome that did not check anything (`TAF_HOOK=0`, the index not ready, an unreadable staged set, no `HEAD`, the 3-second wait, no interpreter) stays completely silent. The hook is not interactive by default; a prompt inside pre-commit would hang GUI clients, CI, and an agent's own commits, so add `--mode confirm` to `hook install` only when the user asks to be asked before such a commit. That launcher writes the same report and then asks, on its own marked line on the controlling terminal, whether to continue: only `y` or `yes` commits, while Enter, `n`, anything unrecognised, end of input, and no answer within 60 seconds (`TAF_HOOK_CONFIRM_TIMEOUT`) all abort the commit, because acting for the person means taking the safe action. Where nobody can be asked at all the question is skipped and the commit proceeds - `/dev/tty` cannot be opened, `CI`, `CLAUDECODE` or `AI_AGENT` is set, or `TAF_HOOK_CONFIRM=0` - since a question nobody sees must never block a commit. The clean line is never followed by a question. Under `--chain`, answering `n` aborts a commit whose chained hook already ran, so anything it re-staged stays staged. `status` reports the installed launcher's mode as `hook_mode` (`advisory`, `confirm`, or null). Questions about the full list go to the agent afterwards. `<python> <skill-dir>/scripts/prepare_repo_context.py hook remove --repo <repo> --confirm-hook-write` undoes the install. `TAF_HOOK=0 git commit` silences one commit without touching the launcher. The launcher follows the broker that last ran on this machine (a pointer file every successful `prepare` command but `hook run` refreshes, under TAF's own user-local state), so a plugin update is picked up automatically by the next TAF session. `status` reports three fields about it: `launcher_current` means the installed launcher runs this plugin's broker - `false` means it does not, and re-installing fixes it; `launcher_text_current` is the stricter, older byte-for-byte comparison alone, and `false` there needs nothing on its own, since the pointer can still make the launcher current even when its embedded fallback text is not; `launcher_generation` names which launcher template is installed (`pointer` for the current self-healing one, `embedded` for an older TAF launcher that predates it). A hook manager that appends its own block after TAF's launcher trips `launcher_text_current: false` but leaves `launcher_current: true`, since TAF's own conditional still runs before whatever was appended - a re-install would still rewrite the whole file and drop that appended block, but nothing requires it. The hook's own query performs the same standing-consent incremental refresh and superseded-generation prune as every query; it never builds, activates, downloads, or removes state.
Current repository identity, worktree identity, commit, dirty fingerprint, and native freshness must agree before context is described as ready. When `state.orphan_count` is nonzero or `state.root_bytes` is large, you may mention that `<python> <skill-dir>/scripts/prepare_repo_context.py gc` (or `remove --repo <repo>` for this repository) reports reclaimable state; both delete only with `--confirm-state-write`, which requires the user's explicit state-write authorization. A nonzero `state.incompatible_generation_count` means the state still holds an index an older runtime wrote, which this one cannot read: `gc` lists such a record under the category `incompatible-generation`, and a `build --confirm-state-write` removes it and rebuilds under that same authorization, reporting the warning `incompatible-generation` and the old runtime in `engine.replaced_generation_version`. An exact-binding `partial` context with `next_safe_action: use-index` is usable bounded coverage; report its warnings without rebuilding it. Report query findings with their paths, line ranges, evidence class, and preview; a preview is a display hint, never evidence, and you do not infer beyond the evidence class. An impact answer names candidates, not defects: a candidate is a symbol that references something you changed, so report it with its anchors and leave the judgement of whether it breaks to a review of that call. A failed preparation never blocks ordinary Git-based skills.