Metadata-Version: 2.4
Name: hologram-map
Version: 0.17.0
Summary: Compress a codebase into a compact, deterministic markdown map embedded in your coding agent's context files
Author: Sebastian Fabisz
License-Expression: MIT
Project-URL: Homepage, https://github.com/lazymaniac/hologram
Project-URL: Repository, https://github.com/lazymaniac/hologram
Project-URL: Changelog, https://github.com/lazymaniac/hologram/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/lazymaniac/hologram/issues
Keywords: llm,agent,context,codebase-map,tree-sitter,claude,digest
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: grammars
Requires-Dist: tree-sitter; extra == "grammars"
Requires-Dist: tree-sitter-bash; extra == "grammars"
Requires-Dist: tree-sitter-c; extra == "grammars"
Requires-Dist: tree-sitter-c-sharp; extra == "grammars"
Requires-Dist: tree-sitter-cpp; extra == "grammars"
Requires-Dist: tree-sitter-css; extra == "grammars"
Requires-Dist: tree-sitter-go; extra == "grammars"
Requires-Dist: tree-sitter-html; extra == "grammars"
Requires-Dist: tree-sitter-java; extra == "grammars"
Requires-Dist: tree-sitter-kotlin; extra == "grammars"
Requires-Dist: tree-sitter-lua; extra == "grammars"
Requires-Dist: tree-sitter-php; extra == "grammars"
Requires-Dist: tree-sitter-ruby; extra == "grammars"
Requires-Dist: tree-sitter-rust; extra == "grammars"
Requires-Dist: tree-sitter-scala; extra == "grammars"
Requires-Dist: tree-sitter-swift; extra == "grammars"
Requires-Dist: tree-sitter-typescript; extra == "grammars"
Dynamic: license-file

# hologram

Hologram gives coding agents a compact map of a repository before they start
exploring it. The map keeps exact source paths, public callables, named fields,
relationships, project-internal calls, constants, useful private names, and
pointers to tests, tools, and benchmarks in the context files agents already
read.

There is no LLM, query step, or separate index service. Generation runs locally
and is deterministic for the same sources, Hologram build, settings, runtime,
and parser versions.

## Quick start

Hologram requires Python 3.11 or newer. For a polyglot repository, install it
with all tree-sitter grammars:

```bash
python3 -m pip install "hologram-map[grammars]"
hologram init --root .
```

`init` builds the map, embeds it in the repository's agent context files, and
installs hooks that refresh it after commits, merges, and checkouts. Content
outside Hologram's managed block is left untouched.

Python-only repositories can use the dependency-free base package:

```bash
python3 -m pip install hologram-map
```

