# Tensor Grep Run And Operate

> Use when running the `tg` CLI day-to-day — exact syntax for orient, search --rank, defs/refs/callers/blast-radius, agent, docs-coverage, context, session open/refresh/serve/daemon, checkpoint, scan --ruleset, run, mcp, doctor, dogfood, upgrade; the symbol-command 0/1/2 exit-code contract and what an agent should branch on; bounding a scan with `--deadline` and reading `partial`/`result_incomplete`/`deadline_limit` truncation flags; excluding vendor/skill trees with `--ignore` on orient/agent/docs-coverage; `tg context --max-tokens` budgets (default 16000, 0=opt-out); where JSON artifacts and cache state land on disk; starting the MCP server; or a whole-repo `tg search` that hangs. The OPERATOR runbook (how to invoke), not theory or audit workflow.

- Skill: `oimiragieo/tensor-grep-run-and-operate` (Agent Skill)
- Install (CLI): `npx skillmds@latest add oimiragieo/tensor-grep-run-and-operate`
- Raw SKILL.md: https://api.skillmd.com/api/skills/oimiragieo/tensor-grep-run-and-operate/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: oimiragieo (https://skillmd.com/u/oimiragieo)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/oimiragieo/tensor-grep-run-and-operate

---


# tensor-grep run & operate

