Skip to content

Releases: fredrikolis/annotated-tree

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 05 Aug 06:30

Added

  • --hidden walks dot-files and dot-directories, which were pruned unconditionally before. A repo
    keeps its hooks, workflows and agent configuration behind a leading dot — .githooks/,
    .github/, .claude/ — and every one of them was invisible to the tree AND to --strict-check,
    which reported all files passed for a tree it had never opened. What is then CHECKED under a
    revealed directory is the ordinary rule, a file that maps to a configured language: on this repo
    the flag brings in .cargo-lint-extra.toml on its extension, plus — through the two entries
    below — .githooks/ and .github/workflows/. Also settable as
    [display] hidden in a config file. It is off by default, so no existing invocation changes;
    the three goldens are unchanged with a dot-directory added to sample/, which is the proof.
    Two boundaries: .git is never walked under any flag combination, and the switch is orthogonal
    to .gitignore — a path that is both hidden and ignored needs --no-gitignore too. The
    --githook-guide recipe and all three copies of this repo's own gate — pre-commit, CI, and the
    pre-tag release check — now pass it, for the same reason they already pass --include-tests.
  • BREAKING (gate). An EXTENSIONLESS file resolves its language from its #! line. A git hook,
    a bin/ script, a configure — the files that decide how a repo builds and gates itself — carry
    their language in a shebang and nowhere else, so they were unlistable and unlintable, and
    --include could not fix it: that flag widens the VIEW, and the gate never honours it. Each
    language now declares interpreters; sh bash zsh dash ksh resolve to shell and
    python python3 to python, by the BASENAME of the shebang's first word, or of the next word
    when the first is env. The probe fires only when a path has no extension at all, so a .rs
    opening with #! is still rust and Cargo.lock is never opened. A file that opens with anything
    but a literal #!, such as a LICENSE, resolves to nothing and stays out of scope exactly as
    before. Expect an adopting repo's first run under --hidden to fail on hooks nothing had ever
    checked — a .githooks/pre-commit whose annotation is missing, or a few characters over the 200
    bound, has had nothing to tell it so. A <script>.annotation sidecar written before this goes
    inert, as one beside any file that maps to a comment marker does: move the line into the script
    itself, behind a #, and delete the sidecar.
  • BREAKING (gate). yaml is a recognized language (.yml, .yaml), with # as its comment
    marker. A workflow, a compose file and a Claude Code skill definition are where a repo's build,
    release and agent behaviour actually lives, and every one of them was a file an agent could only
    route by opening. A leading # comment is legal above any YAML document, so the annotation costs
    the file nothing, and it goes on line 1, above any ---: in YAML that marker starts a
    document rather than the metadata block the scanner skips for a Markdown skill file, so without
    the distinction a file opening --- / name: first / --- would certify as annotated off a
    comment further down. Frontmatter handling is unchanged for every other language; a language
    whose own comment marker is --, such as SQL, has the same shape and is untouched here.
    Excluding a file is the ordinary -I/.gitignore, not a carve-out. A
    <name>.yml.annotation sidecar written before this goes inert, as the .toml and shebang
    entries describe: move the line into the YAML itself, behind a #, and delete the sidecar.
  • BREAKING (gate). .toml is a recognized language, with # as its comment marker (#19).
    A .toml file is listed in the tree and must carry a first-line annotation, at the same bar
    as a .py or a .rs. Config was the one place the tool told an agent to route by opening the
    file. A manifest is a .toml like any other and gets no waiver: Cargo.toml and
    pyproject.toml now render as rows of their own, above the dependency edges their directory
    already states. A repo that does not want a given file held to this excludes it with -I
    or .gitignore, as it would any other file — the check gained no carve-out to configure.
    A <name>.toml.annotation sidecar written before this goes inert, as a sidecar beside any
    file that maps to a comment marker does: move the line into the .toml itself, behind a #,
    and delete the sidecar.

