ASTro-sight

ASTro-sight

AST CLI for AI agents that parses 16 languages with tree-sitter and reports symbols, references, diff impact, API changes, and dead code as JSON or TOON

Supported Platforms

Linux macOS Windows

CI Release License

English | 日本語

--- astro-sight answers the structural questions an AI coding agent has while it edits code: where a symbol is defined and used, what a diff breaks, which public APIs changed, and which exports nothing calls any more. It matches identifier nodes in the tree-sitter syntax tree, so `refs --name new` does not pick up comments or strings the way `grep new` does. Output is compact JSON by default, with TOON or automatic selection when tokens matter, and the same queries run from the CLI, an NDJSON session, or an MCP server. Bash/zsh parse failures are reported explicitly: recognizable declarations inside error regions use `refs.kind: "unknown"`, and `review` separates unverified removals from confirmed deletions. See [shell parse errors](docs/diff-analysis.md#shell-parse-errors). Rust cfg-condition classification reuses the current ancestor path to avoid repeated parent traversal on deep `use` paths. Reference positions, ordering, and cfg-key exclusions are preserved. See [performance checks](docs/development.md#reference-scan-performance) for the reproducible comparison. ## Features - **References by identifier**: `refs` lists the definitions and references of a symbol across a directory, and `refs --names` looks up many names in one pass - **Diff impact**: `context` maps a unified diff to changed symbols, signature changes, and callers, and `impact` exits 1 while callers outside the diff are left unresolved (made for Stop hooks) - **Rust type aliases**: a transparent public alias introduced in the same file can turn an equivalent function signature change into an informational result when the old and new syntax trees prove the underlying type is unchanged - **Rust parameter patterns**: verified ordinary parameter renames and destructuring changes keep the same call contract; types and receivers remain checked. See [proof boundaries and container limitations](docs/diff-analysis.md#rust-parameter-patterns). - **Structured review**: `review` combines the impact, files that usually change together according to git blame (`cochange`), public API changes, and dead symbols in one run - **File structure**: `symbols`, `calls`, `imports`, and `ast` return a file's outline, call graph, dependencies, and exact syntax nodes, and `sequence` draws the call flow as a Mermaid sequence diagram - **Dead code and rules**: `dead-code` reports exported symbols that nothing references, leaving out the ones test runners and frameworks call at run time, and `lint` checks AST pattern rules written in YAML - **Token-aware output**: compact JSON, TOON, or `--format auto`; results for frequent names stop at 100 references or about 3,000 tokens by default, with a `result_summary` of what was left out - **Agent integration**: skills for Claude Code and Codex, an MCP server, and an NDJSON `session` that batches queries Supported languages (extensions and parser versions are in [docs/languages.md](docs/languages.md)):

Rust C C++ Python JavaScript TypeScript TSX Go PHP Java Kotlin Swift C# Bash Ruby Zig

## Installation ### Homebrew (macOS/Linux) ```bash brew install owayo/astro-sight/astro-sight ``` ### winget (Windows) ```powershell winget install owayo.astro-sight ``` ### Cargo Requires Rust 1.99 or later. ```bash cargo install --git https://github.com/owayo/astro-sight --locked ``` ### From GitHub Releases Download the archive for your platform from [Releases](https://github.com/owayo/astro-sight/releases/latest), extract it, and put `astro-sight` on your `PATH`. Each release also includes `SHA256SUMS` for checking the downloads. | Platform | Archive | |---|---| | Linux (x86_64) | `astro-sight-x86_64-unknown-linux-gnu.tar.gz` | | Linux (x86_64, musl) | `astro-sight-x86_64-unknown-linux-musl.tar.gz` | | Linux (ARM64) | `astro-sight-aarch64-unknown-linux-gnu.tar.gz` | | macOS (Intel) | `astro-sight-x86_64-apple-darwin.tar.gz` | | macOS (Apple Silicon) | `astro-sight-aarch64-apple-darwin.tar.gz` | | Windows (x86_64) | `astro-sight-x86_64-pc-windows-msvc.zip` | On macOS, if you downloaded the archive with a browser, remove the quarantine attribute before running it: `xattr -d com.apple.quarantine astro-sight`. ### From Source Requires [mise](https://mise.jdx.dev/) (the Rust toolchain is pinned in `mise.toml`). ```bash git clone https://github.com/owayo/astro-sight.git cd astro-sight make install ``` `make install` installs to `/usr/local/bin`. Set `INSTALL_PATH` to change it (for example `make install INSTALL_PATH="$HOME/.local/bin"`). Check `astro-sight --version` after installing. If `command -v astro-sight` shows a mise shim, `mise which astro-sight` identifies the executable to update; see [installation checks](docs/development.md#same-checks-as-ci). After `winget install`, open a new terminal: the portable package reaches `PATH` by rewriting it, which terminals that were already open do not pick up. The musl archive is a statically linked build for Linux without glibc (Alpine) or with an old glibc (some Docker images). Building from source also needs a C compiler for the tree-sitter parsers (Xcode Command Line Tools on macOS). After the binary, `make install` writes the Claude Code and Codex skills described in [AI Agent Integration](#ai-agent-integration). ## Usage Results are compact JSON on one line by default (batch modes print one line per file). `--pretty` indents the JSON, and `--format toon` or `--format auto` switches to TOON or to whichever of the two is estimated to take fewer tokens. TOON output follows the [v4.1 specification](https://github.com/toon-format/spec/blob/main/SPEC.md); an empty array is `[]`. ### Recommended Flow for Agents ```bash # 1. Start the review of a whole diff with review astro-sight review --dir . --git # 2. Pair context and impact around an edit astro-sight context --dir . --git astro-sight impact --dir . --git # 3. Read structure with symbols, and look up identifiers with refs astro-sight symbols --path src/main.rs astro-sight refs --name "AppService" --dir src/ # 4. Use ast only to pin down an exact syntax node astro-sight ast --path src/main.rs --line 10 --col 0 # 5. Put structural rules you check repeatedly in lint, and use sequence when call order matters or calls go 3 or more levels deep astro-sight lint --path src/main.rs --rules rules.yaml astro-sight sequence --path src/main.rs --function main # 6. Batch two or more different queries into one session printf '%s\n' \ '{"command":"symbols","path":"src/main.rs"}' \ '{"command":"refs","name":"AppService","dir":"src"}' \ | astro-sight session ``` TypeScript/TSX `export const` annotation-only edits appear in `api_changes.type_annotation_changes` with `reason: type_annotation_only` and `compatibility: unverified`. Type compatibility needs a TypeScript check. `review --hook` keeps their references informational; `--strict-public-type-annotations` requests review through `policy.type_annotation`, independently of `--strict-public-const-values`. Annotation formatting alone is omitted and does not block either policy. Python function parameter annotation changes to a local `Protocol` can appear in `api_changes.modified_closed_in_diff` (`api.mod_closed` in hook output) even when callers are unchanged. The proof follows a local variable annotated as `C` or `C | None`, initialized with `C(...)` or `None`, to a unique class declared locally or imported directly from a repository module. The class must declare every inherited Protocol method with matching parameter names, types, defaults, return types, and async behavior. Existing conforming classes also qualify. This checks declared contracts using built-in types and containers. Aliased or relative imports, keyword or spread arguments, omitted changed parameters, complex local scopes or rebinding, decorated or inherited implementation classes, data members, unknown types, parse errors, and companion `.pyi` files keep the conservative blocking classification. Dynamic or transitive name resolution is outside this proof. Adding a required property to a TypeScript interface or object type alias appears in `api.mod_closed` when the same diff updates every function that returns the type, every `const x: T` / `satisfies T` value, and the callers of every top-level function that takes the type as a parameter. The added property must carry a value whose type fits the new member. Generic or `extends` declarations, required methods, and references in unsupported positions (such as `T[]`) stay blocking even when the reference lines changed. See [TypeScript required property additions](docs/diff-analysis.md#typescript-required-property-additions). ### Find References ```bash astro-sight refs --name extract_symbols --dir . ``` ```json { "symbol": "extract_symbols", "refs": [ { "path": "src/engine/symbols/mod.rs", "ln": 107, "col": 7, "ctx": "pub fn extract_symbols(...)", "kind": "def" }, { "path": "src/commands/api_changes/exported.rs", "ln": 45, "col": 39, "ctx": "let syms = crate::engine::symbols::extract_symbols(root, source, lang_id).ok()?;", "kind": "ref" } ] } ``` `path` is relative to `--dir`, and `ln` and `col` are 0-based. When a name has more than 100 references or the output would exceed about 3,000 tokens, the output stops there and a `result_summary` describes the omitted references; `--max-results` and `--token-budget` change the limits (`unlimited` removes them). Source files in a language astro-sight cannot parse (`.vue`, `.applescript`, ...) are listed in `truncations` instead of being skipped silently, and a scope where no file can be parsed returns `UNSUPPORTED_LANGUAGE` rather than an empty list. See [Files astro-sight Cannot Parse](docs/usage.md#files-astro-sight-cannot-parse). ### Check Unresolved Impacts ```bash astro-sight impact --dir . --git ``` ```text Unresolved impacts found: src/engine/symbols/mod.rs changed [extract_symbols]: → src/service.rs:284 [extract_symbols] → src/commands/api_changes/exported.rs:45 [extract_symbols] ``` When every caller has been updated, `impact` prints nothing and exits 0. Otherwise it prints the list above to stderr and exits 1. Line numbers in this text are 1-based so that an editor can open them directly. `review --hook` reports TS/TSX trailing optional/default parameter removal as `mod_compat` with reason `trailing_optional_params_removed` when unchanged remaining parameters, an explicit unchanged return annotation, and a closed inventory of direct internal calls prove the calls still fit. Their references remain in `impact_info`. Aliases, dynamic or ambiguous dependencies, and incomplete source coverage keep the existing conservative classification. This proof covers calls in the analyzed static repository; it does not guarantee external compatibility or equivalent runtime behavior. For a TS/TSX default export that directly registers a single object method through a local identity function with the same explicit interface parameter and return type, starting to receive existing contract parameters can be `mod_compat` with reason `fixed_callback_contract`. The interface, registration, and all source outside the implementation parameters/body must remain unchanged. Only simple required parameters and a `void` method contract are proved; a direct call through the target's default import is required, and observed calls must supply the contract's arguments. Object escapes, aliases, re-exports, dynamic access, incomplete parsing, and source inventories above 512 files, 8 MiB, or 100,000 nodes retain the conservative classification. The current proof requires a JS/TS-only source inventory with relative imports and no computed access or escaped string literals anywhere in that inventory. Raw `context` evidence remains visible; `impact` and the review hook accept the proven compatible change. This establishes a limited static contract proof, without guaranteeing equivalent behavior or external consumers. The full reference is split by topic: - Every command with its options, batch mode, `session`, and the MCP server: [docs/usage.md](docs/usage.md) - `context`, `impact`, `review`, `dead-code`, and `cochange` in depth: [docs/diff-analysis.md](docs/diff-analysis.md) - Compact keys, TOON, and `--format auto`: [docs/output-format.md](docs/output-format.md) ## Configuration `astro-sight init` writes a configuration file to `~/.config/astro-sight/config.toml` (`--path` writes it elsewhere; an existing file is overwritten without asking). `--config ` reads another file for a single run. ```toml debug = false # write debug logs to files format = "json" # default output format: "json" | "toon" | "auto" skip_generated = true # leave generated files out of directory scans ``` `--format` on the command line takes precedence over `format`. The log directory, the cache under `~/.cache/astro-sight/`, and `--no-cache` are described in [docs/configuration.md](docs/configuration.md). ## AI Agent Integration Register astro-sight as a skill so that Claude Code or Codex reaches for it on questions such as "who calls this function?" or "what does this diff affect?": ```bash astro-sight skill-install claude # ~/.claude/skills/astro-sight/SKILL.md astro-sight skill-install codex # ~/.codex/skills/astro-sight/SKILL.md ``` To use it as an MCP server over stdio, start `astro-sight mcp` from the repository you want to analyze. Files outside that directory are rejected. ```json { "mcpServers": { "astro-sight": { "command": "astro-sight", "args": ["mcp"] } } } ``` With only the skill, an agent can still fall back to grep. Rules for `CLAUDE.md` / `AGENTS.md` that make it prefer astro-sight, and a Stop hook that keeps it working while `impact` reports unresolved callers, are in [docs/integrations.md](docs/integrations.md). ## Development Requires [mise](https://mise.jdx.dev/). Tool versions are pinned in `mise.toml`. ```bash make setup # Install the toolchain (mise) and dependencies make ci # Run the same checks as CI (no changes) ``` | Command | Description | |---|---| | `make setup` | Install the toolchain (mise) and dependencies | | `make build` | Build a debug binary | | `make release` | Build a release binary | | `make run` | Run the debug binary (arguments via ARGS="...") | | `make test` | Run the tests | | `make lint` | Run clippy with warnings as errors | | `make fmt` | Format the code (rewrites files) | | `make fmt-check` | Check the formatting (no changes) | | `make check` | Run fmt-check and lint (no changes) | | `make ci` | Run the same checks as CI (no changes) | | `make install` | Install the release binary to INSTALL_PATH (default /usr/local/bin) | | `make uninstall` | Remove the binary from INSTALL_PATH | | `make clean` | Remove build artifacts | Run `make` to list every target. Releases are published from GitHub Actions (**Actions → Release → Run workflow**). Besides mise, the build needs a C compiler for the tree-sitter parsers, and the tests need `git`. Without mise, add `SYSTEM_TOOLS=1` to use the tools on `PATH`. When a `[patch]` in `.cargo/config.toml` swaps in local tree-sitter crates, `Cargo.lock` changes only on your machine and `--locked` stops the build; run `make ci CARGO_FLAGS=` in that case. Choosing the skills that `make install` writes, heap profiling, the usage statistics tool, and how a release is built are covered in [docs/development.md](docs/development.md). ## License [MIT](LICENSE)