grafLet’s talk

Your coding agent giving you trust issues?

Enforce code quality, instead of asking nicely

graf run architecture-rules/

internal/http/handler.go:88

CreateOrder reaches the DB layer without a service

internal/http/user.go:41

GetUser calls legacy_fetch() — use fetch_v2()

2 violations · exit 1

Asking nicely doesn't scale.

Turn your architecture rules into checks that actually run — across files, through the call graph, through the type layer.

no-legacy-fetch.yaml
id: no-legacy-fetchseverity: errormatch:  kind: function  not: { files: "internal/legacy/**" }  calls: legacy_fetch      

Bigger jobs run the same way.

A migration is the same file with more steps — each with its own selector, so one recipe threads a signature, repoints a constructor, relocates a file and reaps what that leaves dead, in turn.

retire-legacy-client.yaml
id: retire-legacy-clientsteps:  - id: thread-a-timeout          # signature ripple — every call site remapped positionally    match: { kind: symbol.method, name: fetch, memberOf: Client, files: "internal/legacy/**" }    do:      - sig: { append: "timeout: float = 5.0" }   - id: repoint-the-constructor   # every reference repo-wide, imports fixed per file    match: { kind: symbol.function, name: new_legacy_client }    do:      - retarget: { to: "internal.data.client.new_client" }   - id: relocate-the-helpers      # move the file, rewrite every importer    match: { kind: file, files: "internal/legacy/util.py" }    do:      - move: { to: "internal/data/util.py" }   - id: reap-what-is-now-dead     # only what nothing uses — a live decl is refused    match: { kind: symbol.function, files: "internal/legacy/**", usedBy: { count: 0 } }    do:      - delete: {} verify:  - match: { kind: symbol, name: new_legacy_client, referencedBy: {} }    expect: { count: 0 }gate: "pytest -q"

Every step commits atomically, behind one gate — and a step that would break something pauses instead, with the blockers listed.

One graph. Every layer of your code.

graf builds a semantic graph of your codebase — AST, cross-file symbols and types — and lets you write rules against actual structure in YAML rather than text patterns.

  • Local-first

    One binary, no daemon or network access needed. Nothing leaves the machine.

  • Cross-file by construction

    Symbols and types resolved repo-wide.

  • Polyglot by design

    One graph schema, one rule language, per-language importers.

  • CLI-first, agent-ready

    Drive it yourself from the terminal, or let your coding agent drive it — we ship a skill.

Your linter sees one file at a time. graf sees the whole project.

Platforms & languages.

Runs anywhere you build. macOS · Linux · Windows.

  • Goshipped
  • Pythonshipped
  • TypeScriptnext
  • Javaplanned
  • JavaScriptplanned
  • .NETplanned
  • C / C++planned
  • Rubyplanned
  • PHPplanned
  • Rustplanned

Let’s talk.

daniel@graflabs.org

Bring a repo. We’ll write a rule against it.