Changed

  • BREAKING (library). walk::configured_walk takes a WalkFilter params struct in place of its
    positional gitignore, include_tests and excludes arguments. Four filtering choices, three of
    them bool, is a signature a caller transposes silently, and the struct is what lets one walk's
    policy be handed to another rather than restated.
  • BREAKING (library). Four public structs gain fields, for a consumer that builds one by
    struct literal: config::Language gains interpreters and frontmatter_prefix, and
    config::Display, config::CliOverrides and the crate-root Cli each gain hidden.

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 03 Aug 04:29
0f2e751

Added

  • trailing_content on the --strict-check report (its own list, like orphan_sidecars: content
    past line 1 is a defect of the FILE, not an issue about an Annotation, so CHECK1 is untouched).
    BREAKING for a consumer that builds a StrictReport by struct literal.
  • Per-file .annotation sidecars (#1). A file that maps to no comment marker — a CSV, a
    dataset, a binary — carries its contract in a <name>.annotation file beside it, holding the
    same bare three-field line a folder's .annotation holds. Three consequences:
    a file carrying a sidecar is listed whatever its extension (writing the sidecar is the
    opt-in, so no --include is needed); the sidecar's own row is suppressed, under a criterion
    the report now states (see Changed); and a sidecar is only ever read for a file that cannot hold
    a first-line comment, so foo.rs.annotation beside a foo.rs is an ordinary file, not a
    sidecar, and an annotation's location stays determined by the path it annotates.
  • orphan_sidecars on the --strict-check report: a <name>.annotation whose named file does
    not exist annotates nothing, and is reported as path: message (its own list — a dangling path
    is not an issue about an Annotation, and no [rules] table configures it). It FAILS the check.
    Nothing is deleted or rewritten: --strict-check still makes no write of any kind.
  • --strict-check enforces a sidecar body with the same grammar as a folder charter, reported at
    the sidecar's own path with language: "sidecar".
  • FileNode.sidecar in the JSON map (omitted when false): the row's annotation came from the
    sidecar beside it. BREAKING for a consumer that builds a FileNode by struct literal.
  • A leading YAML frontmatter block is skipped when looking for a file's annotation, exactly as
    a #! shebang already was (#16). A Claude Code skill/agent/command, or any static-site page,
    must keep its frontmatter on line 1; before this, such a file could not carry an annotation at
    all, so shipping skills and enforcing --strict-check were mutually exclusive. Only a CLOSED
    block at the very start is a prefix — a --- further down stays a horizontal rule.
  • annotated-tree bash-annotator puts each file's contract in an agent's own grep, find
    and ls results, through a Claude Code PreToolUse hook that pipes eligible calls through an
    annotator; --install-claude-hook switches it on. It is a verb on the one binary, so it ships
    on every channel: npx, cargo binstall, the curl installer, cargo install. The tool
    itself is never substituted and the wrapped command's exit status is preserved.
  • annotated-tree bash-annotator --install-claude-hook [FILE] and
    --uninstall-claude-hook [FILE]. Cargo has no post-install or pre-uninstall step — the only
    code it runs is build.rs, at build time — so switching the hook on is an explicit command.
    It MERGES its entries into the settings file, keeping every other key: that file
    holds the permissions a user has accepted, and a setup step that overwrote it would cost them
    all of them. Defaults to ~/.claude/settings.json; pass .claude/settings.local.json for a
    single repo. Idempotent, writes atomically, refuses a file that does not parse rather than
    replacing it, and --uninstall-claude-hook removes only the entries it added.
  • bash-annotator says what it does ONCE, through a SessionStart entry --install-claude-hook
    writes beside the PreToolUse one, running annotated-tree bash-annotator --session-announcement
    — a verb you can run by hand to read exactly what the agent is told. Not an additionalContext on
    each rewritten call: PreToolUse fires before the command runs, so that text would repeat per call
    and describe contracts nothing printed. Claude Code adds a SessionStart hook's stdout to the
    agent's context verbatim, so it is printed bare, with no JSON envelope. Each entry names the one
    verb that does its job, so the settings file says what each is for.
  • That announcement introduces the tool itself, not only the trailing annotations it explains: that
    annotated-tree is installed, that running it on a directory yields a map you can route from
    without opening the files, when to reach for one, and that --annotation-guide is the reference
    for writing an annotation. A notice that only prevents confusion leaves the map unused. The text
    now lives in src/bash_annotator/session-announcement.md, so --session-announcement still
    prints exactly what the agent is handed.
  • --annotation-guide prints the annotation-writing guide to stdout and exits — the same full text
    a failing --strict-check appends, reachable without a violation to trigger it. It ADDS a way to
    reach the guide, it does not move it: --help still carries its compact head, and a failing
    --strict-check still appends it in full.
  • annotated_tree::resolve_charter — resolve a directory's charter through the public API.
    Charter was already exported with no way to obtain one.

Changed

  • BREAKING (gate). An .annotation file — a directory charter or a <name>.annotation
    sidecar — must hold ONE bare annotation line and nothing but whitespace after it.
    --strict-check now FAILS a body with prose below line 1, reporting it in a new
    trailing_content list; the map shows such a directory or file WITHOUT a contract rather than
    with one. Before this, the stray text landed inside the IO field newline and all, and one
    render row printed as two — a tree view whose line count was wrong, a JSON string with a \n
    in it, and a strict-check finding spanning three lines with an unpasteable suggestion. The
    rule is "nothing but whitespace after the first line", never "contains a newline": every editor
    writes a trailing one, so a charter that ends with a newline (or with blank lines) is ordinary
    and passes.
  • The trailing_content finding is reported ALONE for an offending file: its parts are not
    diagnosed until it is one line again, because echoing the offending text into found and
    suggestion is what split the report line in the first place.
  • BREAKING. [rules] max_annotation_length / --max-length <N> now bounds the WHOLE
    annotation, not each field. The bound is on the line an agent ingests: three fields each
    under a per-field bound could still add up to a line nobody wants in a map read a hundred
    times a session. A repo that passes today at 200 per-field will fail — this one did, in 51
    files, every one of which shortened without losing anything it said. The guide now states
    the diagnosis: a line that will not fit is an architecture defect, not a compression
    problem. Do not raise the bound; it is the detector.
  • BREAKING (JSON). defect.too_long (an array naming each over-length field) is replaced
    by defect.length (the annotation's own length). defect.max is unchanged.
  • The maximal-span measurement is gone with it. It existed only because a per-field bound was
    evadable — prose quoting | Non-concern: split the line early and every measured part came
    in under the limit. A whole-line count cannot be evaded that way, so the machinery was
    deleted rather than carried forward.
  • The annotation guide gains a FOLDER CHARTERS section: a .annotation states the directory's
    one job one altitude above its files, and never restates them.
  • -L LEVEL caps the walk, not just the render (#15). The traversal stops at the deepest
    level the output can show, so annotated-tree -L 1 ~ no longer walks an entire home directory
    to print one level (measured on one: 2.4 s warm and 253k directory reads, down to 17 ms and
    88). Three user-visible consequences:
    empty directories are listed — a directory earns its row by being VISITED, not by holding
    a listable file somewhere beneath it. Below the cutoff nothing is visited, so "has a listable
    descendant" is a question the deepest rows can no longer answer, and answering it at one depth
    but not another would be the worse rule; a folder whose contents are all unlistable (only a
    notes.txt, or nothing at all) now gets a row at every depth, where it used to be invisible.
    The -L cap cuts the input to the dependency graph, so a shallow render shows a shallower
    graph of the same tree instead of edges drawn from manifests the caller asked not to see. The
    manifest walk runs exactly ONE level below the deepest row, because a package's manifest lives
    INSIDE the package, one level under the row that names it — so every directory the map
    DISPLAYS still states its own <- depends on […] / used by: […] facts, while a package
    below the cutoff is no row, is never read, and contributes no edge (a path/workspace
    dependency on one now renders as (unresolved)). The extra level reads manifests only and
    can never add a row.
    And --strict-check is not capped: a gate is not a rendered view, so it still lints every
    file at every depth, -L or no -L.
  • The text map states the one exclusion criterion it applies to .annotation files, on stderr
    and only when a sidecar row was actually suppressed. The JSON map states it structurally
    instead, as "sidecar": true on the row that took the contract.
  • A malformed .annotation body that is a conforming line wrapped in a comment marker is
    now diagnosed as exactly that — "remove the <!-- and -->" — instead of "the | field
    separators are missing", which was the one explanation that could not be true (#17). The
    printed suggestion is the line from inside the wrapper, so it is usable as printed; it used
    to embed the malformed text and could not be pasted. Same verdict, same parts reported.
  • SPEC.md gains an accessory vocabulary entry, stating that anything annotated-tree offers
    that helps an agent consume Annotations outside a Report performs no run, emits no Report, and
    is therefore governed by none of...
Read more

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 30 Jul 18:14
d5dda55

Added

  • [rules] max_annotation_length and --max-length <N>: fail any annotation field
    (Concern, Non-concern, IO) whose MAXIMAL SPAN runs over N characters — on a conforming
    line that span is exactly the trimmed value (see Fixed, below). N itself passes. 200 by
    default (see Changed); --max-length 0 normalizes to no bound, like --max-per-node 0.
  • annotation_too_long category, carrying defect.too_long (each offending part and its
    length) and defect.max (the bound).
  • annotation::Outcome::TooLong variant. Outcome is a pub enum with no
    #[non_exhaustive], so a downstream exhaustive match must handle it.

Changed

  • BREAKING: the annotation length bound ships ON at 200 characters per field —
    default_config.toml now sets [rules] max_annotation_length = 200, the built-in layer every
    other layer overrides. A repo that passed --strict-check on 0.4.0 with longer annotations now
    FAILS with no config change. A bound nobody enables catches nothing: 0.4.0 in real repos let
    agents write 500–1600 character fields. Raise it with [rules] max_annotation_length = <N> or
    --max-length <N>; --max-length 0 disables it. The failing TEXT report names that escape once.
  • --strict-check is form-only: every field present, non-empty, within the length
    bound. Filler Concerns, inward Non-concerns, <placeholder> slots and an empty IO
    operand (IO: (a) ->) all pass. The annotation guide still advises against filler.
  • An empty field is fatal as malformed_annotation, naming the field in defect.missing. A
    line whose three keys are present but unextractable (Concern: a|Non-concern: b|IO: c, or
    keys out of order) reports all three parts. Both carry a human detail clause.
  • annotation::Outcome::Malformed gains a detail field: any struct-variant pattern on
    Malformed must be updated.
  • annotation::analyze / analyze_charter / analyze_file take the length bound as a
    trailing Option<usize> (None = no bound).
  • config::CliOverrides gains a public max_annotation_length field: a struct-literal
    construction must supply it, or fall back to ..Default::default().
  • Cli gains a public max_length field. Cli derives no Default, so a struct-literal
    construction must supply it.
  • suggestion is absent for annotation_too_long. The TEXT message never carried one for
    this category either, so TEXT-only consumers see no change.
  • The suggestion stub passes the form check as printed. Its <…> slots are still judgments
    an agent has to write out, and a configured max_annotation_length applies to it
    (<concern owned elsewhere> alone is 25 characters).
  • --help output: the embedded annotation guide declares the Non-concern's where-it-lives
    pointer OPTIONAL and carries a BREVITY section.
  • --githook-guide recipe and the commit-message attestation format: every per-principle line
    carries a severity — none, N/A — reason, or MAJOR/MODERATE/MINOR plus the finding —
    in place of a numeric score. MEDIUM: is renamed MODERATE:, no alias. A numeric MINOR:
    count is now required, and never gated. The gate cross-checks the lines against the counts: a
    line carrying MAJOR under a declared MAJOR: 0 fails. BREAKING for a shipped recipe: a repo
    that wired the example hook in has every commit message rejected until it adopts the format.
  • --githook-guide defines the three severity tiers (MAJOR / MODERATE / MINOR).

Removed

  • --symbols, the [display] show_symbols key, the FileNode.symbols JSON field (and its
    --schema lines), the symbols module, and the symbols Cargo feature with its
    tree-sitter, tree-sitter-python, tree-sitter-rust, tree-sitter-go,
    tree-sitter-typescript and streaming-iterator optional dependencies. The tool reports on a
    file as a NODE — its declared contract — never on its body; a parsed declaration list is a
    second, derived map that can disagree with the annotated one.
  • BREAKING: the Symbol and SymbolKind crate-root re-exports are gone, and Display,
    CliOverrides, Cli and FileNode lose their public show_symbols/symbols fields; none is
    #[non_exhaustive], so a struct literal or field read must drop it. A repo config still
    carrying show_symbols now fails to parse (deny_unknown_fields) — delete the key. mcp is
    the only remaining Cargo feature.
  • --tokens, the [display] show_tokens key, the DirNode.tokens / FileNode.tokens JSON
    fields (and their --schema lines), and the tokens module. It was a ~4 bytes/token
    heuristic, and an unreliable estimate in a tool sold on ingest efficiency is worse than none
    — an agent may budget against it.
  • BREAKING: Display, CliOverrides, Cli, DirNode and FileNode lose their public
    show_tokens/tokens fields; none is #[non_exhaustive], so a struct literal or field read
    must drop it. A repo config still carrying show_tokens now fails to parse
    (deny_unknown_fields) — delete the key.
  • The vacuity gate: the annotation_vacuous category, the defect.vacuous JSON key, and
    annotation::Outcome::Vacuous.
  • The annotation_on_orphan advisory, the strict report's top-level warnings array (and its
    Found N warning(s) TEXT block), and exit::code::ANNOTATION_ON_ORPHAN. The opt-in
    [rules] forbid_orphans / orphan_package rule is untouched.

Fixed

  • .githooks/commit-msg read the FIRST match for each count, so a body line at column 0 reading
    MAJOR: 0 blockers remained shadowed the real trailer and a commit with unresolved blockers
    passed the gate. Every count now takes the LAST match.
  • The length bound under-measured a field whose prose quoted the | Non-concern: / | IO:
    separators with their colons, in EITHER direction: the parser splits at the first occurrence, so
    a quote ahead of the real key hid every character after it (a 300-character Concern measured
    150 and passed --max-length 200), and splitting at the last occurrence instead merely moved the
    shortfall onto a quote that FOLLOWS the real key (a 207-character IO measured 100 and passed).
    The three fields partition the line, so choosing an occurrence only redistributes length; the
    bound therefore no longer measures the parsed values at all. It measures each field over its
    MAXIMAL extent — Concern up to the LAST | Non-concern:, Non-concern from the first of
    those to the LAST | IO:, IO from the first of those to the end of the line. On a conforming
    line each separator occurs once, so the spans are exactly the parsed fields and no reported
    length changes; a line that quotes a separator over-measures and fails loudly, and can no longer
    under-measure. Parsing and rendering are byte-for-byte unchanged.
  • The malformed_annotation suggestion seeded its Concern: from the text before the first bare
    |, so a Concern whose own prose held a pipe (a shell pipeline, a |x| closure, SQL ||)
    was truncated mid-sentence in the suggested stub. It now cuts at the | Non-concern: separator
    the parser splits on, so the seed is exactly the Concern the checker read.

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 20 Jul 03:51

Added

  • Map + render library surface — the crate now re-exports the tree model and renderer at
    the crate root, so a downstream consumer can assemble a CodebaseMap from DirNode /
    FileNode by hand (the charter / deps / symbols / warnings fields may be None /
    Vec::new()) and render it via for_format(Format, ascii) + the Renderer trait, driving
    its own tree without the internal build pipeline. Access-only: no behavior or schema change.
    The node field types (Charter, DirDeps, InternalDep, Warning, Symbol, SymbolKind)
    are re-exported too so every field is nameable; the graph/symbols/strict builder machinery
    stays crate-internal.

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 20 Jul 03:20

Added

  • --include <GLOB> — a positive glob selector, the counterpart to -I/--ignore:
    it adds files of any type to the tree even when their extension maps to no known
    language (repeatable, pipe-separated; --include '*' shows every file). An included
    file's annotation is read marker-agnostically (keyed on the invariant Concern:
    opener), so extensionless and unrecognized files still surface their one-line
    annotation. Config-enablable via [display] include = ["*.sh", "Dockerfile"].
    --strict-check is unaffected — it stays recognized-languages-only (an unknown comment
    grammar cannot be validated).
  • Library API — the crate now exposes its low-level primitives so another program can
    reuse the ignore-based walk (walk::configured_walk, walk::collect_code_files) and
    the annotation grammar (annotation::extract, the marker-agnostic annotation::extract_any,
    annotation::analyze) over files of any shape, driving its own rendering. The config,
    walk, and annotation modules are public, plus the build_globset glob-compile helper;
    the tree model, graph, renderers, and strict-check stay crate-internal.

v0.2.1

Choose a tag to compare

@github-actions github-actions released this 14 Jul 04:18

Docs-only release: no change to the binary. Cut to refresh the README shipped
to crates.io and npmjs.

Changed

  • README.md rewritten around adoption — what the tool is, intended usage
    (annotate, enforce via a local git hook, read the map every session), a TL;DR
    for humans, the rationale for agents, and install/wire/enforce/configure
    steps. Roughly half its former length.
  • README_APPENDIX.md (new) — the extended argument (the infinite-context
    objection, related work, what is still unproven) and the full bibliography
    for every inline citation, cross-linked from the README.
  • Annotation guide — the Non-concern owner may now be an external system or
    out of the repo's scope, not only a named sibling; "true of every file" is
    called out as a truism, not a boundary.
  • The repo now carries its own root .annotation charter, a
    docs/communication-style.md review rubric, and a fixed executable bit on
    .githooks/pre-commit (the strict-check gate was being silently skipped).

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 12 Jul 18:10

Added

  • Annotation guide on --help and failing --strict-check — the format, a
    GOOD/FAILS contrast, and how to find the Non-concern, shown inline where you fix an
    annotation. Opt out with --no-guide.
  • Single-file --strict-check — lint one file, not just a directory (pre-commit
    hook, or the file you just wrote).
  • Directory charters — a directory carries its own Concern | Non-concern | IO
    line (a .annotation breadcrumb, else its entry file), promoted onto its tree row.
  • Actionable --strict-check diagnostics — each violation now names the file's
    language, the exact comment marker to use, the real line number (past any
    shebang/blank lines — no more hardcoded :1 that led fixes to clobber a shebang),
    the offending content, and a copy-pasteable conformant example. Missing vs.
    non-conforming annotations get distinct wording, and a wrong-marker line is echoed
    as found: '…'. The machine-parseable path:LINE: prefix is preserved. MCP
    strict_check inherits the richer messages.
  • Machine-readable --strict-check --format json — the strict check now emits a
    structured document ({passed, error_count, files_checked, violations, rule_violations}) when --format json is passed, one record per violation with
    path/line/language/category/marker/hint/example/found. The default
    TEXT report is unchanged. MCP strict_check returns this same structured object
    (byte-for-byte the CLI's --format json) instead of a flat text report.
  • ANNOTATION FORMAT in --help — --help now shows what a conforming
    annotation looks like, with a verbatim example sourced from the built-in
    [convention].example so help and enforcement can never disagree.
  • [convention].example config field — a full, conformant annotation line
    (per-language overridable, like require/hint) that feeds both --help and the
    strict-check diagnostics. A test proves every built-in language's example passes
    the lint it advertises.
  • Per-directory display cap — --max-per-node <N> (default 50) shows at most
    N subdirectories and N files per directory, folding the overflow into a single
    [+N folders and F files, use --full to expand] marker. Keeps signal-dense
    source trees fully visible while collapsing massive test/corpus folders to one
    line — the overview an agent wants without the token noise. Aggregate --tokens
    totals still reflect the full (untruncated) subtree, so a collapsed folder still
    reports its true size. Expand everything with --full (or --max-per-node 0).
    Display-only: the walk still visits every file, so --max-files is unaffected.
    JSON/MCP carry the breakdown as elided_dirs / elided_files (omitted when
    zero — no schema bump).

Changed

  • One invariant annotation format — the three-field Concern | Non-concern | IO
    grammar is now fixed (not configurable); the only per-language knob is the comment
    marker.
  • Stricter vacuity enforcement — a filler Concern (utils/helpers/…) and an
    inward Non-concern (this file's own …) now fail, matching what the guide teaches.

Removed

  • --explain — superseded by the annotation guide, now shown inline on a failing
    --strict-check and in --help.

v0.1.1

Choose a tag to compare

@github-actions github-actions released this 10 Jul 23:37

Fixed

  • npx annotated-tree / npm install failed — the launcher shim
    (bin/annotated-tree.js) opened with its annotation comment instead of a
    #!/usr/bin/env node shebang, so the npm-linked executable could not run. The
    shebang is restored and CI now asserts it on line 1.
  • Strip a leading UTF-8 BOM when reading a file's annotation head, so a
    BOM-prefixed shebang file is no longer mis-read as lacking a first-line shebang.

Added

  • Shell script support — .sh / .bash files are now recognized by the
    annotation engine (shebang skipped, annotation read from the first comment
    below it).

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 10 Jul 22:35

Initial release.

Added

  • Annotated tree view — a directory tree where every source file shows its
    first-line responsibility annotation, extracted by a configurable, per-language
    engine (structured comment tokens plus a regex escape hatch).
  • Cross-ecosystem dependency graph in the tree — pyproject.toml,
    package.json, Cargo.toml, and go.mod are cross-referenced into internal
    deps, external deps, and reverse "used by" edges; unresolved workspace/path deps
    are flagged.
  • --strict-check lint mode — nonzero exit on any code file lacking a
    conforming annotation. Enforces architectural dependency [rules] (deny edges,
    forbid cycles, forbid orphans) declared in .annotated-tree.toml.
  • --format json (versioned, stable schema) and --format md output for
    tooling and agents.
  • --symbols — per-file top-level definition outline via tree-sitter
    (feature-gated: build with --features symbols).
  • --mcp — serve the map, dependency, and strict-check tools over stdio as a
    Model Context Protocol server (feature-gated: build with --features mcp).
  • --changed / --since <ref> — restrict the view to files changed versus a
    git ref plus their reverse-dependency blast radius.
  • --tokens rough per-file/package token estimate; --age modification
    times; --max-files runaway-scope safety valve (aborts with exit 2 before
    any output).
  • Flags: -L/--max-depth, --include-tests, --no-gitignore, --ascii,
    -I/--ignore, --config, --no-limit, --ignore-parsing-errors.
  • Layered configuration — built-in defaults < ~/.config/annotated-tree/config.toml
    < repo ./.annotated-tree.toml < CLI flags. Regex-configurable extraction and
    validation convention per language.
  • Non-fatal stderr warnings for unparseable manifests (silence with
    --ignore-parsing-errors); a corrupt manifest never aborts the run.
  • Distribution — crates.io, cargo-binstall, Homebrew, npm/npx, and a
    checksum-verifying curl | sh installer.
  • Golden-file and integration test suite; CI across Linux, macOS, and Windows.