{"id":49258610,"url":"https://github.com/dim-s/ast-outline","last_synced_at":"2026-04-28T09:01:21.117Z","repository":{"id":353050836,"uuid":"1217772968","full_name":"dim-s/ast-outline","owner":"dim-s","description":"AST-based code outline CLI (C#, Python, TypeScript, Markdown, etc.) for LLM coding agents. Prints class/method signatures with line ranges — no method bodies. Cuts token usage 5–10× vs full-file reads. Works with Claude Code, Cursor, Aider.","archived":false,"fork":false,"pushed_at":"2026-04-25T09:45:10.000Z","size":390,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-26T07:02:38.587Z","etag":null,"topics":["ai-coding-assistant","aider","ast","claude-code","cli","code-navigation","code-outline","csharp","cursor","developer-tools","llm","markdown","python","tree-sitter","typescript"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dim-s.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-22T07:50:30.000Z","updated_at":"2026-04-25T09:45:13.000Z","dependencies_parsed_at":null,"dependency_job_id":"8d36f944-79a3-462e-80a5-d5ed024626db","html_url":"https://github.com/dim-s/ast-outline","commit_stats":null,"previous_names":["dim-s/code-outline","dim-s/ast-outline"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/dim-s/ast-outline","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dim-s%2Fast-outline","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dim-s%2Fast-outline/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dim-s%2Fast-outline/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dim-s%2Fast-outline/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dim-s","download_url":"https://codeload.github.com/dim-s/ast-outline/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dim-s%2Fast-outline/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32327701,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"online","status_checked_at":"2026-04-27T02:00:06.769Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-coding-assistant","aider","ast","claude-code","cli","code-navigation","code-outline","csharp","cursor","developer-tools","llm","markdown","python","tree-sitter","typescript"],"created_at":"2026-04-25T06:00:52.820Z","updated_at":"2026-04-28T09:01:21.105Z","avatar_url":"https://github.com/dim-s.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ast-outline\n\n**English** · [Русский](./README.ru.md) · [简体中文](./README.zh-CN.md)\n\n\u003e **Renamed from `code-outline` in v0.3.0** to join the `ast-*` family convention popularised by [ast-grep](https://github.com/ast-grep/ast-grep). The legacy `code-outline` CLI command still works as a backward-compat alias through 0.4.x.\n\n\u003e Fast, AST-based **structural outline** for source files — classes, methods,\n\u003e signatures with line numbers, but **no method bodies**. Built for LLM coding\n\u003e agents that should read the *shape* of a file before reading the whole thing.\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)\n![Python: 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)\n![Status: beta](https://img.shields.io/badge/status-beta-orange.svg)\n\n---\n\n## Purpose\n\n**`ast-outline` exists to make LLM coding agents faster, cheaper, and smarter\nwhen navigating unfamiliar code.**\n\nModern agentic coding tools (Claude Code, Cursor's agent mode, Aider,\nCopilot Chat, custom CLI agents) explore codebases by reading files directly\n— not via embeddings or vector search. That approach is reliable but has a\ncost: on a 1000-line file, the agent pays for 1000 lines of tokens just to\nanswer *\"what methods exist here?\"*.\n\n`ast-outline` closes that gap. It's a **pre-reading layer** for agents:\n\n1. **Token savings — typically 5–10×.** An outline replaces a full file\n   read when the agent only needs structural understanding.\n2. **Faster exploration.** A whole module's public API fits on one screen.\n3. **Precise navigation.** Every declaration has a line range (`L42-58`).\n   The agent goes straight to the method body it needs.\n4. **AST accuracy, not fuzzy match.** `implements` and `show` understand\n   real syntax — no false positives from comments or strings.\n5. **Zero infrastructure.** No index, no cache, no embeddings, no network.\n   Live, always fresh, invisible to your repo.\n\n### The typical agent workflow\n\n**Before `ast-outline`:**\n\n```\nAgent: Read Player.cs            # 1200 lines of tokens\nAgent: Read Enemy.cs             # 800 lines of tokens\nAgent: Read DamageSystem.cs      # 400 lines of tokens\nAgent: grep \"IDamageable\" src/   # noisy, lots of false matches\n...\n```\n\n**With `ast-outline`:**\n\n```\nAgent: ast-outline digest src/Combat         # ~100 lines, whole module\nAgent: ast-outline implements IDamageable    # precise list, no grep noise\nAgent: ast-outline show Player.cs TakeDamage # just the method body\n```\n\nResult: **same understanding, a fraction of the tokens, a fraction of\nthe round-trips.**\n\n---\n\n## Supported languages\n\n| Language | Extensions |\n| --- | --- |\n| C#         | `.cs` |\n| Python     | `.py`, `.pyi` |\n| TypeScript | `.ts`, `.tsx` |\n| JavaScript | `.js`, `.jsx`, `.mjs`, `.cjs` (parsed by the TypeScript grammar) |\n| Java       | `.java` — classes, interfaces, `@interface`, enums, records, sealed hierarchies, generics, throws, Javadoc |\n| Kotlin     | `.kt`, `.kts` — classes, interfaces, `fun interface`, `object` / `companion object`, `data` / `sealed` / `enum` / `annotation` classes, extension functions, `suspend` / `inline` / `const` / `lateinit`, generics with `where` constraints, `typealias`, KDoc |\n| Scala      | `.scala`, `.sc` — Scala 2 + Scala 3: classes, traits, `object` / `case object`, `case class`, `sealed` hierarchies, Scala 3 `enum` / `given` / `using` / `extension`, indentation-based bodies, higher-kinded types, context bounds, `opaque type`, `type` aliases, Scaladoc |\n| Go         | `.go` — packages, structs (with method-grouping under receiver), interfaces, struct/interface embedding as inheritance, generics (Go 1.18+), `type` aliases + defined types, `iota` enum-blocks, doc-comment chains |\n| Markdown   | `.md`, `.markdown`, `.mdx`, `.mdown` — heading TOC + fenced code blocks |\n\nAdding another language is a single new adapter file. See\n[`src/ast_outline/adapters/`](src/ast_outline/adapters/).\n\n---\n\n## Install\n\n### One-liner (recommended — macOS / Linux / Windows)\n\nRequires [`uv`](https://docs.astral.sh/uv/) (a fast Python package manager):\n\n```bash\nuv tool install git+https://github.com/dim-s/ast-outline.git\n```\n\nThis installs the `ast-outline` CLI globally into `~/.local/bin` (Mac / Linux)\nor `%USERPROFILE%\\.local\\bin` (Windows) — make sure that's on your `PATH`.\n\nDon't have `uv` yet?\n\n```bash\n# macOS / Linux\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n\n# Windows (PowerShell)\npowershell -ExecutionPolicy ByPass -c \"irm https://astral.sh/uv/install.ps1 | iex\"\n```\n\n### Using the install scripts in this repo\n\n```bash\n# macOS / Linux\ncurl -LsSf https://raw.githubusercontent.com/dim-s/ast-outline/main/scripts/install.sh | bash\n\n# Windows (PowerShell)\niwr -useb https://raw.githubusercontent.com/dim-s/ast-outline/main/scripts/install.ps1 | iex\n```\n\n### Alternative: `pipx`\n\n```bash\npipx install git+https://github.com/dim-s/ast-outline.git\n```\n\n### Alternative: `pip` (into an active venv)\n\n```bash\npip install git+https://github.com/dim-s/ast-outline.git\n```\n\n### Update / uninstall\n\n```bash\nuv tool upgrade ast-outline\nuv tool uninstall ast-outline\n```\n\n---\n\n## Quick start\n\n```bash\n# Structural outline of one file\nast-outline path/to/Player.cs\nast-outline path/to/user_service.py\n\n# Outline a whole directory (recurses supported extensions)\nast-outline src/\n\n# Print the source of one specific method\nast-outline show Player.cs TakeDamage\n\n# Several methods at once\nast-outline show Player.cs TakeDamage Heal Die\n\n# Compact public-API map of a whole module\nast-outline digest src/Services\n\n# Every class that inherits/implements a given type\nast-outline implements IDamageable src/\n\n# Built-in guide\nast-outline help\nast-outline help show\n```\n\n---\n\n## Using with LLM coding agents\n\nThis is the main use case. Add the snippet below to your `CLAUDE.md`,\n`AGENTS.md`, subagent file, or any system prompt that steers a coding\nagent. It will then prefer `ast-outline` over reading full files.\n\nThe same snippet ships with the tool — `ast-outline prompt` prints it\nverbatim, so you can append it to a project's agent config without\ncopy-pasting:\n\n```bash\nast-outline prompt \u003e\u003e AGENTS.md\nast-outline prompt \u003e\u003e .claude/CLAUDE.md\nast-outline prompt | pbcopy   # macOS clipboard\n```\n\n### Prompt snippet (copy-paste)\n\n```markdown\n## Code exploration — prefer `ast-outline` over full reads\n\nFor `.cs`, `.py`, `.pyi`, `.ts`, `.tsx`, `.js`, `.jsx`, `.java`, `.kt`, `.kts`,\n`.scala`, `.sc`, `.go`, and `.md` files, read structure with `ast-outline`\nbefore opening full contents.\nPull method bodies only once you know which ones you need.\n\nStop at the step that answers the question:\n\n1. **Unfamiliar directory** — `ast-outline digest \u003cdir\u003e`: one-page map\n   of every file's types and public methods.\n\n2. **One file's shape** — `ast-outline \u003cfile\u003e`: signatures with line\n   ranges, no bodies (5–10× smaller than a full read).\n\n3. **One method, class, or markdown section** — `ast-outline show \u003cfile\u003e\n   \u003cSymbol\u003e`. Suffix matching: `TakeDamage`, or `Player.TakeDamage` when\n   ambiguous. Multiple at once: `ast-outline show Player.cs TakeDamage\n   Heal Die`. For markdown, the symbol is heading text and matching is\n   case-insensitive **substring** — `\"installation\"` finds\n   `\"2.1 Installation (macOS / Linux)\"`. Multiple matches all print, so\n   you can tighten the query if needed.\n\n4. **Who implements/extends a type** — `ast-outline implements \u003cType\u003e\n   \u003cdir\u003e`: AST-accurate (skip `grep`), transitive by default with\n   `[via Parent]` tags on indirect matches. Add `--direct` for level-1 only.\n\nFall back to a full read only when you need context beyond the body\n`show` returned.\n\nIf the outline header contains `# WARNING: N parse errors`, the outline\nfor that file is partial — read the source directly for the affected region.\n\n`ast-outline help` for flags and rare options.\n```\n\n### Why this helps\n\n- **Fresh subagents with shallow context** (like Claude Code's `Explore`\n  agent) can scan a whole module in one call instead of 10–20 `Read`/`grep`\n  rounds.\n- **\"Where is X defined?\"** becomes one `implements` or `show` call.\n- **Line ranges** (`L42-58`) turn the outline into a precise navigator —\n  the agent reads only the lines it needs.\n- **AST-based** `implements` has no false positives from string literals,\n  comments, or unrelated name mentions — unlike `grep`.\n\n### Works with\n\n- Claude Code (+ custom subagents like `Explore`, `codebase-scout`)\n- Cursor agent mode\n- Aider\n- Copilot Chat / Workspace\n- Any custom agent on the Claude / OpenAI / Gemini APIs\n- Humans (the format is readable; `show` is a nice alternative to `grep -A 20`)\n\n---\n\n## Commands\n\n### `outline` — default\n\nPrint the file's classes, methods, properties, fields with line ranges.\n\n```bash\nast-outline path/to/File.cs\nast-outline path/to/module.py --no-private --no-fields\n```\n\nFlags:\n\n- `--no-private` — hide private members (Python: names starting with `_`)\n- `--no-fields` — hide field declarations\n- `--no-docs` — hide `///` XML-doc / docstrings\n- `--no-attrs` — hide `[Attributes]` / `@decorators`\n- `--no-lines` — hide line-number suffixes\n- `--glob PATTERN` — restrict directory mode to a pattern\n\n### `show` — extract source of a symbol\n\n```bash\nast-outline show File.cs TakeDamage\nast-outline show File.cs PlayerController.TakeDamage   # disambiguate overloads\nast-outline show service.py UserService.get\nast-outline show File.cs TakeDamage Heal Die           # several at once\n```\n\nFor code, matching is **suffix-based**: `Foo.Bar` matches any `*.Foo.Bar`. If\nmultiple declarations match, all are printed with a summary.\n\nFor markdown, matching is **case-insensitive substring** per dotted part.\nLLM agents rarely remember the exact decoration of a heading (number prefixes\nlike `1.`, trailing `(Feb 2026)`, `(Confidence: 70%)`), so a fuzzy core works:\n\n```bash\nast-outline show forecast.md \"current analysis\"\n# → matches `## 1. CURRENT ANALYSIS (Feb 2026)`\n\nast-outline show forecast.md \"scenario.transit\"\n# → matches `### SCENARIO A: \"MANAGED TRANSIT\"` under any parent\n#   heading containing \"scenario\"\n```\n\nIf the substring matches several headings, all are printed and the\ndisambiguation summary lands on stderr — tighten the query to narrow.\n\n### `digest` — one-page module map\n\n```bash\nast-outline digest src/\n```\n\nSample output:\n\n```\nsrc/services/\n  user_service.py (140 lines)\n    class UserService : IUserService  L8-138\n      +get  +search  +create  +delete  +update\n  auth_service.py (95 lines)\n    class AuthService  L10-95\n      +login  +logout  +refresh  +verify_token\n```\n\n### `implements` — find subclasses / implementations\n\n```bash\nast-outline implements IDamageable src/\n```\n\nAST-based — no false positives from comments or unrelated mentions.\n**Transitive by default**: if `Puppy extends Dog extends Animal`, then\n`implements Animal` returns all three, with an annotation on indirect\nmatches:\n\n```\n# 3 match(es) for 'Animal' (incl. transitive):\nsrc/Animals.cs:5   class Dog : Animal\nsrc/Cats.cs:3      class Cat : Animal\nsrc/Puppies.cs:12  class Puppy : Dog          [via Dog]\n```\n\nAdd `--direct` / `-d` to restrict to level-1 subclasses only:\n\n```bash\nast-outline implements --direct IDamageable src/\n```\n\nThe search works across any number of files and nested directories —\nno reliance on filename↔classname convention. Matching is by the last\nsegment of the type name (stripping generics and namespace prefixes).\n\n### `prompt` — print the agent prompt snippet\n\n```bash\nast-outline prompt\nast-outline prompt \u003e\u003e AGENTS.md\n```\n\nPrints the canonical copy-paste snippet used to steer LLM coding agents\nto prefer `ast-outline` over full reads. English, universal across\nClaude Opus 4.7 / Sonnet 4.6 / Haiku 4.5. Running it ensures you always\nget the current recommended version.\n\n---\n\n## Output format\n\nThe format is designed to be **LLM-friendly**: Python-style indentation,\nline-number suffixes in `L\u003cstart\u003e-\u003cend\u003e` form, doc-comments preserved.\nThe header summarises scale and flags partial parses.\n\n### C#\n\n```\n# Player.cs (142 lines, 3 types, 12 methods, 5 fields)\nnamespace Game.Player\n    [RequireComponent(typeof(Rigidbody2D))] public class PlayerController : MonoBehaviour, IDamageable  L10-120\n        [SerializeField] private float speed = 5f  L12\n        public int CurrentHealth { get; private set; }  L15\n        /// \u003csummary\u003eApply damage.\u003c/summary\u003e\n        public void TakeDamage(int amount)  L30-48\n        private void Die()  L50-55\n```\n\n### Python\n\n```\n# user_service.py (70 lines, 2 types, 5 methods, 3 fields)\n@dataclass class User  L16-29\n    def display_name(self) -\u003e str  L26-29\n        \"\"\"Human-friendly label.\"\"\"\n\nclass UserService  L31-58\n    def __init__(self, storage: Storage) -\u003e None  L34-35\n    def get(self, user_id: int) -\u003e User | None  L37-42\n        \"\"\"Look up a user by id.\"\"\"\n    def save(self, user: User) -\u003e None  L44-46\n```\n\n### `show` with ancestor context\n\n`ast-outline show \u003cfile\u003e \u003cSymbol\u003e` prints a `# in: ...` breadcrumb\nbetween the header and the body so you know what the extracted code is\nnested inside, without a second `outline` call:\n\n```\n# Player.cs:30-48  Game.Player.PlayerController.TakeDamage  (method)\n# in: namespace Game.Player → public class PlayerController : MonoBehaviour, IDamageable\n/// \u003csummary\u003eApply damage.\u003c/summary\u003e\npublic void TakeDamage(int amount) { ... }\n```\n\nTop-level symbols (no enclosing namespace/type) have no breadcrumb.\n\n### Partial parses\n\nWhen tree-sitter recovers from syntax errors, the outline is kept but a\nsecond header line flags the gap:\n\n```\n# broken.java (16 lines, 1 types, 3 methods)\n# WARNING: 3 parse errors — output may be incomplete\n```\n\nAgents should treat these files as partial and read the source directly\nfor the affected region.\n\nDifferences are language-idiomatic:\n\n- C# `///` XML-doc appears **above** the signature.\n- Python `\"\"\"docstrings\"\"\"` appear **below** the signature with one extra\n  indent (matching Python semantics).\n- C# attributes (`[Attr]`) and Python decorators (`@foo`) are inlined with\n  the declaration.\n- C# property accessors `{ get; private set; }` are preserved.\n\n---\n\n## How it works (briefly)\n\n- Parses source with [tree-sitter](https://tree-sitter.github.io/) —\n  real AST, not regex.\n- Language-specific adapters convert the AST to a uniform\n  `Declaration` intermediate representation.\n- Language-agnostic renderers produce outline / digest / search output.\n- Purely local, no network, no indexing, no cache — just reads and parses\n  the files you ask about.\n\nNo vector database, no embedding, no RAG. This is deliberate — the philosophy\nmatches how agentic coding tools like Claude Code actually work.\n\n---\n\n## Development\n\n```bash\ngit clone https://github.com/dim-s/ast-outline.git\ncd ast-outline\n\n# Create a venv and install in editable mode\nuv venv\nuv pip install -e .\n\n# Run against the included samples\n.venv/bin/ast-outline tests/sample.cs\n.venv/bin/ast-outline tests/sample.py\n.venv/bin/ast-outline digest tests/\n```\n\n### Running the tests\n\nTests are an optional dev dependency — end users don't pull them in. Install\nthem once and run via `pytest`:\n\n```bash\n# Install pytest into the same venv as the editable install\nuv pip install -e \".[dev]\"\n\n# Run the full suite (takes ~0.1s)\n.venv/bin/pytest\n\n# Just one file, verbose\n.venv/bin/pytest tests/unit/test_csharp_adapter.py -v\n\n# Match by test name\n.venv/bin/pytest -k file_scoped_namespace -v\n```\n\nThe suite (400+ tests) covers every adapter (C#, Python, TypeScript/JS,\nJava, Kotlin, Scala, Go, Markdown), the language-agnostic renderers, symbol\nsearch, and the CLI end-to-end. Fixtures live under `tests/fixtures/`;\ntests never reach outside that directory.\nNew behaviour should come with a test; new languages should ship with a\ndedicated fixture directory and a `tests/unit/test_\u003clang\u003e_adapter.py` file.\n\n### Adding a new language\n\nCreate `src/ast_outline/adapters/\u003clang\u003e.py` implementing the\n`LanguageAdapter` protocol (see `adapters/base.py`). Then register it in\n`adapters/__init__.py`. The core renderers and CLI pick it up automatically\n— no further wiring needed.\n\n---\n\n## Roadmap\n\n- [x] TypeScript / JavaScript adapter (`.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`)\n- [x] Java adapter (`.java`) — classes, interfaces, `@interface`, enums, records, sealed hierarchies, generics, throws, Javadoc\n- [x] Kotlin adapter (`.kt`, `.kts`) — classes, interfaces, `fun interface`, `object` / `companion object`, `data` / `sealed` / `enum` / `annotation` classes, extension functions, `suspend` / `inline` / `const` / `lateinit`, generics with `where` constraints, `typealias`, KDoc\n- [x] Scala adapter (`.scala`, `.sc`) — Scala 2 + Scala 3: classes, traits, `object` / `case object`, `case class`, `sealed` hierarchies, Scala 3 `enum` / `given` / `using` / `extension`, indentation-based bodies, higher-kinded types, context bounds, `opaque type`, `type` aliases, Scaladoc\n- [x] Go adapter (`.go`) — packages, structs (with method-grouping under receiver), interfaces, struct/interface embedding as inheritance, generics (Go 1.18+), `type` aliases + defined types, `iota` enum-blocks, doc-comment chains\n- [x] Markdown adapter (`.md`, `.markdown`, `.mdx`, `.mdown`) — heading TOC + code blocks\n- [ ] Rust adapter\n- [ ] `--format json` output mode for programmatic consumers\n- [ ] Optional multiprocessing for very large codebases (\u003e500 files)\n\nContributions welcome.\n\n---\n\n## License\n\n[MIT](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdim-s%2Fast-outline","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdim-s%2Fast-outline","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdim-s%2Fast-outline/lists"}