Skip to content
Back to skills

astro-sight

ASecurity

tree-sitter AST でコード構造を解析する CLI。コード識別子 (関数/クラス/変数/型/メソッド名) を探すときは Grep でなく必ずこれ — refs --name/--names (関数の呼び出し元の特定も refs)。 diff/PR レビューは review --git、編集中・編集後の diff 影響分析は context/impact、 構文ノードの特定・parse エラー調査は ast。 dead-code/symbols/calls/imports/sequence/lint/session も提供。 識別子検索・シンボル参照・呼び出し関係・構造把握・構文確認・コードレビューの場面で発動。

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 25, 2026
developmentjavascripttypescriptpythonrustgojavarubyphpswiftkotlin

Works with

  • cursor
  • cli
  • api

Security analysis

A100/100

Scanned October 1, 2026

npx -y skills add owayo/astro-sight --skill skills --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of astro-sight?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for astro-sight
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/owayo-astro-sight/badge)](https://www.skillsdirectory.com/skills/owayo-astro-sight)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: astro-sight
description: >-
  tree-sitter AST でコード構造を解析する CLI。コード識別子 (関数/クラス/変数/型/メソッド名)
  を探すときは Grep でなく必ずこれ — refs --name/--names (関数の呼び出し元の特定も refs)。
  diff/PR レビューは review --git、編集中・編集後の diff 影響分析は context/impact、
  構文ノードの特定・parse エラー調査は ast。
  dead-code/symbols/calls/imports/sequence/lint/session も提供。
  識別子検索・シンボル参照・呼び出し関係・構造把握・構文確認・コードレビューの場面で発動。
allowed-tools: Bash(astro-sight:*)
---

# astro-sight

tree-sitter AST-based code structure CLI. The primary **Grep replacement for code identifiers** (`refs`), plus diff review (`review`), impact analysis (`context` / `impact`), dead-code detection, and structural queries. Output defaults to compact JSON, with TOON and automatic token-size selection available on data commands.

## When to Use (Decision Checklist)

**Immediately before every Grep call, ask: "Does my search contain code identifiers?"** If yes → astro-sight, not Grep. Classify the search pattern itself; do not infer from the file type or the surrounding task.
The same rule applies inside shell commands: wrapping `grep` / `rg` in Bash is not an exception. Searching inside a **single file** is still identifier search — use `refs --name <sym> --dir . --glob <file>` instead of grepping that file. Wanting the surrounding lines (`grep -A 20 <sym>`) is not an exception either — run `refs` for the exact hit lines, then Read those offsets.

| Need | Command |
|---|---|
| Find a function / class / variable / type / constant / method name | `refs --name <sym>` (pipe-separated `FOO`/`Bar` → `refs --names FOO,Bar`; single file → add `--glob <file>`) |
| Review a diff / PR / bug-fix end-to-end | `review --dir . --git` (external patch: `--diff-file <patch>`) |
| Who calls a function — check this before changing its signature | `refs --name <fn>` (`ctx` shows each call site, across files) |
| What the current diff breaks (needs uncommitted changes; a clean tree returns nothing) | `context --dir . --git` |
| Unresolved impacts (after editing) | `impact --dir . --git` |
| Dead (unreferenced) exported symbols | `dead-code --dir .` (diff-scoped: `--git`) — results carry `line` |
| File / directory structure | `symbols --path <file>` / `symbols --dir <dir>` |
| Exact syntax node at a cursor, or parse-error debug | `ast --path <file> --line <n> --col <n>` |
| What a function calls (its callees — not its callers) | `calls --path <file> --function <name>` |
| What a file imports | `imports --path <file>` |
| Understand ordered call flow, especially 3+ interactions | `sequence --path <file> --function <name>` |
| Files that usually change together | `cochange --dir . --paths <file>` |
| Repeated AST/text policy | `lint --path <file> --rules rules.yaml` |
| 2+ mixed queries in one process | `session` (NDJSON) |

**Grep is fine for**: error messages, config values, TODO comments, file-path patterns — anything that is NOT a code identifier.

**Languages parsed** (16): Rust, C, C++, Python, JavaScript, TypeScript, TSX, Go, PHP, Java, Kotlin, Swift, C#, Bash, Ruby, Zig. Identifiers that live *only* in a file type outside this list — SQL column names, HCL/Terraform blocks, `.vue` / `.svelte` templates — cannot be resolved, so Grep stays correct there. Do not guess, though: run `refs` first. A zero-result AST query is itself an analysis result, and `dead-code` now names the unparseable file types it had to skip (`truncations` → `unanalyzable_source`).

## Quick Reference

```bash
astro-sight refs --name <symbol> --dir .           # 1. references (REPLACES Grep for identifiers)
astro-sight refs --names sym1,sym2 --dir .         # 2. batch symbol search (REPLACES Grep "FOO|Bar")
astro-sight review --dir . --git                   # 3. one-shot diff/PR review
astro-sight context --dir . --git                  # 4. what the current diff breaks (needs a diff)
astro-sight impact --dir . --git                   # 5. unresolved impacts (after editing)
astro-sight dead-code --dir . --git                # 6. dead exported symbols
astro-sight symbols --path <file>                  # 7. file structure
astro-sight calls --path <file> --function <name>  # 8. callees of a function (callers → refs)
astro-sight imports --path <file>                  # 9. imports/exports
astro-sight sequence --path <file> --function <name> # 10. ordered call flow (3+ interactions)
astro-sight cochange --dir . --paths <file>        # 11. files that usually change together
astro-sight lint --path <file> --rules rules.yaml  # 12. repeated structural policy
printf '%s\n' \
  '{"command":"refs","name":"S","dir":"."}' \
  '{"command":"symbols","path":"src/main.rs"}' \
  | astro-sight session                                  # 13. batch mixed queries
astro-sight <command> --format auto                # 14. json/toon whichever costs fewer tokens
```

## Commands

### `refs` — Cross-File Symbol Search (Use Instead of Grep)

The primary Grep replacement. Matches only tree-sitter identifier nodes — no false positives from comments, strings, or partial matches.

```bash
astro-sight refs --name <symbol> --dir <directory>
astro-sight refs --name <symbol> --dir <directory> --glob "**/*.rs"   # narrow by glob
astro-sight refs --names sym1,sym2,sym3 --dir <directory>             # multiple symbols (NDJSON, one line each)
astro-sight refs --name <symbol> --dir . --max-results unlimited      # opt out of the default cap
```

Output: `refs` array with `path`, `ln`, `col`, `ctx` (source line), `kind` (`def`/`ref`/`unknown`). `ctx` shows the source line — no need to Read files afterward.

`unknown` marks a recognizable Bash/zsh declaration header inside a parse error region. Its occurrence is retained, but neither a definition nor a use is verified. Check the `symbols` / `ast` diagnostics; zsh uses the Bash grammar as a fallback.

**Output is capped by default** (100 refs / ~3,000 tokens) so one hot identifier cannot burn tens of thousands of tokens. Analysis is never truncated — only the output is. When results are omitted you get a `result_summary`:

```json
"result_summary": {
  "shown": 64, "total": 1846, "omitted": 1782,
  "limited_by": ["max_results", "token_budget"],
  "by_lang": { "rust": 1776, "php": 5 },
  "files": [ { "path": "src/foo.rs", "count": 326 } ],
  "other_files": { "files": 128, "count": 1231 }
}
```

`total` is exact. `by_lang` / `files` describe **only the omitted refs** — use them to narrow with `--glob` (e.g. if most omitted hits are in another language, the name collides across languages). Raise or remove the cap with `--max-results N|unlimited` and `--token-budget N|unlimited`. If no results are omitted, `result_summary` is absent and output is byte-identical to before. A `budget_exceeded: true` field means the output could not be squeezed into `--token-budget` even at zero shown refs (the summary itself has a fixed cost) — raise the budget, pass fewer names, or narrow with `--glob`.

### `calls` — Call Graph Extraction

```bash
astro-sight calls --path <file>                    # all call edges in a file
astro-sight calls --path <file> --function <name>  # only calls made by one function
```

Output (compact): `calls` array grouped by `caller`, each with `range` and `callees` (`name`, `ln`, `col`). `--pretty` for full format.

`--function <name>` keeps only the calls made **from inside** `<name>`, so it answers "what does `<name>` call?". It never lists who calls `<name>` — for callers (including other files) use `refs --name <name>`, whose `ctx` shows each call site.

### `context` — Diff Impact Analysis

Reads a unified diff and finds affected symbols, signature changes, and impacted callers. Answers "what does this change break?". It needs a diff to exist: on a clean working tree `--git` yields `{"changes":[]}`, so before your first edit list the call sites with `refs --name <symbol>` instead, and run `context` once edits are in place.

```bash
astro-sight context --dir . --git                      # auto git diff (recommended)
astro-sight context --dir . --git --staged             # staged changes
astro-sight context --dir . --git --base HEAD~3        # custom base ref
astro-sight context --dir . --diff-file changes.patch  # diff file (also: --diff "<str>", or pipe `git diff`)
```

Output: `changes` per file with `affected_symbols`, `signature_changes`, `impacted_callers` (real call sites — act on these). Low-signal references are split out: `low_confidence_callers` (bare-name / generic-method matches) and `informational_callers` (import / barrel re-export lines — no edit needed while the symbol keeps its name and signature). Vendor/build-artifact exclusion applies — see Notes.

Function-local symbols are excluded as cross-file impact origins in TypeScript/JavaScript, Rust, Python, Go, Java, and Kotlin. This includes Kotlin nested functions; a same-named call in another file is not treated as their caller.

### `impact` — Unresolved Impact Detection (Stop Hook)

Uses `context` internally, then flags impacts whose callers live in files NOT included in the diff. Designed for AI agent stop hooks.

```bash
astro-sight impact --dir . --git            # auto git diff (recommended)
astro-sight impact --dir . --git --staged   # staged changes
astro-sight impact --dir . --git --hook     # appends triage hint on detection
```

Exit codes: `0` = no unresolved impacts (silent), `1` = unresolved impacts found (stderr). Only real call-site references count as unresolved — import-only / re-export references never trigger exit 1. `--hook` appends a triage hint for AI agents.

### `review` — Structured Diff Review (One-Shot)

Integrates `context` (impact) + `cochange` (missing co-change) + API surface diff (added/removed/modified public symbols) + dead symbol detection. Ideal for PR review or pre-merge checks.

```bash
astro-sight review --dir . --git
astro-sight review --dir . --git --base HEAD~3
astro-sight review --dir . --diff-file changes.patch                       # external rename-aware patch
astro-sight review --dir . --git --framework laravel                       # exclude framework conventions from dead_symbols
astro-sight review --dir . --git --exclude-dir generated --exclude-glob 'app/Legacy/**'
astro-sight review --dir . --git --dead-scope touched-symbols              # only dead symbols whose declaration overlaps the diff
```

Output: `impact` (ContextResult), `missing_cochanges`, `cochange_diagnostics` (why `missing_cochanges` is empty — see `cochange`), `api_changes`, `dead_symbols`, `test_only_symbols` (public symbols referenced only from tests — review, don't auto-delete).

Review in dependency order: contracts/types → implementation → callers → tests. Treat a finding as actionable only after AST evidence and a concrete failing scenario verify it; keep unverified concerns informational.

**Blocking vs informational** (what `--hook` blocks on): unresolved `impact` from real call sites, `api_changes.removed` / `.modified`, and `dead_symbols`. Everything else is informational — `api_changes.added` / `.moved` (add/rm pair matched across files) / `.property_to_field` / `.removed_dead` (dead-code cleanup, not breakage) / `.modified_closed_in_diff` (all cross-file refs already updated in the same diff) / `.const_value_changes` (value-only const/static edits; blocking only with `--strict-public-const-values`) / `.compatible_modified` (signature string changed but call sites stay compatible: React HOC wrap, unreferenced object-member removal, trailing optional/default params in TS/Python; `reason` explains why), plus `missing_cochanges` and import-only impact (hook key `impact_info`). Entries in `api_changes.modified` may carry `contract_change` (hook key `contract`) — `{kind, breaks}` naming the language-specific contract change and which side breaks (`producer` = code building the value, `consumer` = code reading it). It classifies Python `TypedDict` contract changes: class-level `total=` flips (`typed_dict_total_false_removed` / `_added`) and per-field requiredness flips (`typed_dict_field_became_required` / `_not_required`, e.g. `y: NotRequired[str]` → `y: str`). Field-level entries appear as pseudo-symbols (`name` = `Class.field`, `kind` = `field`, signatures like `optional str` → `required str`) inside the same `api.mod` list. It also classifies public, module-level, direct Python `Literal` aliases: a value-set shrink is `literal_values_narrowed` / `producer`, and a value-set expansion is `literal_values_widened` / `consumer`; these entries use `kind` = `type`. Literal aliases must resolve to `typing` / `typing_extensions`, and only strings without escapes or prefixes, decimal integers, booleans, and `None` are compared. Mixed replacements, dynamic `__all__`, shadowing, star imports, and non-static values stay unclassified and blocking rather than guessing a direction. Test files are excluded under the same public API surface policy as other symbols. Both field- and type-level entries are output-only pseudo-symbols and never enter `symbols` / `refs` / `dead-code`. Classification never changes severity: classified entries stay blocking and are never demoted to `modified_closed_in_diff` / `compatible_modified`, because external repos and dynamically constructed values cannot be tracked statically. Unclassified changes simply omit the key.

For JS/TS value bindings, replacing a local public variable/constant with a named re-export (including an imported name exported by a clause) remains a blocking `api.mod`. The forwarded definition, value, type, and module evaluation are unverified; the same export name alone does not prove compatibility. Signatures show the old declaration and the new export specifier. This does not resolve downstream consumers through barrel files. Existing function/class/type re-export handling is unchanged.

For Bash/zsh parse errors, `api_changes.uncertain_removals` (hook: `api.rm_unverified`) reports declaration headers that remain in the source but could not be parsed. These entries are informational; confirmed deletions in the same file still block. `truncations` (hook: `trunc`) includes `parse_error_region`: declarations, references, and signature changes in that region are unverified, so exit 0 does not establish compatibility.

- Rust functions and methods can opt out of dead/test-only candidates with the exact standalone line `// astro-sight:allow-dead` immediately before the declaration. Outer attributes and outer doc comments may intervene, with no blank line or ordinary comment. The marker applies to that declaration only; it is not inherited from an impl/module or shared across cfg variants, and does not apply to types or constants. It does not change refs, impact, or API changes.
- `--framework` filters `dead_symbols` only. `--exclude-dir` / `--exclude-glob` affect both impact and `dead_symbols` (same meaning as on `dead-code`). `review` always excludes vendor / tests / build from `dead_symbols`.
- `--dead-scope` defaults to `touched-symbols` with `--hook`, `all` otherwise. With `--hook`, exports newly added in the same diff are excluded from dead warnings (WIP noise); `--include-wip-dead` opts back in.
- Hook JSON includes `blocking_categories`, listing the emitted buckets responsible for exit 1: `impacts`, `api.rm`, `api.mod`, `api.const_value` (strict mode only), and `dead`, in that order. An empty array means the output is informational and does not block. No JSON is emitted when there are no findings or coverage notices. Informational buckets may coexist with blocking ones; their presence does not make them a stopping reason.
- `trunc` entries with `r: "unanalyzable_source"` include `d: {x, n, e?}`: lowercase extension without a leading dot, total file count, and up to three sorted representative paths. Other reasons and legacy entries omit `d`. These coverage facts remain informational.
- `missing_cochanges` and `expected_with` are legacy names for historical associations, not change obligations. Each candidate includes `interpretation` with `relation: historical_cochange`, `actionability: history_only`, and `resolution: not_assessed` (hook: `i`). Neither changed-line blame nor file history proves necessity or completion. Unknown values must not be treated as required or resolved. `confidence` is a frequency ratio; `ranking_score` (hook: `s`) explains the selected ranking and is omitted when unavailable or non-finite. Neither is a probability of an omitted change.
- `--git --base <rev>` uses the same base for the diff and for blame-backed `missing_cochanges`. `--min-confidence` (default 0.3) tunes `missing_cochanges` volume. `--cochange-min-samples` (default 3, stricter than the standalone `cochange` default of 2) requires a pair to have changed together at least that many times: with changed-line blame a source often has only 2 evidence commits, so a pair that co-changed once reaches confidence 1.0 and dominates the report. Pass 2 to see small-sample candidates. Deduplication and the final top-10 selection preserve the standalone command's smoothed `score`; raw confidence remains the filter and displayed evidence. Dependency manifests (`Cargo.toml` / `package.json` / `pyproject.toml`, ...) and lockfiles are never reported as missing co-changes: dependency-adding commits always touch them together with sources, so the historical correlation hits 100% even though a body-only edit that adds no imports has no causal link to them (the standalone `cochange` command still reports manifests as historical fact; lockfiles are excluded there too as generated files). External snapshots are handled directionally: updating only a `__snapshots__/*.snap` file never asks for its source test, while changing the test still reports the missing snapshot update. Suppression requires all of — the snapshot's immediate parent directory is exactly `__snapshots__`, stripping one trailing `.snap` yields the missing candidate's exact path (the Jest / Vitest / Bun convention), the source test exists as a regular file, and the snapshot's first line matches a known runner header exactly. Unknown headers, custom snapshot resolvers, inline snapshots, and other formats keep the usual historical-correlation report; `--include-generated` disables the direction rule. Each entry carries `n` (co-change count) / `d` (denominator) and `e: "history"` when the evidence came from file-history fallback rather than changed-line blame.
- CLI-only (not available in `session`).

### `imports` — Import/Export Extraction

Language-specific tree-sitter queries for all 16 languages. JavaScript, TypeScript, and TSX include static imports, `require()`, and dynamic `import()` whose first argument is a plain string or an interpolation-free template literal. Only the first argument is used as the dependency target. Interpolated template literals are omitted because their dependency cannot be determined statically.

```bash
astro-sight imports --path <file>
astro-sight imports --paths src/main.rs,src/lib.rs   # batch
```

Output: `imports` array with `src`, `ln`, `kind` (Import/Use/Include/Require), `ctx`.

### `symbols` — Symbol Extraction

Lists function/class/struct/enum definitions. Compact by default for token efficiency: `name`, `kind` (short form), `ln` (0-indexed), plus `cx` (cyclomatic complexity, functions/methods only) and `cn` (enclosing container name) when applicable.

`cx` follows McCabe and is **comparable across languages**: base 1 plus one per decision point. switch/match counts each arm but not the construct itself (a 3-way switch is 4); a plain `else` is not a decision point (`if/else` is 2, `if/else if/else` is 3); ternaries count. Whether a catch-all arm (`default:` / `_ =>`) counts is grammar-dependent, so expect ±1 there. Nested functions and closures keep their own `cx` and do not inflate the enclosing function's.

`cn` is the enclosing container; for Go methods it is the receiver type (`func (b *Box) Area()` → `cn: "Box"`).

Besides functions/classes/types, it also extracts JS/TS destructured bindings (`export const { auth, signOut } = NextAuth()` → one symbol per bound name, `ln` = that name's line; property keys are not bindings), `var`, generator functions, `abstract class`, Java/C# `record`, Go type aliases, and Rust trait methods (including bodiless required ones, which inherit the trait's visibility). TS interface / abstract methods and Go interface methods are not extracted yet.

```bash
astro-sight symbols --path <file>            # single file
astro-sight symbols --path <file> --doc      # include docstrings
astro-sight symbols --path <file> --full     # full legacy output (hash, range, doc)
astro-sight symbols --dir <directory>        # directory scan (NDJSON)
astro-sight symbols --dir <directory> --glob "**/*.rs"
```

### `sequence` — Mermaid Sequence Diagram

Use this after `calls` when execution order matters or the flow spans three or more caller/callee interactions; the diagram makes branches and hand-offs easier to verify.

```bash
astro-sight sequence --path <file>
astro-sight sequence --path <file> --function main
```

Output: `diagram` (Mermaid text), `participants` (ordered list).

### `cochange` — Co-change Analysis

Blame-based: starts from source files (auto-derived from `git diff` or explicit), runs `git blame` on changed lines to get the latest-modifying commits, then aggregates co-occurring files from each commit's diff-tree. When changed-line blame cannot produce enough evidence, the source falls back to its own file history.

```bash
astro-sight cochange --dir . --git                                 # uncommitted changes (same default as review)
astro-sight cochange --dir . --git --base HEAD~5                   # last 5 commits plus uncommitted
astro-sight cochange --dir . --paths src/service.rs               # explicit sources (history fallback)
astro-sight cochange --dir . --git --base HEAD~10 --rename --copy  # track renames/copies
astro-sight cochange --dir . --git --base HEAD~5 --no-smoothing    # rank by raw confidence
```

Output: `entries` with `file_a`, `file_b`, `co_changes`, `confidence`, `denominator` (|C|), `score` (smoothed, ranking-only), and `evidence: "history"` when the pair came from the history fallback (absent = changed-line blame); `commits_analyzed` reports |C|. Requires `--git` or `--paths` / `--paths-file`. `--base` defaults to `HEAD` and is evaluated as `git diff <base>` — same as `context` / `impact` / `review` / `dead-code`, so plain `--git` means "my uncommitted changes" and `--base HEAD~5` means "last 5 commits plus uncommitted". `--min-confidence` / `--min-score` must be finite in `0.0..=1.0`; smoothing priors finite non-negative. Default `--git` source collection skips vendor / node_modules / dist / lock / minified assets; explicit `--paths` are kept as-is (a `--git` diff that is entirely excluded returns an empty result, not an error). Source paths must stay under `--dir` — `..`, absolute, and drive-qualified paths are rejected with `PATH_OUT_OF_BOUNDS`.

**Thresholds vs ranking**: `--min-confidence` (default 0.3) gates on the raw `co_changes / denominator` ratio. The smoothed `score` is used only for ordering — gating on it would make small-evidence sources structurally unreportable, since its ceiling is `(denom + alpha) / (denom + alpha + beta)` (with the default beta=8, a source with 2 evidence commits caps at 0.27). Use `--min-score` if you explicitly want a shrinkage gate on top.

**History fallback**: a source whose changed-line blame yields fewer than `--min-denominator` commits (no diff against base, pure-addition hunks only, or a single blame commit) falls back to `git log <base> -n 20 -- <file>`. This is what makes `--paths <file>` on an unmodified file work at all, and it covers the very common "add a new field/method" shape. Tune with `--history-limit` (`0` disables it; note the window is also the confidence denominator for fallback sources, so widening it dilutes coupling). Files newly added against `--base` have no prior history and cannot be sources — `diagnostics.new_files_without_history` records them.

**Diagnostics**: `diagnostics` explains an empty or short result — how many sources were requested, how many got blame vs history evidence, how many had none, and how many candidate pairs each filter dropped (`filtered_min_samples` / `filtered_min_confidence` / `filtered_min_score` / `skipped_min_denominator`), plus a sorted `reasons` enum. Check it before concluding "no co-change". A nonzero `commit_scan_failures` (reason `CommitScanFailed`, omitted from JSON when zero) means some evidence commits could not be read with `git diff-tree` — those count toward the confidence denominator but can never contribute a co-change, so the result is *partially unanalyzed* rather than "these files don't change together".

Noise-suppression defaults (each has a flag to loosen): top 10 candidates per source file (`--per-source-limit`), pairs need ≥2 shared commits (`--min-samples`), merge commits excluded (`--include-merges` restores), same-author commits within 7 days collapse into one unit (`--author-unit-window-days`), commits touching >100 files skipped (`--max-files-per-commit`, and those commits are excluded from the confidence denominator too). If expected pairs are missing, read `diagnostics` first, then loosen these.

### `ast` — AST Fragment Extraction

```bash
astro-sight ast --path <file> --line <n> --col <n>   # node at position/range
astro-sight ast --path <file>                        # full file, top-level nodes
```

### `lint` — AST Pattern Matching

Lint with custom YAML rules (tree-sitter query or text pattern).

```bash
astro-sight lint --path <file> --rules rules.yaml
```

### `dead-code` — Dead Code Detection

Exported symbols with zero non-definition references. Diff flags limit the scan to diff-related files; without a diff, scans the whole project. Package-manager trees, test dirs, and build artifacts are excluded by default (`--include-vendor` / `--include-tests` / `--include-build` to opt back in).

When the directory contains source files astro-sight has no parser for (`.vue`, `.svelte`, `.astro`, `.erb`, `.razor`, `.scala`, ...), references inside them cannot be counted, so a live symbol used only from such a file would be reported as dead. Those files are declared in `truncations` with `reason: "unanalyzable_source"` (folded to one entry per extension). The declaration covers the whole directory the references were counted in, so `--glob` / `--git` do not narrow it. **Read it before acting on a dead symbol** — the field is absent when every source file was analyzed.

Files detected as generated (a generated-file header comment, `.gitattributes` `linguist-generated`, ...) are excluded from the dead **candidates** only; references inside them are always counted, because generated code (e.g. a gRPC `*_grpc.pb.go` handler) calls hand-written code at runtime. The excluded files are listed in `generated_candidates_skipped` (same `{generated, paths, truncated}` shape as `refs`' `skipped`); the global `--include-generated` makes their symbols candidates too.

```bash
astro-sight dead-code --dir .                       # auto-detects each monorepo workspace with a `next` dependency
astro-sight dead-code --dir . --glob "**/*.rs"
astro-sight dead-code --dir . --git                # diff-related files only
astro-sight dead-code --dir . --git --staged
astro-sight dead-code --dir . --framework laravel  # Laravel conventions (migrations, Controllers, Middleware, ...)
astro-sight dead-code --dir . --exclude-dir generated --exclude-glob 'app/Legacy/**'
astro-sight dead-code --dir . --git --dead-scope touched-symbols   # only dead symbols declared inside diff hunks
```

Output: `dir`, `scanned_files`, `dead_symbols` (`name`, `kind`, `file`). Duplicate-named symbols across files are conservatively skipped. Runtime conventions (PHPUnit `*Test`/`*TestCase` + `testXxx`/`setUp`; Python unittest/pytest; Python `urllib.request.BaseHandler` protocol methods; watchdog `FileSystemEventHandler` callbacks; Angular lifecycle hooks like `ngOnInit`, `@Pipe` `transform`, and `ngOnDestroy` on `@Injectable` / `@Pipe` classes; program entrypoints — C/C++ global `main`, Kotlin top-level `fun main` and `@JvmStatic fun main` in an `object` / `companion object`, Java non-private `void main` with no parameter or one `String[]`, C# `static Main` — plus the Java / C# / Kotlin types declaring them) are auto-excluded. A bin-only Rust crate's `pub fn` is excluded from `review`'s `api_changes` (unreachable from outside the crate).

### `session` — NDJSON Batch Mode

Multiple queries in one process (avoids repeated startup):

```bash
printf '%s\n' \
  '{"command":"refs","name":"MyType","dir":"src/"}' \
  '{"command":"calls","path":"src/main.rs","function":"main"}' \
  | astro-sight session
```

Supports `ast`, `symbols`, `doctor`, `calls`, `refs`, `context`, `imports`, `lint`, `sequence`, `cochange` (note: `review` is CLI-only).

## Workflow Examples

Single-command uses are in Quick Reference above; these are multi-command flows.

### "How does this module work?"
```bash
astro-sight symbols --dir src/engine/               # all files in directory
astro-sight calls --path src/main.rs --function main
astro-sight sequence --path src/main.rs --function main
```

### "Is it safe to rename this function?"
```bash
astro-sight refs --name old_name --dir .              # every definition, call site, and import
# ...rename...
astro-sight impact --dir . --git                      # any caller left outside the diff?
```

### "What changed together with this file recently?"
```bash
astro-sight cochange --dir . --paths src/service.rs
```

### "Several different queries in one request"
```bash
printf '%s\n' \
  '{"command":"symbols","path":"src/main.rs"}' \
  '{"command":"calls","path":"src/main.rs","function":"main"}' \
  '{"command":"context","dir":".","diff":"..."}' \
  | astro-sight session
```

## Notes

- **16 tree-sitter languages**: Rust, C, C++, Python, JavaScript, TypeScript, TSX, Go, PHP, Java, Kotlin, Swift, C#, Bash, Ruby, Zig. Ruby methods may use Unicode identifiers, including simple case-fold characters such as `ſ` and `K`; ambiguous forms such as spaced index assignment, bare lambda parameters followed by a block, empty regex literals, blocks after paren-less calls, and empty-identifier heredocs are parsed without error nodes.
- Compact JSON by default (short keys: `ln`, `col`, `ctx`, `refs`, `src`, `def`/`ref`, `fn`...). Use `--pretty` (global) for human-readable output.
- **`--format json|toon|auto`** は出力形式を切り替える。既定は JSON、CLI 指定は `~/.config/astro-sight/config.toml` の `format` より優先。TOON は [v4.1 仕様](https://github.com/toon-format/spec/blob/main/SPEC.md) に従い、配列・表をインデント付きで表す。空配列は `[]`、省略された DTO 列は必要に応じて null または既定値で補完する。`--pretty` は JSON 専用。TOON はバッチと `auto` を含め末尾改行なし、JSON / NDJSON は改行終端。
- **`--format auto`** encodes both and emits whichever is estimated to use fewer tokens (character count plus a per-line penalty, since BPE spends roughly one token per newline+indent; ties go to JSON), so it is never worse than either candidate. The choice is deterministic for a given input. For batch modes the winner is decided from the first 32 records (a fixed sample, so the choice does not change with the worker count / `ASTRO_SIGHT_BATCH_WORKERS`) and applied to the rest, since results are streamed rather than fully buffered.
- **Always JSON regardless of `--format`**: `session` (line-oriented NDJSON protocol), `review --hook` / `impact --hook` (Stop hook contract), and the `{"error":{...}}` envelope. Passing `--format toon` explicitly to those is an `INVALID_REQUEST`; a config-file default silently falls back to JSON so setting `format = "toon"` never breaks hooks. `--format auto` is accepted there and simply yields JSON.
- With `--format toon`, batch modes (`--paths` / `--paths-file` / `--dir`) emit **one root-array document** (`[N]:` followed by `- ` items, or `[]` when empty) instead of NDJSON. Optional fields that compact JSON omits (e.g. `cx`) appear as explicit `null` cells so uniform tables stay possible.
- `refs` respects `.gitignore`; results include `ctx` (source line) so no follow-up Read is needed. Use `refs --names` for symbol-only batches, `session` for mixed commands.
- A zero-result identifier query is still an AST analysis result; do not repeat the same search with Grep/rg.
- **Vendor/build exclusion** (`context` / `impact` / `review`): cross-file ref search skips package-manager trees (`vendor/`, `node_modules/`, `.venv/`, `Pods/`, `Carthage/`...) and build artifacts (`target/`, `build/`, `dist/`, `.build/`, `DerivedData/`, `.next/`, `bin/` (except Cargo's `src/bin/`), `obj/`...) so generic method names (`new`, `save`, `find`) don't flood `impacted_callers`. `ASTRO_SIGHT_INCLUDE_VENDOR_FOR_IMPACT=1` opts back in; `.gitignore` / hidden exclusions are independent and always on. For non-default vendored trees (`pjproject-2.15/`, `third_party/`...) pass `--exclude-dir <NAME>` / `--exclude-glob <PATTERN>` (workspace-relative, negative-override); invalid globs fail up-front with `INVALID_REQUEST`.
- **Input validation**: empty `--name` / `--names` / `--paths` / `--paths-file` rejected with `INVALID_REQUEST`; `--paths-file` capped at 100MB; `cochange` rejects out-of-range `--min-confidence` / negative smoothing priors; `--base` rejects values starting with `-` (blocks git option injection).
- Batch `--paths` output uses a dedicated rayon pool, defaulting to `min(available CPUs, 4)` workers to bound thread-local parser memory. Set a positive `ASTRO_SIGHT_BATCH_WORKERS` value to tune concurrency up to the available CPU count. Output preserves input order while retaining only `8 × workers` pending results; a closed stdout stops processing at the current window.
- With `ASTRO_SIGHT_WORKSPACE`, session-relative `path` / `dir` resolve from the workspace root (invalid values fail closed). stdout broken pipes are handled gracefully (`symbols --dir src | head` won't panic).
- **Large repos (10k+ files)**: `review --dir .` is the heaviest command (context + cochange + API diff + dead-code in one process) and can exhaust memory. Narrow `--dir` to a subtree, bound diff commands with `--base HEAD~N`, restrict with `--glob`, or split `review` into per-command runs (`impact` → `dead-code` → `cochange`). `symbols --path` is memory-light.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…