An imperative, copy-pasteable runbook for **running** `tg` (the tensor-grep CLI). Ground-truthed
against `src/tensor_grep/cli/main.py` at **released v1.96.0**, re-verified
**2026-07-24**. Every command below is a real `@app.command` in that file — re-verify with the
commands in [Provenance and maintenance](#provenance-and-maintenance) before trusting a flag on a
newer version. `main.py` churns ~100+ lines per release, so treat every `main.py:NNNN` cite as an
approximate anchor: `grep` the symbol, don't trust the raw line.

## Scope — and when to use a sibling instead

| You need | Use |
| --- | --- |
| **How to type the command / where does output go** | **this skill** |
| Search theory (BM25, trigram index, ripgrep internals, AST/tree-sitter concepts) | `code-search-and-retrieval-reference` |
| The blast-radius-before-editing audit workflow, `callers` truncation caveats | `tensor-grep-code-audit` |
| WHY the front door / registration / backend contract is shaped this way | `tensor-grep-architecture-contract` |
| Adding a `tg` command or search flag; gates before merging any tg change | `tensor-grep-change-control` |
| A live bug you just hit while running `tg` | `tensor-grep-debugging-playbook` |
| A hang/`--deadline` overrun on a large repo, or the unscoped-search-refusal campaign history | `tensor-grep-large-repo-scale-campaign` |
| "Has this already been tried and lost?" before re-attempting a fix | `tensor-grep-failure-archaeology` |
| Which commands accept `--deadline`/`--ignore`/`--max-tokens` (NOT the same set); env-var/flag axis list | `tensor-grep-config-and-flags` |
| Building from source, Rust/Python toolchain setup | `tensor-grep-build-and-env` |
| **Interpreting** `doctor`/`dogfood` fields or a `result_incomplete`/`partial`/`deadline_limit`/exit-`2` payload — what it PROVES | `tensor-grep-diagnostics-and-tooling` |
| Running the pytest/ruff/mypy gates; the exit-code-contract test pattern (`tests/unit/test_cli_deadline_flag.py`) | `tensor-grep-validation-and-qa` |
| Claiming/reading a speed number | `tensor-grep-benchmark-and-proof-toolkit` |
| Release mechanics, publish gates, external positioning claims | `tensor-grep-release-and-positioning` |

If you are about to *change* `tg` code rather than *run* it, stop and load
`tensor-grep-change-control` first — this skill assumes the CLI surface as shipped.

## 0. Confirm you are running the real thing first

Every command below assumes a working, correctly-resolved `tg` on PATH. Windows in particular can
silently resolve a stale or foreign `tg` (see `.claude/skills/tensor-grep/SKILL.md` "Start Here").
Before trusting any output:

```powershell
tg --version
tg doctor --json
```

`tg doctor --json` (§8) is the single first check for launcher drift, version mismatch, and
Windows shell escaping pitfalls — run it, not `tg --version` alone, when something looks wrong.

## 1. Command-anatomy quick table (the moat commands)

All symbol/orientation commands are **path-first**: `tg <command> PATH [SYMBOL|QUERY]`. `PATH`
defaults to `.` (current directory) everywhere below unless noted. Add `--json` to any of them for
machine-readable output (`--json` is a real per-command flag, not a global one — pass it after the
command name).

| Command | Positional form | Purpose |
| --- | --- | --- |
| `tg orient PATH` | `PATH` | One-call codebase orientation: central files by import in-degree, entry points, symbol map, AST snippets |
| `tg search PATTERN PATH --rank` | `PATTERN PATH` | Text search with a real hit re-ranked by BM25 relevance instead of grep order |
| `tg find PATH "query"` | `PATH QUERY` | Whole-repo natural-language hybrid search (BM25 [+ CPU dense] -> RRF -> budget-fitted `file:line`); v1.77.0, #189 |
| `tg defs PATH SYMBOL` | `PATH SYMBOL` | Exact definition locations |
| `tg source PATH SYMBOL` | `PATH SYMBOL` | Full source block for a symbol |
| `tg refs PATH SYMBOL` | `PATH SYMBOL` | References to a symbol |
| `tg callers PATH SYMBOL` | `PATH SYMBOL` | Call sites + likely impacted tests |
| `tg blast-radius PATH SYMBOL` | `PATH SYMBOL` | Callers + transitive file/test impact |
| `tg imports FILE` | `FILE` | Forward file-dependency edges (#74; O(1), no repo scan) |
| `tg importers FILE [ROOT]` | `FILE [ROOT]` | Reverse file-dependency edges (bounded scan; `--deadline` on large roots) |
| `tg evidence emit PATH` | `PATH` + `--capsule`/`--manifest` | Aggregate prior outputs into an EvidenceReceipt |
| `tg codemap PATH` | `PATH` | Browsable folder→file→symbol map (`--out`; slow — prefer `/tmp`) |
| `tg map PATH` | `PATH` | AST-indexed repo map (files/symbols/coverage); takes `--deadline` (`main.py` `def map`, grep the symbol) and sits under the §11a three-state contract — a scan-truncated map exits `2` (added to this table 2026-08-12; it was missing) |
| `tg agent PATH "query"` | `PATH QUERY` | Actionable Context Capsule — prefer `PATH/src` for speed |
| `tg session open PATH` | `PATH` | Create a cached repo-map session (returns `session_id`) |
| `tg scan --ruleset NAME` | (flag-driven) | Run a built-in security/compliance AST rule pack |
| `tg run PATTERN PATH` | `PATTERN [PATH]` | Bounded AST structural search / guarded rewrite |
| `tg mcp` | — | Start the MCP stdio server |
| `tg doctor` | `PATH` | System/GPU/cache/AST/daemon/shell diagnostics |
| `tg route-test PATH "query"` | `PATH QUERY` | Diagnose routing agreement between `context-render` and `edit-plan` for one query -- reports `agreement`/`warnings` |
| `tg prepare PATH "task"` | `PATH QUERY` | One-call edit readiness: composes orient→search→agent→route-test→callers→evidence→ledger into a single call (primary target, confidence, blast-radius floor, validation commands, claim/evidence coordination hooks) |
| `tg ledger claim\|release\|list\|record\|find` | (subcommand-driven) | Advisory multi-agent coordination: claim/release/list a symbol/file scope (Slice 1); record/find a content-addressed finding for reuse (Slice 2, repo-root canonicalized since #850) |
| `tg dogfood` | (flag-driven) | Wraps `agent_readiness.py` into one verdict + JSON |
| `tg upgrade` | — | Upgrade the installed `tensor-grep` package |
| `tg calibrate` | -- (no positional; delegates to the native binary) | Measure CPU-vs-GPU crossover thresholds; exit 1 with a remediation pointer if no CUDA-enabled native binary is installed (#596). See `docs/gpu_crossover.md`. |
| `tg devices [--json] [--format text\|json]` | (flag-driven) | Print routable GPU device IDs + VRAM inventory (`collect_device_inventory`); the CLI counterpart of the `tg_devices` MCP tool (S7). See `docs/gpu_crossover.md`. |

## 2. Orientation and content search

```powershell
tg orient C:\repo --json
tg orient C:\repo --max-tokens 6000 --max-central-files 15          # widen the capsule
tg orient C:\repo --ignore "vendor/**" --ignore "core/skills/**"    # drop vendor/skill trees from ranking
```
`orient` (`grep -n "^def orient" src/tensor_grep/cli/main.py` -- `:9422` as of 2026-08-14, was `:8761`) takes `path` (default `.`), `--max-tokens` (default 3000, `orient`'s
snippet-token budget — **not** the same axis as `context --max-tokens`, §14), `--max-central-files`
(default 10), `--ignore` (repeatable glob), `--json`.

`--ignore GLOB` (repeatable) excludes a subtree from the **centrality ranking** so vendor/skill CODE
trees do not outrank real hubs on a harness or monorepo. The glob matches the file basename **or** the
repo-relative path (`--ignore 'seo/**' --ignore 'core/skills/**'`). Receipt: shipped for `orient` in
`#392` after a ~1900-file TS repo dogfood showed a central-skills tree crowding out the real code
hubs; the twin flag on `tg agent` (`#397`) is below. This is a *ranking* exclusion, not a scan
exclusion — the files are still walked, just kept out of the "central files" / "primary target" list.
(Note: `tg search --ignore` is a **different**, boolean flag — "respect ignore files" — not this glob;
`tg docs-coverage --ignore` (§13) is a coverage exclusion. See `tensor-grep-config-and-flags`.)

`orient`'s JSON also carries a **`suggested_ignore`** field (`orient_capsule.py:1094`,
`_suggested_ignore_from_deweighted_trees`) -- ready-to-paste `--ignore` globs for whatever
auto-de-weighted vendor/skill trees it found (de-weight, never hard-exclude, by default). **v1.75.0
(#593, "M1+M2") broadened this from narrow nested-manifest islands to whole vendor/skill trees**: a
new STRONG-0 promotion fires on 5 unambiguous vendor-dir basenames alone (`node_modules`, `vendor`,
`third_party`, `_vendored`, `external_repos` -- no manifest needed), and a new STRONG-3 shape
heuristic detects a whole `skills/`-named tree whose children look like independent leaf skills with
no imports crossing out of the tree (a genuine imported `skills/` package stays un-deweighted). M2 in
the same PR also added `suggested_ignore` parity to **`tg agent --json`**, which previously never
surfaced it at all even though `tg agent` runs the identical de-weight during ranking -- additive-only,
mirroring `suggested_scope`'s convention (present only when non-empty).

```powershell
tg search "invoice tax" C:\repo --rank --json
```
`--rank` (alias `--bm25`, `main.py:7062`) re-ranks ripgrep hits by BM25 lexical relevance —
pure CPU, no API key, no model download. Default `--format` for plain search is `rg` (exact
ripgrep-style text); use `tg search PATTERN PATH --format rg --json` for ripgrep JSON Lines, or
`--json` alone for tensor-grep's own aggregate JSON object, or `--ndjson` for tensor-grep's
flattened streaming rows. These three JSON shapes are **not interchangeable** — `--json` is NOT
`rg`'s JSON Lines schema (`--json`/`--rank`/`--ndjson`/`--format` at `main.py:7054-7202`, in
`search_command`).

```powershell
tg agent C:\repo "change invoice tax rounding" --json
```
`agent` (`grep -n "^def agent" src/tensor_grep/cli/main.py` -- `:10331` as of 2026-08-14, was `:9640`) is opt-in and takes `path` then positional `query` (not `--query`, which
is a hidden deprecated alias, `main.py:9341-9468`). Key flags: `--max-files` (3), `--max-sources`
(5), `--max-tokens` (1200), `--max-repo-files` (2000, `_DEFAULT_AGENT_REPO_SCAN_LIMIT`; raised from
the old 512 — see §11a's exit-code history below), `--provider native|lsp|hybrid`,
`--gpu-device-ids` (opt-in native GPU evidence only — sidecar-routed GPU is reported unsupported),
and `--ignore GLOB` (repeatable, `main.py:9386`) — the same vendor/skill-tree ranking exclusion as
`orient` above, here keeping a vendor/skill tree from being picked as the capsule's **primary target**
on a harness repo (`#397`). Before editing from a capsule, check top-level `ambiguity.status`:
`"tie_requires_confirmation"` is a hard stop for autonomous edits.

`TG_CAPSULE_INLINE_CALLERS` (default-OFF, v1.92.0/#697): prepends `# tg: callers=N (top: a, b)` to the
primary snippet's source and adds an additive `snippets[i].inline_structural_annotation` field, reusing
already-collected blast-radius evidence (no new scan) at a ~+2.8% token cost. See
`tensor-grep-config-and-flags` for the full env-var entry.

## 2a. One-call edit readiness — `tg prepare`

```powershell
tg prepare C:\repo\src "improve session daemon timeout" --json
tg prepare C:\repo\src "task" --claim --json      # also submits an advisory ledger claim
tg prepare C:\repo\src "task" --out capsule.json --json   # persists the capsule to FILE, byte-identical to stdout JSON
```

`prepare` (`grep -n "^def prepare" src/tensor_grep/cli/main.py` -- `:11144` as of 2026-08-14, was `:10862`) composes the orient→search→agent→route-test→callers→evidence→ledger loop
into one call: `path` then positional `query`, default 60s deadline (`DEFAULT_AGENT_CLI_DEADLINE_SECONDS`,
same class as `tg agent`'s cold-path default), `--claim` (opt-in only, submits an advisory
`tg ledger claim`), `--out FILE` (`main.py:10562`, v1.93.0/#705 — atomic write, refuses a symlink/
dangling-symlink/directory destination, works with `--text` too). Response carries `primary_target` +
`confidence` + `ask_user_before_editing`, `validation_commands`, a `blast_radius_floor`
(`callers_count`/`top_callers`/trust summary), and `coordination.claim`/`coordination.evidence` hooks so
a caller doesn't have to guess the follow-up argv. A truncated/deadline-partial result reuses the same
symbol-command exit contract as §11a below (exit 2 on any `partial`/`result_incomplete`, regardless of
whether a primary target was found) via `_scan_incomplete`.

## 2b. Multi-agent coordination — `tg ledger` (EXPERIMENTAL, advisory)

```powershell
tg ledger claim C:\repo --symbol open_session --agent-id "$AGENT_ID" --json
tg ledger list C:\repo --json              # rolls scope UP -- any subtree PATH under the same repo sees it
tg ledger release C:\repo --symbol open_session --agent-id "$AGENT_ID" --json
tg ledger record C:\repo --receipt receipt.json --artifact-kind evidence-receipt --symbol open_session --agent-id "$AGENT_ID" --json
tg ledger find C:\repo --symbol open_session --artifact-kind evidence-receipt --fresh-only --json
```

Never blocks an edit — a claim is advisory, and overlaps are reported for the caller to decide, not
enforced. Slice 1 (`claim`/`release`/`list` -- `main.py`'s `ledger_claim`/`ledger_release`/`ledger_list`) canonicalizes its store to
the nearest `.git` ancestor (worktree-aware; v1.93.0/#706 — before this, each command resolved the store
from the literal PATH argument, so `claim core/hooks` + `list .` silently used two different stores).
Slice 2 (`ledger_record` -- `grep -n "^def ledger_record" src/tensor_grep/cli/main.py`, `:17510` as of 2026-08-14, was `:17323`; `ledger_find` -- `grep -n "^def ledger_find" src/tensor_grep/cli/main.py`, `:17604` as of 2026-08-14, was `:17417`) got the SAME fix in #850
(v1.101.16): both call `_ledger_physical_root`, so `record core/hooks` + `find .` now hit one store.
This line said "unchanged and still literal-path-rooted" for three releases after it stopped being
true -- a reader would treat a subtree miss as expected instead of a regression, or avoid Slice 2
entirely. Re-grep the `main.py:NNNNN` cites; they drift every release. `find`'s exit contract is a distinct 3-state family from §11a's
symbol-command contract: `0` = at least one fresh finding (revision matches, safe to reuse); `1` =
nothing matched, or matches exist but none are fresh (recompute); `2` = fail-closed (missing `--symbol`,
corrupt index/blob). Full command reference: `tensor-grep-ledger`.

## 3. Symbol navigation (`defs` / `source` / `refs` / `callers` / `blast-radius`)

```powershell
tg defs C:\repo open_file --json
tg source C:\repo open_file --json
tg refs C:\repo open_file --json
tg callers C:\repo open_file --json
tg blast-radius C:\repo open_file --json
```

All five share the same positional contract: `path` then `symbol_arg`, both optional Typer
arguments (`defs` (`grep -n "^def defs" src/tensor_grep/cli/main.py` -- `:11924` as of 2026-08-14, was `:11475`) and `blast-radius` (`grep -n "^def blast_radius" src/tensor_grep/cli/main.py` -- `:12813` as of 2026-08-14, was `:12047`)). If you type them reversed
(`tg defs SYMBOL PATH`), the CLI auto-detects it — `path` that fails `Path(path).exists()` and a
present `symbol_arg` get swapped, with a warning on stderr — but **write path-first** to avoid the
extra hint round-trip (`_maybe_swap_reversed_positionals` -- `grep -n "^def _maybe_swap_reversed_positionals" src/tensor_grep/cli/main.py`, `:11795` as of 2026-08-14, was `:11346`; called from
`_resolve_path_and_symbol` -- `grep -n "^def _resolve_path_and_symbol" src/tensor_grep/cli/main.py`, `:11862` as of 2026-08-14, was `:11413`). A bare `tg defs SYMBOL` (single arg) resolves against
the current directory.

A hidden `--symbol` / `--query` flag still works and prints a deprecation warning to stderr
(`main.py:11056`) — treat it as legacy, not the contract; the positional form is canonical.

Common flags: `--provider native|lsp|hybrid` (default `native`), `--max-repo-files` (2000,
`_DEFAULT_AGENT_REPO_SCAN_LIMIT` — raised from the old 512 the #398→#399 exit-code history below
calls out as "the actual friction"), `--json`. `callers`/`refs`/`impact`/`blast-radius` also take
`--deadline SECONDS` to wall-clock-bound the scan (§12) — **and so do `defs`/`source`** (plus
`orient`/`context`/`docs-coverage`); the #232/`#585` waves extended it well past the original
graph-commands-only set. Derive it, don't trust this sentence: `tg defs --help | grep deadline`.
(This line read "`defs`/`source` do **not**" until 2026-08-01, while §12's own table listed both as
taking it and the pitfall table below explicitly warned against believing a stale "these don't take
it" claim — the doc carried its own correction and its own error at the same time. The `--deadline`
option on `defs` even carries an in-source comment naming the v1.71.3 dogfood fix that added it.
**A contradiction inside one document is not resolved by whichever half you read first.**)
`blast-radius`
additionally takes `--max-depth` (3), `--max-callers` (25), `--max-files` (25) (in the `blast_radius`
def, `grep -n "^def blast_radius" src/tensor_grep/cli/main.py` -- `:12813` as of 2026-08-14, was `:12047`+). `defs` additionally takes `--class TEXT` to disambiguate a common method name
by its enclosing class (`main.py:11128`).

**Truncation contract (read §11 before scripting an exit code):** when a `callers`/`refs`/`impact`/
`blast-radius` JSON payload carries `"result_incomplete": true` (a scan cap) or `"partial": true`
(a `--deadline` cutoff), the scan did **not** finish — treat the list as a floor, never as proof of
zero callers. The exit code encodes this too, and truncation **trumps** found:
`main.py:11344-11470` raises `typer.Exit(2)` on ANY `partial`/`result_incomplete`
(found OR empty) BEFORE the not-found check, and the comment at `main.py:11334-11466`
records #399's "found-but-truncated exits 0" as tried and overturned by a unanimous
design council (§11). The full audit decision procedure (P2 = truncation,
P7 = "zero callers != dead code") lives in `tensor-grep-code-audit`; this skill covers how to invoke
the command and how to branch on its exit/flags.

**`tg callers` is Python-first — prefer `tg refs` for TS/JS symbol navigation.** Call-site
resolution matches Python AST call nodes most reliably and can under-match or run for minutes on
large TypeScript/JS repos. Dogfood receipt (v1.19.3, `AGENTS.md`): on a TS-heavy repo, `tg refs`
returned 14 reference sites for a symbol where `tg callers` returned 1. Prefer `tg refs` there —
and still cross-check with `tg scan`/grep, because the call graph cannot see
set/list/decorator/dispatch-table registration sites, so even a COMPLETE zero-caller result is
not proof of dead code.

## 4. Session lifecycle — open, refresh, serve, daemon

Sessions cache the repo-map so repeated context/edit-plan/blast-radius calls skip re-indexing.

```powershell
tg session open C:\repo --json                    # returns session_id; capture it
tg session list                                    # sessions for the current root (no ID)
tg session show SESSION_ID
tg session refresh SESSION_ID C:\repo              # after file changes
tg session context-render SESSION_ID C:\repo "query"
tg session edit-plan SESSION_ID C:\repo "query"
tg session blast-radius SESSION_ID C:\repo SYMBOL
tg session serve SESSION_ID C:\repo                # reads JSONL requests from stdin, --jsonl is default-on
tg session daemon start C:\repo --json             # start/reuse the warm localhost daemon
tg session daemon status C:\repo --json
tg session daemon stop C:\repo --json
```

`session open` (`main.py:12284`) takes `path` (default `.`) and `--max-repo-files` (default 512,
the agent-safe cap — this literal is unrelated to the agent-family 2000 default in §2/§3, still 512
as of this pass). `session refresh` (`main.py:12477`) and every `session <subcmd> SESSION_ID
[PATH]` command require `session_id` as the **first** positional argument — it is not implicit.
`session serve` (`main.py:13155`) additionally accepts `--refresh-on-stale` to refresh once and
retry a request when file changes are detected mid-stream; passing `--no-jsonl` errors (JSONL is
currently the only serve mode, `main.py:13173`). `session context-render` / `session context` accept
`--max-tokens` (default 16000, `0` = unbounded) — see §14.

`session daemon start/status/stop` (`session_daemon_app`, `main.py:238`) each take only
`PATH` — there is no CLI flag for the daemon's idle/uptime limits; those are environment-only:
`TG_SESSION_DAEMON_IDLE_SECONDS` and `TG_SESSION_DAEMON_MAX_UPTIME_SECONDS`. `daemon start` prints
`host:port` and `pid`; `daemon status`/`stop` report whether a daemon is currently `running` for
that root. `tg session list` and `tg session daemon status` will discover nearby session scopes
when the current directory has no direct session metadata of its own.

## 5. Checkpoints (rewind before a risky rewrite)

```powershell
tg checkpoint create C:\repo --json
tg checkpoint list C:\repo --json                  # one detected scope
tg checkpoint list C:\repo --discover --json        # bounded child-scope discovery
tg checkpoint list C:\repo --discover-full --json   # exhaustive, can be slow on broad roots
tg checkpoint undo CHECKPOINT_ID C:\repo --json
tg checkpoint undo --last C:\repo --json            # restore the newest checkpoint in scope
```

`checkpoint undo` (`main.py:13362`) takes `checkpoint_id` as an optional positional, or `--last` to
restore the newest checkpoint for `path` without naming an ID — do not pass both
(`main.py:13381`/`13510`). If `checkpoint_id` resolves to an existing filesystem path, the error
message suggests `--last` explicitly (`main.py`'s `checkpoint_undo`), which is a strong signal the two
positionals (`checkpoint_id`, `path`) got confused.

## 6. AST scan (built-in rule packs) and structural run/rewrite

```powershell
tg rulesets --json                                          # list built-in packs
tg scan --ruleset RULESET_NAME --path C:\repo\api --json    # narrowest useful root first
tg scan --config sgconfig.yml --json                        # custom ast-grep project config
tg scan --rule my-rule.yml --json                            # single custom rule, no sgconfig
```

`scan` (`grep -n "^def scan" src/tensor_grep/cli/main.py` -- `:14533` as of 2026-08-14, was `:14000`) accepts positional `PATHS`, or `--path` (default `.`) when using a
built-in ruleset — the two are mutually exclusive (`main.py`'s `scan`), as are `--rule`,
`--ruleset`, and `--inline-rules` with each other (the same guard in `scan`). Useful narrowing flags:
`--glob`/`-g`, `--type`/`-t`, `--max-depth`, `--filter`/`-f` (regex over loaded rule IDs). Baseline
workflow: `--baseline FILE` / `--write-baseline FILE` compare or snapshot matched-finding
fingerprints; `--suppressions FILE` / `--write-suppressions FILE` mark or record accepted findings
(writing suppressions requires `--justification TEXT`). `--allow-broad-generated-scan` opts into an
otherwise-refused scan of a generated/cache/dependency/multi-project root — prefer scoping first.

`--ruleset` also accepts a handful of RESOLVE-ONLY 1:1 mental-model aliases on top of the 6
canonical pack names (`rule_packs.py`'s `_RULE_PACK_ALIASES`): `auth`->`auth-safe`,
`secrets`->`secrets-basic`, `crypto`->`crypto-safe`, `tls`/`ssl`->`tls-safe`,
`subprocess`->`subprocess-safe`, `deserialize`/`deserialization`->`deserialization-safe`. A real
pack name always wins over an alias; aliases never appear in `tg rulesets`/`list_rule_packs()`.
`security` names the shared category all 6 packs belong to, not one pack, so it raises an
actionable error listing the 6 packs instead of guessing.

```powershell
tg run "function_definition" C:\repo\src --lang python --json
tg run --pattern 'def $NAME($$$ARGS): $$$BODY' --rewrite 'def $NAME($$$ARGS) -> None: $$$BODY' C:\repo --apply --verify
```

`run` (`grep -n "^def run" src/tensor_grep/cli/main.py` -- `:17740` as of 2026-08-14, was `:17149`) takes the AST pattern positionally (or via `--pattern`/`-p`) and an
optional `PATH`; supplying only a path that exists with no pattern is a hard error
(`main.py`'s `run`, `typer.Exit(2)`), not a silent zero-match. `--rewrite`/`-r` sets the replacement,
`--apply` writes it, `--verify` runs tests after applying, `--checkpoint` wraps the apply in a
checkpoint, `-U`/`--update-all` is an ast-grep-compatible alias for apply-all (requires
`--rewrite`). Read-only structural-search extras: `--selector`, `--strictness`, `--stdin`,
`--globs` (repeatable, prefix `!` to exclude), `--filter` (text regex over matched nodes),
`--files-with-matches`. PowerShell users must single-quote patterns containing `$` captures (e.g.
`'def $NAME($$$ARGS): $$$BODY'`) or PowerShell expands `$NAME` before `tg` sees it.

KEY FACT: `tg run` already IS ast-grep when the `sg` binary is on PATH -- `AstGrepWrapperBackend`
delegates the pattern to `sg run -p <pattern>` verbatim (`ast_wrapper_backend.py:146`), so
`$NAME`/`$$$ARGS`/`--selector`/`--strictness` are already 100% ast-grep-compatible with no
translation layer. When `sg` is absent, a native-shaped pattern (no `$`) still runs through tg's
own tree-sitter `AstBackend`, but that backend speaks a DIFFERENT query DSL than ast-grep
(task #141) -- a `$`-metavariable pattern is never silently rerouted there (that would silently
mistranslate and return wrong matches, worse than an honest empty result). Instead
`_select_ast_backend_for_pattern` raises a fail-closed `ConfigurationError`, which `run_command`
now catches and reports as a clean `Error: ...` message + exit `2` (mirroring the Task #166
`ConfigurationError` handling in `main.py`'s search path), never a raw Python traceback. A
zero-match `tg run` (exit `1`, not an error) additionally emits static/heuristic remediation via
`_emit_ast_run_remediation` -- idiom shapes (`def $NAME($$$ARGS): $$$BODY`,
`function $NAME($$$) { $$$ }`), a `tg ast-info` pointer, and cheap "no `$`" / "no `--lang`" hints
-- on stderr for text modes and as an additive `"remediation"` `--json` key; this is `tg run`-only
and never fires on `tg scan` (a 0-finding scan is a clean pass, exit `0`).

## 7. MCP server

```powershell
tg mcp
```

Starts a **stdio** MCP server (`FastMCP("tensor-grep")`, `mcp_server.py:120`, `anyio.run` over
`_run_mcp_stdio_async` -- `grep -n "def _run_mcp_stdio_async" src/tensor_grep/cli/mcp_server.py`, `:7951` as of 2026-08-14, was `:7847-7859`) — it is meant to be launched by an MCP client
(Claude Desktop, an agent harness), not run interactively and left open in a terminal.

Call `tg_mcp_capabilities` **first** in any new client/sandbox — it reports which tools work
without a standalone native `tg` binary versus which require one (`mcp_server.py:1948`).

Representative tool names (**58 advertised with the default `TG_MCP_LEGACY_TOOLS` ON — but the
surface was REORGANIZED post-v1.96.0**, #98 MCP consolidation Phase-1, verified 2026-08-12:
**46 legacy tools** (the individual names below) are gated behind `TG_MCP_LEGACY_TOOLS`
(default **ON**; `_legacy_tools_enabled()` / `_register_legacy_tool` in `mcp_server.py`);
**10 task-shaped meta-tools** (`tg_navigate`, `tg_impact`, `tg_query`, `tg_context`,
`tg_explore`, `tg_session`, `tg_scan`, `tg_audit`, `tg_checkpoint`, `tg_rewrite` — the
`_META_MCP_TOOLS` tuple) compose the legacy tools by an `action` selector param and are ALWAYS
registered regardless of the flag; plus **2 always-on singletons** (`tg_mcp_capabilities`,
`tg_classify_logs` — `_SINGLETON_MCP_TOOLS`). 46 + 10 + 2 = 58. Flipping `TG_MCP_LEGACY_TOOLS`
OFF de-advertises the 46 legacy names but keeps the 10 meta + 2 singletons (the meta tools'
dispatch bodies call the legacy functions in-process either way). Re-derive the shape with
`grep -n "_META_MCP_TOOLS\|_SINGLETON_MCP_TOOLS\|def _legacy_tools_enabled"
src/tensor_grep/cli/mcp_server.py`; the full advertised set below — the legacy individual names
first, the 10 meta-tools and `tg_classify_logs` singleton at the end):
`tg_search`, `tg_find` (whole-repo hybrid NL search, agent-callable form of `tg find`, v1.78.0/#189/#627 —
see `docs/harness_api.md`), `tg_ast_search`, `tg_symbol_defs`, `tg_symbol_source`,
`tg_symbol_refs`, `tg_symbol_callers`, `tg_symbol_impact`, `tg_symbol_blast_radius`,
`tg_symbol_blast_radius_render`, `tg_symbol_blast_radius_plan`, `tg_context_pack`,
`tg_context_render`, `tg_edit_plan`, `tg_agent_capsule`, `tg_ruleset_scan`, `tg_rewrite_plan`,
`tg_rewrite_apply`, `tg_rewrite_diff`, `tg_classify_logs`, `tg_devices`, `tg_index_search`, `tg_repo_map`,
`tg_file_imports`, `tg_file_importers`, `tg_session_file_importers`,
`tg_checkpoint_create` / `_list` / `_undo`, `tg_session_open` / `_list` / `_show` / `_refresh` /
`_edit_plan` / `_context_render` / `_blast_radius*`, `tg_audit_manifest_verify`,
`tg_audit_history`, `tg_audit_diff`, `tg_review_bundle_create` / `_verify`, `tg_navigate`, `tg_impact`,
`tg_query`, `tg_context`, `tg_explore`, `tg_session`, `tg_scan`, `tg_audit`, `tg_checkpoint`, `tg_rewrite`.

**Gap worth knowing:** neither `tg prepare` nor `tg ledger` has an MCP tool counterpart as of v1.96.0 —
both are CLI-only. An agent driving `tg` over MCP cannot reach the one-call edit-readiness CUJ or the
advisory ledger the way a CLI-driven agent can; this is a real surface gap, not an oversight to route
around silently.

`tg_rewrite_apply` refuses free-form `lint_cmd`/`test_cmd` (they shell-execute on the host) unless
the operator opts in with `TG_MCP_ALLOW_VALIDATION_COMMANDS=1`, returning
`code="unsupported_option"` otherwise. The primary native `rg` passthrough now inserts a `--`
end-of-options sentinel before user/LLM-controlled paths (CWE-88 / the MCP-276 CVE class) — shipped
and unit-tested in `#370` (`rust_core/src/rg_passthrough.rs:574-598`, `ripgrep_operand_args`). It is **not** solved
forever: a second native rg-invocation path is the open follow-up (`#49`), so any **new** MCP tool
or argv builder that shells out to `tg`/`rg` must still insert `--` before positionals itself. See
`AGENTS.md` "Security Hardening Patterns" and `tensor-grep-debugging-playbook` /
`tensor-grep-change-control` before adding a new MCP tool that shells out.

## 8. Diagnostics and operational health — doctor, dogfood, upgrade, repair-launcher

```powershell
tg doctor --json                       # full diagnostics, LSP included by default
tg doctor --no-lsp --json              # skip external LSP provider probes
tg doctor C:\repo --config sgconfig.yml --json
```
`doctor` (`grep -n "^def doctor" src/tensor_grep/cli/main.py` -- `:15329` as of 2026-08-14, was `:14763`) takes `path` (default `.`), `--config` (default `sgconfig.yml`),
`--with-lsp/--no-lsp` (default **on**), `--json`. Inspect `path_tg_first_launcher_kind`,
`fresh_shell_path_tg_first_launcher_kind`, `python_subprocess_path_tg_first_launcher_kind`,
`shell_escaping_guidance`, and any `*_is_foreign` field before trusting a Windows timing or
routing claim — see `.claude/skills/tensor-grep/SKILL.md` "Start Here" for the full field list and
`tensor-grep-diagnostics-and-tooling` for interpreting them in depth.

`session_daemon.autostart` (v1.93.0/#705, A12(b)): when `session_daemon.running` is `false`, doctor
additively reports why — `"on-first-use (not yet warmed)"` for a daemon that hasn't been touched yet,
or a `"disabled (...)"` reason string when autostart itself is off — instead of a bare `running: false`
that reads as broken.

```powershell
tg dogfood --output artifacts/dogfood_readiness.json
tg dogfood --json --root C:\repo --timeout-s 170
```
`dogfood` (`grep -n "^def dogfood" src/tensor_grep/cli/main.py` -- `:15059` as of 2026-08-14, was `:14493`) runs the agent-readiness gate and prints a one-page verdict; it "writes
only explicit `--output` and a sibling readiness report" next to it — it does not write anywhere by
default with no `--output` given (docstring on `main.py`'s `dogfood`). Flags:
`--root` (default `.`), `--output PATH`, `--expected-version` (defaults to `pyproject.toml`),
`--json`, `--progress auto|always|never` (stderr only), `--progress-interval-s` (30.0),
`--timeout-s` (170.0, the nested `agent_readiness.py` child budget), `--no-shell-probes`,
`--no-wsl-probe`. A non-zero exit means at least one readiness check failed — read
`failed_checks` in the verdict before treating a release or a change as safe.

```powershell
tg upgrade
```
`upgrade` (`grep -n "^def upgrade" src/tensor_grep/cli/main.py` -- `:15435` as of 2026-08-14, was `:14869`) upgrades the installed `tensor-grep` package to the latest PyPI
release. It tries, in order: `uv tool install --force` first **only** when the running Python is a
`uv tool`-managed venv (`_is_uv_tool_managed_python` -- `grep -n "^def _is_uv_tool_managed_python" src/tensor_grep/cli/main.py`, `:15355` as of 2026-08-14, was `:14789`, detects `.../uv/tools/`
in `sys.executable`), then `uv pip install --upgrade --refresh-package tensor-grep`, then `pip
install --upgrade --no-cache-dir`. This is the source-aware upgrade path shipped to fix a WSL
uv-tool install getting stranded at a stale version — see `tensor-grep-failure-archaeology` for
the incident. `tg upgrade` also verifies the sidecar import/version post-upgrade and schedules a
managed native front-door refresh when the sidecar version moved ahead of the native binary.

```powershell
tg repair-launcher --json
tg repair-launcher --allow-foreign-rename --json     # only for a foreign tg.exe you own
```
`repair-launcher` (`main.py`'s `repair_launcher`, Windows-relevant) removes a verified or self-identifying stale
`tensor-grep` Python `Scripts\tg.exe` launcher that shadows the managed native front door on PATH.
`--allow-foreign-rename` additionally moves aside a **foreign** (non-tensor-grep) `tg.exe` — use it
only when you own that binary.

## 9. Artifact conventions — where state and JSON reports actually land

| Location | What lives there | Created by | Tracked in git? |
| --- | --- | --- | --- |
| `.tensor-grep/sessions/` | `index.json` + per-session repo-map payloads | `tg session open` | No — `/.tensor-grep/` is gitignored (`.gitignore:51`) |
| `.tensor-grep/checkpoints/` | `index.json`, per-checkpoint `metadata.json` + `snapshot/` tree | `tg checkpoint create` | No — same `.tensor-grep/` ignore rule |
| `.tg_semantic_index/` (or `$TG_SEMANTIC_INDEX_DIR`) | Experimental semantic (dense) index shards | opt-in semantic-search paths | Not committed; experimental subsystem |
| `artifacts/` | `--output` JSON from `tg dogfood`, `scripts/agent_readiness.py`, `benchmarks/run_*.py` | explicit `--output PATH` only — nothing is written here by default | No — `artifacts/` in `.gitignore:61` is gitignored |

None of these directories are portable outputs to hand to another agent/process by default —
treat them as local cache/scratch. If you need a durable artifact, pass an explicit `--output` and
copy it somewhere tracked (or attach it to a PR) yourself; `tg dogfood`/benchmark scripts will not
do that for you.

`sgconfig.yml` (used by `tg scan --config`) is a project-level ast-grep config file, not a
tensor-grep cache directory — it lives wherever you point `--config`, typically the repo root.

## 10. Unscoped `tg search` — fail-fast/refuse, not a hang (shipped: #400/v1.40.3, #413/v1.42.0)

`tg search PATTERN` with **no path** argument (or `--glob X -l` without a scoped path) used to walk
the whole tree and could burn the full ripgrep-subprocess timeout before returning. That is now
**shipped, released** fail-fast/refuse behavior, not an open hang — four layered guards catch the
unscoped case before it reaches a slow walk, plus a wall-clock backstop if all four miss:

1. **Vendored-root refusal** (`_should_refuse_unbounded_vendored_root_scan` -- `grep -n "^def _should_refuse_unbounded_vendored_root_scan" src/tensor_grep/cli/main.py`, `:5527` as of 2026-08-14, was `:5094`) — a
   root with a top-level `node_modules`/`vendor`/`external_repos`/`third_party` dir **exits 2
   instantly** (no scan at all) unless `--allow-broad-generated-scan` opts in.
2. **Workspace-root refusal** (`_should_refuse_unbounded_workspace_root_scan` -- `grep -n "^def _should_refuse_unbounded_workspace_root_scan" src/tensor_grep/cli/main.py`, `:5469` as of 2026-08-14, was `:5036`) — a
   root with >=3 sibling project directories (a monorepo/workspace parent) is refused the same way.
3. **Large single-project-root refusal** (`_should_refuse_unbounded_large_root_scan` -- `grep -n "^def _should_refuse_unbounded_large_root_scan" src/tensor_grep/cli/main.py`, `:5701` as of 2026-08-14, was `:5204`,
   `#413`, dogfood v1.42.0) — closes the remaining gap: a large but non-vendored, non-workspace
   single-project root (matches neither guard above) refuses instantly via a **bounded scandir
   probe** — it checks the already-collected candidate-file count against a 1500-file ceiling
   (gated identically on `--allow-broad-generated-scan`/glob-type-depth scope) rather than falling
   through to the slow per-file Python match loop.
4. **Flag-less bootstrap-passthrough refusal** (`bootstrap._search_paths_include_oversized_implicit_root`,
   `bootstrap.py:804`, v1.92.3/#702, A9) — closes a DEFAULT-path gap the three guards above never
   covered: `bootstrap._run_rg_passthrough` (`bootstrap.py:1088`, the plain flag-less search front door
   that runs *before* `main.py`'s Typer app is ever reached) had **no walk ceiling at all** until this
   shipped — natively reproduced, not a WSL artifact. It fires only when `paths_defaulted` (no explicit
   PATH given) and the implicit root is over `IMPLICIT_SEARCH_WALK_FILE_CEILING = 1500` (now defined
   in `io/scan_limits.py:106`, re-exported for backward compat from `io/directory_scanner.py:34`) —
   the **same single constant** guards 1-3 above and the Rust `rg_passthrough.rs` all import, so all
   four doors agree on one ceiling. Exits 2 in ~1.7s (was a silent ~60s timeout before this shipped).
5. **Native-walk wall-clock deadline** (`compute_native_walk_deadline` /
   `native_walk_deadline_exceeded`, `src/tensor_grep/backends/cpu_backend.py:52,36`, checked during the
   walk) — the last-resort backstop: if none of the four refusals above fire, the native per-file
   search walk still self-bounds and **breaks to a flagged partial** (`result_incomplete` + a stderr
   warning) instead of running unbounded.

`TG_RG_TIMEOUT_SECONDS` (default **60.0 seconds**, `subprocess_policy.py:75`, lowered from 600s in
#288) remains the ripgrep-subprocess-level backstop for the plain rg-passthrough path, but the five
guards above mean an unscoped *vendored*, *workspace*, or *large* root — scoped or not, flag-bearing or
flag-less — now fails in well under two seconds; you should rarely see the 60s subprocess timeout
actually fire on a repo shaped like this one anymore.

**Best practice — still scope to a path** (cheaper than even the refusal-probe cost, and the only
way to get real results instead of an instant refusal):

```powershell
tg search "pattern" C:\repo             # ~0.4s
tg search "pattern" --glob "*.py" C:\repo
# Avoid: tg search "pattern" --glob "*.py" -l     (no path -- triggers a refusal or a bounded walk)
```

Same rule for file listing — prefer a scoped root over `tg search --files . --hidden --no-ignore`
across a large workspace; see `.claude/skills/tensor-grep/SKILL.md` for the broad-generated-scan
guardrail (`--allow-broad-generated-scan`). Task `#390`'s warm-daemon deadline gap is **CLOSED**:
`session_store.WARM_DAEMON_DEFAULT_DEADLINE_SECONDS` bounds those requests. Residual deadline
overruns are separate findings, not evidence that the daemon path is unbounded. For remaining
large-repo campaign history, see `tensor-grep-large-repo-scale-campaign`.

## 11. Exit codes — the layered contract (READ THIS before scripting `tg`)

There is **no single 0/1/2 table** any more. Exit codes are layered by command family. The old
"1 = no match, 2 = error" table is materially **misleading for symbol commands** — those now use a
three-

…(truncated)
