Project history¶
A timeline of releases and notable events. The full record lives in
git log and
the GitHub release page.
2026¶
May¶
| Date | Event |
|---|---|
| 2026-05-06 | v0.7.7: Setup-prompt — Claude-Code-only Explore-shadow sub-step. Claude Code's built-in Explore subagent runs in an isolated context — it does not inherit CLAUDE.md / AGENTS.md, so the snippet written in Step 2 of setup-prompt does not reach Explore invocations on its own. Setup-prompt now detects when the user has Claude Code (~/.claude/ exists) AND no .claude/agents/Explore.md shadow file exists yet, and asks once whether to create the shadow. If approved, writes a ready-to-go Explore definition that embeds the full fresh canonical from Step 2.1 verbatim (not a short pointer — the shadow is a brand-new file and embedding avoids forcing every Explore invocation to re-run ast-outline prompt), wrapped in the standard markers; offers project-local vs global scope (.claude/agents/Explore.md / ~/.claude/agents/Explore.md). Codex and Gemini subagents are user-defined files only — they fall under the existing diff-aware patch logic, no shadow concept needed. Skipped in headless mode and when a shadow already exists. Closes the gap where ast-outline integration via AGENTS.md silently failed to reach the most-used Claude Code subagent. Step 2 also detects user-written ast-outline content outside markers — previously the "markers absent" branch silently appended a fresh marker block at the end of an existing AGENTS.md / CLAUDE.md / GEMINI.md, leaving two competing references when the user had hand-written content (e.g. from an old ast-outline prompt >> AGENTS.md run that they later edited). Setup-prompt now scans for ast-outline mentions in the target file before any write; if found outside markers, shows the offending lines and asks the user which path to take — (1) wrap the existing content in markers verbatim (preserves their version, future re-runs go diff-aware), (2) replace with the fresh canonical, (3) append anyway with duplication, or (4) skip Step 2 entirely. Default is ask — silent append on top of user content is no longer reachable. Site homepage now also surfaces an Install with AI call-to-action linking to a dedicated section that walks through the setup-prompt flow before the manual install paths. |
| 2026-05-06 | v0.7.6: setup-prompt subcommand — ast-outline setup-prompt prints an install-time checklist meant for one-shot consumption by a coding agent (Claude Code, Codex CLI, Gemini CLI, Cursor). Tell the agent "run ast-outline setup-prompt and follow its instructions" and it walks the user through three idempotent steps: (1) verify the CLI (offer uv tool install / pipx / pip if missing, run on user's behalf with explicit consent — never silently); best-effort PyPI version check, surface the matching upgrade command (uv tool upgrade / pipx upgrade / pip install -U) without auto-upgrading; (2) write the canonical ast-outline prompt snippet to the right persistent-context file — ./AGENTS.md cross-tool default (covers Codex CLI, Claude Code via @AGENTS.md import, Gemini CLI with settings.json config, and Cursor) or the native single-vendor file (./CLAUDE.md / ./GEMINI.md); offers project-local vs global scope (~/.claude/CLAUDE.md / ~/.codex/AGENTS.md / ~/.gemini/GEMINI.md); checks for Codex AGENTS.override.md so writes are not silently shadowed; wraps the snippet in <!-- ast-outline:start --> ... <!-- ast-outline:end --> markers and runs diff-aware on re-run — if existing block content differs from the fresh canonical, the agent shows the diff and offers replace / keep / show-full-diff, never overwriting customizations silently; (3) optionally patch existing exploration-oriented subagent files in .claude/agents/ / .codex/agents/ / .gemini/agents/ with per-agent permission. Cross-vendor universal — outcome-first markdown structure, no persona, no chain-of-thought, no aggressive emphasis; calibrated for Claude Opus 4.7 / Sonnet 4.6 / Haiku 4.5, GPT-5.x, and Gemini 3.x. Cross-OS — agent translates which / $VIRTUAL_ENV / curl to PowerShell / cmd.exe equivalents on Windows. Adapts to whichever human language the surrounding conversation uses for spoken replies and any free-form prose written into a freshly-created AGENTS.md / CLAUDE.md, while keeping the marker block content and subagent files in English. Headless harnesses (codex exec, claude -p, Gemini non-interactive, scheduled CI) restrict execution to read-only checks plus AGENTS.md write at project-local scope. The CLI itself is print(SETUP_PROMPT, end="") — file IO is delegated to the agent's native edit tools, so encoding / permission / merge-conflict edge cases are handled in agent context, not in Python. |
| 2026-05-06 | v0.7.5: SQL adapter (.sql) — DerekStride tree-sitter-sql plus a regex fallback for grammar-unsupported constructs. Tables surface as KIND_TABLE with each column as a KIND_FIELD child carrying the full source-true column line as signature (email TEXT NOT NULL UNIQUE); views, materialized views, composite types, enums, functions, triggers, indexes, sequences, schemas, extensions all parse natively. The regex fallback recovers six constructs the upstream grammar can't parse: CREATE [OR REPLACE] PROCEDURE, CREATE DOMAIN, CREATE TABLE … PARTITION OF parent (modern PG declarative partitioning — child tables vanish without this), CREATE FUNCTION … SECURITY DEFINER and similar exotic modifier orderings, LOAD 'lib', and IMPORT FOREIGN SCHEMA … FROM SERVER … INTO …. Fallback is line-anchored, gated by AST-derived skip ranges (comment / marginalia / literal / block subtrees) so red herrings inside comments / string literals / PL/pgSQL bodies don't surface as spurious declarations, short-circuited by a bytes-level fingerprint check so the typical schema-dump file with no fallback constructs pays nothing, and uses a byte-range guard against double-extracting AST-parsed functions. PL/pgSQL bodies inside AS $$ … $$ parse as opaque dollar-quoted strings — header (name, parameters, return type) extracts cleanly, and parse errors inside function bodies are excluded from error_count so files using := or IF…THEN…END IF don't get mis-reported as broken. PostgreSQL is the primary target; MySQL and SQLite extract tables / columns / indexes / views cleanly with some error_count > 0 noise on dialect-specific syntax (ENGINE=InnoDB, AUTOINCREMENT, inline KEY constraints). MSSQL, T-SQL, Oracle PL/SQL, and BigQuery have partial coverage — bracketed identifiers, GO separators, VARCHAR2, and STRUCT<> / backtick names degrade. Same release adds TypeScript / JavaScript conditional dynamic imports — import('...') calls inside a function / method / control-flow scope contribute to conditional_imports_count, rendered as [+ N conditional includes] next to the imports line, mirroring Python / Ruby / PHP. Top-level dynamic imports are NOT counted (they execute unconditionally on module load). |
| 2026-05-06 | v0.7.4: CSS adapter (.css) and SCSS adapter (.scss). Rules carry simple-selector tokens in match_names — find_symbols(".btn-primary") returns every cascade-relevant rule (top-level, inside @media, themed, descendant in .modal) with the wrapping at-rule visible in the breadcrumb. Pseudo-classes and attribute filters are stripped for matching, so .btn-primary finds the rule whether it carries :hover, [disabled], or sits in .modal .btn-primary. :is(.a, .b) / :where(.a, .b) recurse (additive); :not(...) / :has(...) don't. Native CSS nesting and SCSS & resolve against each parent simple selector — .card { &__header { } } is findable as .card__header; multi-selector parents propagate (a, .link { &:hover { } } is findable as both a and .link). SCSS additionally surfaces @mixin name($args) (callable, gets () in digest), @function, top-level $variable: value, and %placeholder; Sass privacy convention applied — names with leading _ / - marked private. New optional Declaration.match_names field for declarations reachable under several identifiers. [huge] size label ships in the same release — fourth bucket alongside [tiny] / [medium] / [large] with a behavioral twist: files at or above 100 000 estimated tokens collapse to header-only in digest mode, leaving the agg counters intact so the agent can still size the file up. A directory of 50 huge generated/vendored mega-files goes from 1 602 lines of digest output to 52 — measured 31× reduction. outline and show are unaffected. The legend gets one extra clause whenever a huge file appears in the batch: [huge]=body omitted (use \ast-outline outline . Default ignore list also expanded with.min.js/.min.mjs/.min.cjs/.min.css/.min.html/.map`. |
| 2026-05-06 | v0.7.3: outline header now carries the [tiny] / [medium] / [large] size label — the same categorical bucket digest stamps next to each filename. An agent calling outline directly (skipping digest) gets the at-a-glance size signal in plain English alongside the precise ~N tokens count, instead of having to map the raw token number onto a bucket from memory. Header reads # /abs/path.py [medium] (95 lines, ~1,200 tokens, 5 types, 12 methods). Digest output is unchanged. |
| 2026-05-06 | v0.7.2: Ruby language adapter (.rb, .rake, .gemspec, .ru, plus Rakefile / Gemfile resolved by exact basename — first adapter to ship basename-matching) covering modules with qualified-form (module Foo::Bar) + old-style nested-module collapse to A::B::C (HIGH-fix from C++ applied: comments don't count as structural children when deciding whether to collapse), classes with < Super superclass + include / extend / prepend mixins surfaced as : Base, include Mod, extend Mod2 on the digest type header (signature stays Ruby-true class Foo < Bar, mixins live only in the digest's MRO clause), methods, def self.foo singleton methods + entire class << self blocks (both render [static]), full operator coverage as KIND_OPERATOR (+ / - / * / / / % / **, == / != / < / > / <= / >= / <=> / ===, & / | / ^ / ~ / << / >>, [] / []=, unary -@ / +@ / !), attr_accessor / attr_reader / attr_writer (one KIND_FIELD per symbol with [accessor] / [reader] / [writer] marker — multi-symbol calls split so each name stays grep-able), alias / alias_method (with [alias] marker and new → old signature), constants (MAX_NAME_LENGTH = 64) as fields, visibility tracked as a state machine across the class body — bare private / public / protected flips subsequent decls; targeted private :foo, :bar and private_class_method :baz retroactively mark named methods (forward + back references both supported); private() with explicit empty parens parses as a call node and still flips state. require / require_relative / load / autoload collected as imports; lazy loads inside method / block / lambda bodies counted into [+ N conditional includes]. Rails associations recognised by default — has_many, has_one, belongs_to, has_and_belongs_to_many surface as KIND_FIELD with [has_many] / [has_one] / [belongs_to] / [habtm] markers, by direct analogy to how the C++ adapter recognises Unreal Engine UPROPERTY macros. Other Rails DSL (validates, scope, before_action) intentionally NOT recognised — line drawn at relations because they describe model-to-model edges, not behaviour. |
| 2026-05-05 | v0.7.1: agent-prompt snippet (printed by ast-outline prompt, copied verbatim into the EN/RU/ZH READMEs and docs/agents.md) now lists C++ extensions (.cpp/.cc/.cxx/.h/.hpp/.hh) alongside the rest of the supported set. v0.7.0 shipped the C++ adapter but the snippet still claimed the tool only handled the pre-C++ language list, so an LLM agent reading the snippet for the first time on a UE or general C++ project wouldn't learn ast-outline applies. RU and ZH READMEs also got the C++ row in their supported-languages table that was previously only in the English copy. |
| 2026-05-05 | v0.7.0: C++ language adapter (.cpp, .cc, .cxx, .c++, .h, .hpp, .hh, .hxx, .h++, .ipp, .tpp, .inl, .cppm, .ixx) covering classes / structs / unions / enums (classic + enum class), namespaces with single-chain collapse to a::b::c (anonymous + inline preserved), templates with full / partial specialisation / variadic / template template params, operator overloads incl. conversion operators and the C++20 spaceship <=>, ctors / dtors with classification, public: / protected: / private: access blocks with C++-correct defaults (class → private, struct/union → public), base-class clauses with access + virtual markers, out-of-class member definitions (Widget::draw), #include directives as imports, C++20 concepts. Unreal Engine reflection macros recognised by default: UCLASS() / USTRUCT() / UENUM() / UINTERFACE() attach to the next type, UPROPERTY() / UFUNCTION() to the next member; GENERATED_BODY() family is stripped from the source pre-parse (length-preserving — line numbers stay aligned) so UHT's missing-semicolon convention no longer breaks tree-sitter on UE headers. Synthetic MISSING-; parse errors that tree-sitter emits after every UE macro are subtracted from the reported error count, so valid UE files no longer surface as [broken] in the digest. |
| 2026-05-05 | v0.6.8: directory walks now respect .gitignore and .ignore (root + nested, with deepest-wins override semantics including ! negation) and prune a hardcoded list of universally non-source dirs out of the box (.git, node_modules, __pycache__, .venv, .tox, .next, .gradle, .idea, .vscode, .cursor, .zed, .fleet, .vs, *.egg-info/, …). Conflict-prone names (build/, bin/, dist/, target/, vendor/, out/, obj/) are intentionally NOT in the hardcoded list — those go through .gitignore per-repo. When the walker filters anything, output starts with # note: ignored N dirs (basename1, basename2, …) via .gitignore/.ignore + defaults — pass --no-ignore to disable so the agent sees what got skipped and learns the escape hatch. New --no-ignore flag disables the entire filter pipeline (one switch — no per-rule combinatorics like ripgrep's six). New pathspec>=0.12 dependency (MPL-2.0, Apache-2.0 compatible). |
| 2026-05-04 | v0.6.7: ast-outline digest legend is now dynamic — only entries whose token shape actually appears in the rendered body are listed. YAML- and markdown-only digests (whose body contains no callables, kinds, markers, or inheritance) emit no legend at all; code batches keep a legend pruned to the subset of tokens that actually surface. The omission rule also drops the legend in the rare case where only L<a>-<b> would fire (e.g. a code batch of pure marker classes with no members) — a one-entry legend documenting line ranges is more overhead than insight when the suffix shape is already obvious from the body. Drops ~200 bytes of noise from yaml/md digests and saves prompt budget when digest output is piped into LLM context. |
| 2026-05-04 | v0.6.6: ast-outline show gains a depth dial — --signature (and the long form --view signature) returns header only (docs + attributes + the signature line, no method body), with the mutex-grouped --full / --view full aliases preserving the existing body-extraction behavior as the default. Closes the gap between digest (just symbol names) and show (full body) for the post-digest "I have the name, I want the contract, not the implementation" workflow — and removes the temptation for agents to pipe show through head -80 to peek at signatures of large methods. Doc placement matches outline: /// / JSDoc / Rust before the signature; Python docstrings after with +1 indent. Composes with --no-doc for a bare contract line. New public helper core.render_signature_view(match) and SymbolMatch.decl back-reference. Agent-prompt snippet (English canonical + RU/ZH README copies + docs-site agents.md) gets a closing line on step 3 documenting the flag as a modifier across every show form. |
| 2026-05-03 | v0.6.5: ast-outline prompt snippet (and its README copies) reworded to be explicitly cross-vendor — Claude Opus 4.7 / Sonnet 4.6 / Haiku 4.5 and OpenAI GPT-5.x (5.3-codex / 5.4 / 5.5). The broad-to-narrow command list lead-in changed from "Stop at the step that answers the question" to a menu/decision-tree framing, avoiding GPT-5.5's process-prescription antipattern while keeping the same scope guard for Claude's literal models. Module docstring of _prompt.py now codifies the cross-vendor invariants future edits must preserve. |
| 2026-05-03 | v0.6.4: outline and digest no longer produce empty stdout when every file in a batch fails to parse. Per-file # note: parse error in <path>: <err> lines now go to stdout (matching the existing convention for user-facing failures), so an LLM agent reading stdout sees what happened instead of (no output). Detailed # WARN lines still go to stderr for humans; partial-failure batches keep their existing behavior. |
| 2026-05-03 | v0.6.3: conditional_imports_count extended to Python, Rust, Scala — same [+ N conditional includes] marker now flags lazy import inside fn / class bodies (Python), use inside fn / closures (Rust), and method-scoped import (Scala). |
| 2026-05-03 | v0.6.2: PHP language adapter (.php, .phtml, .phps, .php8) targeting modern PHP 8.x and the 7.4 LTS line — namespaces, classes (abstract / final / readonly), interfaces, traits, PHP 8.1 enums, magic ctor / dtor, PHP 8.0 ctor property promotion, multi-variable properties, PHP 8.3 typed class constants, PHP 8.0 #[Attr] attributes, use (incl. grouped) and top-level include / require. Verified on real WordPress core (no parse errors on files up to 291 KB). New common-IR field ParseResult.conditional_imports_count — renderers append [+ N conditional includes] to the imports line when the file has dependencies that aren't statically listed. |
| 2026-05-03 | v0.6.1: PyPI metadata refresh after the GitHub Organization transfer (no code changes). |
| 2026-05-03 | Repository transferred from dim-s/ast-outline to the ast-outline GitHub Organization. Old dim-s/ast-outline URLs continue to redirect. Copyright remains with Dmitrii Zaitsev (dim-s); the org is hosting infrastructure, not a new copyright holder. |
| 2026-05-03 | v0.6.0: relicense from MIT to Apache License 2.0. Documentation separately licensed under CC BY 4.0. The previous MIT text is retained in LICENSE-MIT for compatibility with downstream forks of the 0.5.x tree. |
| 2026-05-02 | First publish to PyPI as ast-outline. v0.4.2 / v0.4.3 / v0.5.0 (code-outline CLI alias dropped) / v0.5.1 (implements command dropped — outline/digest already render : Base) / v0.5.2 (--imports flag) / v0.5.3 (--version flag). |
| 2026-05-01 | v0.4.0: digest method markers ([async] / [unsafe] / [const] / [suspend] / [static] / [abstract] / [override] / [classmethod] / [property]); type modifiers, attrs, and [deprecated] tag. |
April¶
| Date | Event |
|---|---|
| 2026-04-30 | YAML adapter; per-file size labels + token estimate in digest headers; Rust adapter. |
| 2026-04-28 | # note: … LLM-friendly error contract on stdout with rc=0; substring matching for Markdown headings. |
| 2026-04-25 | Go adapter. |
| 2026-04-24 | Scala adapter. Renamed code-outline → ast-outline (v0.3.0). GitHub repo renamed to dim-s/ast-outline. |
| 2026-04-23 | Kotlin adapter; prompt subcommand. |
| 2026-04-22 | Repository created on GitHub as dim-s/code-outline. First public commit, v0.2.0b0. Russian and Chinese READMEs added; TypeScript / JavaScript adapter shipped same day. |
Priority date¶
The 2026-04-22 date matters: it's the first public commit and the
anchor for trademark priority. It's recorded in
NOTICE
and frozen in PyPI release metadata, so it can't be backdated by
downstream forks or copies.