You can also download the single-file `hologram.pyz` from the
[latest release](https://github.com/lazymaniac/hologram/releases):

```bash
python3 hologram.pyz init --root .
```

If a parser is missing, an interactive run can create a nearby `.venv` and
install only the grammars it needs. Non-interactive runs print the equivalent
install command instead.

## What the map looks like

An abridged Java map looks like this:

```text
# hologram ·.java
· C/R/I{fields} · f(args):Ret > calls · ×0=unused · !E=throws · p{a,b}s=pas,pbs · ←A|B=implementors
src
 App(C) ×0
  main(args) ×0 > PricingEngine,evaluate,{Order,Item}Id.of
 engine
  PricePort(I) ←PricingEngine
   quoteFor(order):Quote ×0
  PricingEngine(C{basePrices})
   evaluate(order,items):Quote !UnknownItem > UnknownItemException,Quote
  Quote(R{order,totalCents})
 ids
  {ItemId,OrderId,UserId}(R{value})
? tests
 src/test
  PricingEngineTest
· 186 LOC · input 1,051 · output 233 tokens · state 0123456789ab
```

The second line is generated with the map and explains its applicable core
notation. Section-specific layout such as `? tests` is described below. The
essentials are:

- The tree mirrors the repository and names exact source files. Similar files
  may be grouped losslessly with braces.
- A title line such as `# hologram ·.py` declares the extension the map would
  otherwise repeat on every leaf: a bare leaf carries it, and a leaf that states
  an extension has exactly the one it states. Grouped landmarks
  (`{ItemId,OrderId}(R{value})`) take the declaration too. A node with other
  nodes below it is a directory; otherwise it names a file. That rule reads a
  directory whose files are all grouped landmarks as a file — about one node in
  fifty on the corpora it was measured against — but never silently: the
  reconstructed path simply does not exist, and the directory does. The
  declaration is made only where it costs less than the repetition, so small or
  mixed corpora carry none.

  ```text
  # hologram ·.py
  · C/R/I{fields} · f(args):Ret > calls · ×0=unused · !E=throws
  app
   main() ×0 > price_order,OrderId,ItemId
   price_order(order,items):int
  models
   ItemId,OrderId,UserId(R{value})
  ```

  Here `app` is `app.py` and `models` is `models.py`; both are files, because
  neither has a node below it and neither names a file of its own.
- `f(args):Ret > calls` shows a callable, its return type, and retained internal
  calls. `←` shows implementors. A call target is named relative to the caller:
  it carries only what the caller's own path, file and owner do not already
  say, so `total` is in the same class, `order.py:Order.total` is a sibling
  file, and a full path means another part of the tree.
- `×0` means no static project reference was found; `!E` means a callable
  throws. These are navigation hints, not correctness claims. Code a framework
  constructs or calls — routes, listeners, DI components and `@Bean` factories,
  ORM entities, lifecycle callbacks — is exempt, because static fan-in cannot
  see the container.
- Deselecting `params` keeps each signature's arity and drops only the argument
  names: `place(order,items)` becomes `place(_,_)`, which is the same
  placeholder the map already uses for an argument no extractor could name.
- `? tests` uses the same path-compressed tree as the source section and
  retains a compact, reconstructable landmark for every detected test file, so
  an agent can see where coverage lives before adding more. It names files, not
  cases: case names were the most expensive thing in the map — a third of it on
  a large corpus — for a fact that is read in the file the landmark points at.
  `*` marks reusable test support: helper classes, declared fixtures, and helper
  functions another test file uses, because those are what another test consumes
  without opening anything. The index names no call targets either. Separate
  tool/benchmark landmarks stay compact.
- The footer carries freshness and saved settings, plus what the map cost:
  `input` is the estimated token count of the scanned sources, `output` the
  estimated count of the map itself — the compression the block bought, stated
  inside the block. Both use the `ceil(characters / 4)` estimate the budget
  uses.

## Common commands

| Command | Purpose |
|---|---|
| `hologram build --root .` | Rebuild the embedded map |
| `hologram build --root . --if-stale` | Skip extraction when the map is fresh |
| `hologram check --root .` | Exit 0 when every target is fresh, otherwise 1 |
| `hologram print --root .` | Print without modifying context or source files |
| `hologram diff HEAD~3 --root .` | Show the semantic map diff from a revision |
| `hologram review --root .` | Review the working tree against `HEAD` |
| `hologram review --root . --json` | Emit structured findings with stable IDs |
| `hologram stats --root . --budget 8000` | Explain a token-budget decision |
| `hologram uninstall --root .` | Remove managed hooks and map blocks |

`build` and `init` remember their settings in the map itself:

- `--lang java,python` limits extraction; clear it with `--lang all`.
- `--target AGENTS.md` selects context files; clear it with `--target all`.
- `--budget 8000` sets an estimated digest-token target; clear it with
  `--budget 0`.
- `--features calls,tests` selects which fact classes the map carries; clear it
  with `--features all`.

Other useful options include `--warn-tokens N`, `review --brief K`,
`review --quiet-if-clean`, and `uninstall --keep-blocks`. Run
`hologram <command> --help` for the full CLI.

## Choosing what the map carries

A budget decides how much fits. `--features` decides what is eligible in the
first place — useful when a fact class is noise for your repository rather than
merely expensive.

| Feature | What it renders |
|---|---|
| `calls` | Call chains between project symbols (`sig > callee`) |
| `types` | Declared return and parameter types (`:Ret`, `arg:T`) |
| `params` | Parameter names on signatures (`f(order,items)`) |
| `relations` | Supers, implements, sealed permits, implementors |
| `fields` | Field names, record components, enum values |
| `constants` | Public constants and their short values |
| `decorators` | Routes and framework annotations (`@GET/path`) |
| `raises` | Declared or thrown exception types (`!E`) |
| `usage` | The `×0` marker on symbols with no static reference |
| `size` | The `~N` body-size marker on large bodies |
| `private` | The names-only private member inventory |
| `tests` | The test index: files, helpers, fixtures |
| `support` | `tools/` and `benchmark/` landmark lines |

The package tree, type headers, and public signatures are the map's identity
and always render; `--features none` leaves exactly those. A deselected class is
absent at every level, so a budget can never restore it. The legend states only
notation the map actually uses, so it shrinks with your selection.

```bash
hologram build --root . --interactive
```

`--interactive` (on `build` and `init`) prices a selection against the full map
before writing it. It needs a terminal and fails with the equivalent `--features`
flag otherwise, so hooks and CI never block on a prompt. Note that dropping
`calls` can make a map *larger*: a private helper named only by a retained call
chain has to reappear in the private inventory once that chain is gone.

## Review changes

`hologram review [REV]` looks for near-duplicate callables, repeated test
coverage, new public symbols with no static references, tests that name removed
code, public API drift, and additions that appear misplaced. Findings are
deterministic heuristics and advisory: a successful review still exits zero.
The post-commit hook runs the same review against the previous commit.

Review scans Git-indexed files. Use `git add -N path/to/file` to include a
completely untracked addition. JSON output contains project paths and symbols,
so treat it as repository-derived data.

## Token budgets

When a full map exceeds `--budget N`, Hologram starts with a compact semantic
floor: retained business types, fields, and top-level signatures with exact
file ownership, plus external entrypoints and tool/benchmark orientation. The
test index is optional in full: at the floor it is absent entirely, and above
it each test file landmark, suite/case label, and test-to-business edge is a
separately restorable fact — a restored label always brings back the file
landmark it renders on. Hologram then restores ranked whole facts,
prioritizing tested and cross-file paths, widely used APIs, and breadth across
files.

Below that floor is one more level, reached only when the semantic floor itself
cannot fit: a structure-only map that states the same facts in project vocabulary
alone. Return and parameter types, decorators and route paths, `!throws`, the
`~N`/`×0` markers, `{field}` lists and type relations are language and
framework words, and all of them go; the source tree, type names, and function
names with their parameter names stay. On this repository that is 714 tokens down
to 460. Ranked facts still compete for whatever slack remains above it. If even
that cannot fit, Hologram warns and emits the smallest complete candidate instead
of cutting facts in half.

The budget applies to the digest. `hologram stats` separately reports the
wrapper, coaching text, and total managed-block estimate. `--warn-tokens`
checks that managed block for `build`/`init`, while `print` checks its printed
digest. Estimates use `ceil(characters / 4)` for deterministic planning; they
are not tokenizer counts from a particular model.

## Language support

- Application code: Java, Python, TypeScript, JavaScript, TSX/JSX, C#, Kotlin,
  Go, Rust, C, C++, PHP, Swift, Scala, Ruby, and Lua.
- Components and web assets: Angular, React, Vue, Svelte, HTML, and CSS.
- Project files: Bash/zsh scripts, Helm charts, and Makefiles.

Extraction depth varies by language. Where supported, maps retain types,
fields, signatures, relations, resolved calls, constants, throws, routes,
annotations, component usage, and framework entrypoints. Python uses the
standard library's `ast`; Helm and Make support are also built in. Most other
languages use optional tree-sitter grammars.

## Context files and freshness

When `--root` is a Git worktree root, Hologram scans indexed source files.
Otherwise, it walks the tree while pruning hidden, generated, vendored, and
fixture directories.

Hologram recognizes the instruction files used by Claude Code, Codex, opencode,
Jules, Zed, Amp, Gemini CLI, Qwen Code, Aider, GitHub Copilot, Cline, Cursor,
Windsurf, Roo Code, JetBrains Junie, Continue, and Kiro. Auto-detection updates
supported files that already exist; supported rule directories receive one
managed rule file. If no target exists, Hologram creates `CLAUDE.md`. Use
`--target` when you want an explicit destination.

The `state` stamp covers source content and Hologram's generator code. It does
not fingerprint the Python runtime or installed grammar versions, so rebuild
after upgrading that toolchain even if `check` still reports fresh.

## Limits

Hologram is static context, not proof:

- `×0` means no static project reference was observed, not that code is safe to
  delete.
- A test edge means a test references a symbol, not that the behavior is
  correct.
- Test inventories are declaration-based. Cases named only through strings,
  framework DSL calls, or macros are not extracted as function/method names.
- Function bodies are summarized rather than embedded.
- Extraction depth varies, and review findings can produce false positives.

## Development

Run the dependency-free test profile:

```bash
python3 tools/run_tests.py --profile core
```

With every optional grammar installed, run the complete profile with no allowed
skips:

```bash
.venv/bin/python tools/run_tests.py --profile full
```

See [CHANGELOG.md](CHANGELOG.md) for release details. The
[benchmark guide](benchmark/README.md) describes matched map/control experiments
and their privacy boundary.

## License

MIT — see [LICENSE](LICENSE).
