Repository navigation
Releases
Releases · Attackwave/glasir
v0.11.7
Rebuild note: the graph format changed, so the first start after the upgrade builds each tree once.
Calls are linked only where the build allows them
- A Rust name reaches only its crate and the crates it depends on. A call matched by name alone linked to the one definition of that name anywhere in the tree, even in a crate the caller cannot reach. It now links only when that definition is in the caller's crate or a crate its
Cargo.tomlnames bypath(directly or through[workspace.dependencies]). - A standard method on a value of unknown type links nowhere.
x.len(),x.is_empty(),x.iter()… no longer point at a tree method of that name;find_callerslists such calls from the definition's own crate underundecided_callers. - A call to a definition the file has just renamed or removed is no longer taken as a local call, so
detect_changeslists the dangling callers in the same file too. r#nameis the namename.
Tools
find_unusedlists the definitions of Rust files nomoddeclaration reaches undernot_compiled, andoverviewnames those files. Crate roots,#[path], inline modules andinclude!are followed.overviewlists the crates of a Rust workspace and names a subsystemcrate/stem, so thelib.rsof two crates no longer read as one subsystem.affected_testspages its tests withlimit(default 50) andoffset, and for Rust names thecargo testruns that cover them.change_historyreturns the firstbody_lines(default 6) of each commit body and says how many it left out.query_graphranks an implementation before the tests that repeat its words, unless the question asks about tests.explain_node:structurally_similarlists only the nearest definitions by embedding; callers, callees and the file's other definitions follow undernearby.
Security
- Access-token hashes (SHA-256) and the random bytes of new tokens come from
ring, the library rustls already uses for TLS. Stored token hashes stay valid. SECURITY.mdstates the versioning policy and that release notes name every vulnerability a release fixes.
Install
- Homebrew:
brew install attackwave/glasir/glasir - Scoop: add the bucket
Attackwave/scoop-glasir, then runscoop install glasir
v0.11.6
Rebuild note: the graph format changed, so the first start after the upgrade builds each tree once.
Calls are linked only where the code says so
- A method on a value of unknown type is a guess.
x.is_empty()linked to the one tree method of that name, in whatever crate or package it lived;cyclesandcheck_architecturethen reported cycles and violations the build system rules out. Such a call is nowambiguous, so theinferredfloor ofcyclesand ofdenyrules no longer sees it.impact,find_callersandaffected_testsstill include it. - A known type outside the tree holds its own methods.
labels.get()withlabels: HashMapno longer links to a treeget. A type in the tree is followed strictly, and a Rust method is found in everyimplof its type, wherever the crate puts it. - Rust:
use crate::…,super::…andself::…(lists and aliases included) scope a call to the file they name, so an imported function isinferredrather thanambiguous. A path keeps all its segments, sostd::fs::read_dirnever names the tree, and a path is never read as a value of the same name.f(x).g()names no receiver;Variant(x)andTupleStruct(x)are no calls. A name bound to a field (let bank = &mut self.data;) or a loop over fields of one type takes that type. - A placeholder reached from several languages links only into one of them.
Tools
detect_changeslists definitions the change removed or renamed (removed_symbols) and every definition still calling one (risk.dangling_callers), which raises the risk to high;affected_teststakes those callers as seeds. All definitions of a new, untracked file count as changed.affected_testslists a production helper namedtest_*only where a runner collects tests by name (Python, Ruby); elsewhere the path or a test attribute decides.find_unusedno longer counts a local of the same name (let x = …) as a use, and leaves out methods of a Rustimpl Trait for Type.explain_nodenames the same callers asfind_callers, once each; its similar definitions come without placeholders and most alike first.- The
cyclesdescription states its real default.
Servers
- A backend reached at the moment it ends no longer drops the client that just connected.
--lingerand--sleep-afterare checked before a backend starts; an edited language rule file, and a rebuilt binary of the same version, get a backend of their own.
Releases
- Each release is now published to the MCP registry by the release workflow.
Install
- Homebrew:
brew install attackwave/glasir/glasir - Scoop: add the bucket
Attackwave/scoop-glasir, then runscoop install glasir
v0.11.5
Servers
serveworks where the shared backend cannot. In 0.11.4 a client whose state directory was not writable, as in the container image, got an error instead of a server.servenow reaches for the shared backend first and, when that fails, serves the client itself.
Security
SECURITY.mdstates the threat model: what the index defends the machine against, that tool answers carry repository text to the model unfiltered, and how to check the binary without trusting its author.- The OpenSSF Scorecard is published for the repository.
Install
- Homebrew:
brew install attackwave/glasir/glasir - Scoop: add the bucket
Attackwave/scoop-glasir, then runscoop install glasir
v0.11.4
Servers
- One server per tree, however many assistants use it. Over stdio,
servenow relays to a backend for the tree on a Unix socket in the per-user state directory (directory0700, socket0600) and starts it when none runs. The backend holds the graph, the watcher and the sleep, and answers every client. Several assistants on one tree build and watch it once; a connection a client forgets to close costs a few megabytes and no polling. - The backend accepts connections before it reads the tree, so a client connects at once. A lock lets one client start it while the others wait, a socket left by a backend that died is replaced, and a client whose backend went away starts a new one and sends the request again.
- The backend ends a minute after its last client leaves (
--linger <seconds>). Backends are keyed by version, tree and options, so an upgrade never talks to an older one.--own-processkeeps a server per client; Windows always does.
Releases
- The Homebrew tap and the Scoop bucket move only once the release is published. A release that failed after its build left them naming archives that did not exist.
Install
- Homebrew:
brew install attackwave/glasir/glasir - Scoop: add the bucket
Attackwave/scoop-glasir, then runscoop install glasir
v0.11.3
Servers
- Idle servers cost almost nothing. A watching server polled its whole tree four times a second while nothing happened. It now waits longer while files stay still, up to two seconds, and never less than ten walks of the tree.
- Servers sleep. After ten minutes without a request a server lets its graph go and stops watching; the next request loads it again first. The connection stays open, since a stdio client does not start a stopped server again.
--sleep-after <minutes>sets the wait,0keeps the graph. - Replaced servers can be found. A client that starts a new server without closing the old connection leaves the old one running.
glasir serverslists every running server with its tree, client and last request;--stop-replacedends those a newer server for the same tree and client took over without a request since.
Servers from earlier versions record no status and are not listed.
Install
- Homebrew:
brew install attackwave/glasir/glasir - Scoop: add the bucket
Attackwave/scoop-glasir, then runscoop install glasir
v0.11.2
Fixes
- The server no longer stops on a snippet. Tools other than the scanners lexed files without their language's syntax: a JavaScript regexp holding a quote read as a string to the end of the file, a snippet range ran past its definition and the server panicked. Every reader of tokens now uses the scanner's configuration, and a range that does not fit the file is refused.
- Names stay within their language. A name used in one language no longer counts as a use of a definition in an unrelated one, so a JavaScript variable no longer reaches Rust tests.
- A documentation heading is no class. A struct sharing its name with a heading resolves its methods again.
- Rust aliases and unit structs end at their semicolon instead of taking the next item into their range and snippet.
Tools
get_code_snippetnames the lines of other definitions of the same name in the file (also_defined_at) and takesmax_lines, with the line it would go on at.
The snapshot format and the scanner identity changed; the first analyse after upgrading rebuilds the graph.
Install
- Homebrew:
brew install attackwave/glasir/glasir - Scoop: add the bucket
Attackwave/scoop-glasir, then runscoop install glasir
v0.11.1
0.11.0 was tagged but never published: a timing test stopped its build. This is that release.
Highlights
- Inherited methods resolve in every class-based language. Java, Kotlin, C#, Swift, C++, Scala, Rust, Python, JavaScript, TypeScript, Go, PHP, Ruby and Dart state their bases, and a call follows them up the chain to the method that answers it, including across traits, mixins, extensions and Go embedding. Only classes the tree defines count, and an import or the file's package decides which of two same-named classes is meant.
find_callerssays what it cannot decide. Calls by a name several definitions could answer are listed asundecided_callersinstead of reading as if nothing called the symbol.- Rust calls through a typed value resolve. A field, parameter or
letbinding states the type, soself.path.grant()reaches that type's method even when the name alone is ambiguous. - Very large trees finish. A language check walked the whole symbol table per definition, which on the largest trees never ended. Crafted files no longer cost the square of their length either: nesting and lookahead are bounded, and files stay within 1 MiB.
- A served tree is not trusted to run anything. Every git call Glasir makes checks the repository's own configuration first and refuses, by name, a repository whose
.git/configsets something git would run on a diff, a log or the index:core.fsmonitorother than git's own daemon,diff.external, a diff driver'scommandortextconv,filter.*. Before, serving a checkout with a nested repository carrying such a setting ran it on the nextdetect_changes,affected_tests,impact-oforreview. Every git child now has a deadline and a bound on the output read, and leaves the optional index locks alone. - Release archives carry build provenance. Every
.tar.gzand.zipis attested by the release workflow and verifiable withgh attestation verify <archive> --repo Attackwave/glasir; the.sha256beside it stays. The review action verifies that before it runs a downloaded release, and refuses releases before 0.11.0, which carry none.
Hardening
glasir viewserves its map with the connection cap, the deadlines and the thread per connection the MCP endpoint already had; one idle client or one wide viewport query no longer holds the map for everyone, and a viewport query costs at most the nodes it returns.- The map page escapes the names it embeds: a file whose name ends a script element could run in the map's origin before.
- A stored
.glasir-graphis checked against itself after reading (offsets, edge arrays, targets, names, communities, hubs) and rebuilt when it does not agree, as for a stale version; a crafted file could stop the server before. - The release workflow holds write access only in the jobs that publish; the tools it installs are pinned by version, the container's base images by digest, and the image build leaves out the files Glasir writes beside a tree.
Assistants
glasir installadds a session hook that points a coding assistant at the graph in an indexed tree, and the repository ships a plugin with a marketplace entry.
Scanners
- Block balance on real code: blocks, strings and comments no longer derail a file in Ruby, TypeScript, JSX, Kotlin, Dart, Elixir, PHP, C and C++ macros and conditional branches, Java and C# constants, or shell. Heredocs,
#{...}and${...}with nested quotes, raw strings, regexps and JSX text are read as what they are. - Classes and methods defined in one file resolve per class;
super.run()inrunno longer links to itself. overviewstates the server version and when the index was loaded, and warns about directories that repeat each other.impactof a class or module reaches through its members.
Review
- A pull request from a fork, whose token cannot comment, no longer fails the action's comment step: the report is in the job summary and the gate applies.
- Release notes come from the tree (
.github/releases/<tag>.md) and travel through the release's pull request.
Operations
- The flood regression test compares cost against a quarter of the input instead of a fixed time, so a slower runner fails nothing.
- Partition floor 58 -> 62.
The snapshot format and the scanner identity changed; the first analyse after upgrading rebuilds the graph.
Install
- Homebrew:
brew install attackwave/glasir/glasir - Scoop: add the bucket
Attackwave/scoop-glasir, then runscoop install glasir
v0.10.0
Highlights
- Several repositories as one tree.
glasir serveon a directory holding more than one git repository (up to two levels down) serves them as one graph: HTTP routes already joined across repositories, and nowdetect_changes,co_changes,change_history,impact-ofand the git hooks work per repository, with each path prefixed by its repository. - Workflows and local actions are in the graph.
.github/workflows/*.ymland everyaction.ymlare read for what they express: jobs are symbols,needsconnects them, anduses: ./…and the scripts a step runs connect the job to those files.impacton a script shows the workflow that runs it;glasir reviewon a workflow change has symbols to report..github/workflows,.github/actionsand.github/scriptsenter the indexed scope; issue templates, dependabot and the rest of.githubstay out. - Rust calls through a module resolve.
commits::changes()reacheschangesincommits.rs— beside the caller, under the crate'ssrc/, or as amod.rs— where before the bare name was refused whenever two files defined it.
Review
- A symbol a test reads through a reference (a constant, a table a check iterates) now counts as reached by that test;
reviewreported such code as untested before. - A file the history says usually changes with a changed one is a
mediumreason now, nothigh: a reminder, not a blocked merge on its own.highis a changed symbol with callers and no test reaching it.
Fixes
- Directories named
parsers,bench/deeporbench/langswere left out of every tree. Those rules belonged to this repository and are in its own.glasirignorenow; the indexer's fixed rules keep onlytmpandvendorat the root andtarget,node_modulesand dot-directories anywhere. - Shell scripts: only the command is a call.
set -eno longer yields a call toe,echo hinone tohi,local owner=…none toowner; flags, variables, operands and continuation lines are arguments. A shell script has a top-level node even when it calls nothing, so a step running it has something to point at. - A Rust value named like a file (
community.get(i)besidecommunity.rs) was read as a call into that file. - A root
action.ymlread as plain YAML produced dozens of nodes named after its keys.
The snapshot format and the scanner identity changed; the first analyse after upgrading rebuilds the graph.
Install
- Homebrew:
brew install attackwave/glasir/glasir - Scoop: add the bucket
Attackwave/scoop-glasir, then runscoop install glasir - Archives for Linux (static, x86_64 and arm64), macOS (arm64 and x86_64) and Windows are attached, each with a SHA-256 checksum, the licences and an SBOM.
v0.9.1
Fixes
glasir review --fail-on <level>gated nothing. The command-line parser took--fail-on highas a flag followed by a revision namedhigh, so the merge gate in 0.9.0 never fired unless written--fail-on=high; the GitHub Action passes the spaced form, so itsfail-oninput had no effect. The same applied toimpact-of --depth <n>andguard --rules <file>. All three read their value now, and a check makes sure every option the code reads is parsed with one.
Install
- Homebrew:
brew install attackwave/glasir/glasir - Scoop: add the bucket
Attackwave/scoop-glasir, then runscoop install glasir - Archives for Linux (static, x86_64 and arm64), macOS (arm64 and x86_64) and Windows are attached, each with a SHA-256 checksum, the licences and an SBOM.
v0.9.0
Highlights
- A review on every pull request.
glasir review [rev]prints whatdetect_changesandaffected_testsknow about a change, as Markdown: the risk level and its reasons, the changed symbols, their dependents by distance, the tests that reach the change, and the files the graph does not cover.--fail-on mediumturns it into a merge gate. A GitHub Action in this repository (Attackwave/glasir@v0.9.0) runs it on each push to a pull request and keeps one comment current; the graph is built on the runner and no source leaves it. - Five more coding assistants.
glasir installregisters with Copilot CLI, Roo Code, Kiro, Amp and Continue, each in its own project file. That makes 14 clients, from one command in the project directory. glasir exportwrites the graph as node-link JSON, the formglasir importreads: every symbol by name, every edge with the tier it was resolved at. For moving a graph into another tool, or comparing two builds.
Agents
- At the handshake, the server now tells a client when to reach for which tool:
overviewin an unfamiliar tree,query_graphfor a question or a file's path,get_code_snippetinstead of opening the file,impactorfind_callersbefore a change,detect_changesandaffected_testsbefore a commit. Clients speaking the newer protocol revision received no instructions before.
Install
- Homebrew:
brew install attackwave/glasir/glasir - Scoop: add the bucket
Attackwave/scoop-glasir, then runscoop install glasir - Archives for Linux (static, x86_64 and arm64), macOS (arm64 and x86_64) and Windows are attached, each with a SHA-256 checksum, the licences and an SBOM.