dircue 1.0.0
dircue 1.0.0
Try it
dircue map --summary /path/to/checkout
dircue map --json /path/to/checkout > map.jsonThe directory-map guide documents the schema, coverage semantics, attachments, comparison, routing and SARIF location support. The compatibility policy lists what 1.x keeps stable.
dircue 1.0 adds the map. One deterministic, offline command answers what an unfamiliar directory contains and how it is built and run: projects, deployables, declared interfaces and capabilities, and the relationships between them. Every fact carries evidence identifying its source file and rule; a source span is included when the analyzer can locate one. Every question has a coverage status. The strict Linguist-compatible language commands remain available with their 0.9 output contract; changes to other commands are listed below.
The map
-
dircue map [path]writes a portable map document (schema/map.schema.json, schema version 1.0.0) for a committed Git tree or an ordinary directory. Redirected output defaults to JSON; a terminal gets a one-screen summary (--summary). Inspected content is never executed. -
Output. Nodes (content populations, components, deployables, interfaces, capabilities, packages) and edges (
contains,member_of,depends_on_local,depends_on,builds,runs,exposes,declares,uses_capability,packaged_in,analyzed_by) have stable IDs derived from paths and declared names. Evidence identifies the source file and rule, with a span when available. Coverage (complete,partial,unknown,not_run,tool_error) includes named reasons. DockerfileCOPYobservations include thecopy_sourceandcopy_source_stagefact kinds; recognized Maven archive attribution uses thedockerfile_copy_source_matches_maven_archiveedge reason. -
What
completemeans. A question, node or edge sayscompleteonly when its evidence is exhaustive for its scope. Heuristic attribution, such as a capability credited to a component by directory containment or a Dockerfile shared by several projects, ispartial, with a named reason. Content problems never abort a map; they degrade coverage. -
Components come from static, bounded parsing of 36 component kinds, reported under 27
ecosystemvalues:- npm, Go, Cargo and Python (pyproject, uv,
setup.cfgwith project metadata,Pipfile, requirements files and their-rincludes); - Maven, Gradle, .NET and Kbuild;
- Ruby (Bundler, gemspec, Rails application name) and PHP (Composer);
- Swift, Dart (including
path:dependencies), Elixir, Erlang, Scala (sbt) and Haskell; - CMake, Meson and Autoconf;
- Deno, Bazel, Zig, Julia, R, Clojure and Perl.
Each component has a
role:primary,test,fixture,example,vendored,docsortooling. The summary leads with primary components. Local project relationships (depends_on_local) come from path, workspace and module references, including Maven reactor sibling dependencies and pubpath:dependencies. - npm, Go, Cargo and Python (pyproject, uv,
-
Deployables:
- Dockerfiles (named by directory);
- Compose services;
- Kubernetes objects, one per kind, name and namespace, with every declaring manifest as evidence;
- Helm charts, one per chart, with static
values.yamlimages; - Terraform modules, one per directory;
- SAM and Serverless functions, .NET Aspire app hosts, and CI workflows;
- Maven WAR and EAR packaging, as
archivedeployables named byfinalNameor Maven'sartifactId-versiondefault, with same-file properties resolved.
-
How things build and run.
buildsandrunsedges come from static declarations:- Dockerfile co-location;
- Dockerfile copies that identify a Maven WAR/EAR artifact (
dockerfile_copy_source_matches_maven_archive); - Skaffold artifacts and exact image references;
- SAM
CodeUri; - Aspire
AddProject<>(); - Maven WAR/EAR packaging;
- GitHub Actions working directories.
A
CodeUrior working directory belongs to the nearest enclosing project root. When that root holds more than one project, the path leaves the repository, or a working directory is created at run time rather than committed, no edge is emitted and the reference ispartialwith a named reason. -
Interfaces:
- Go
package mainbinaries; - Rust, npm and Python entry points;
- Spring Boot applications and Maven/Gradle main classes;
- module-level Flask application objects;
- declared ports (
EXPOSE, includingARG/ENVdefaults; Composeports/expose; KubernetescontainerPort; application listener keys); - gRPC services and operations;
- OpenAPI and Swagger documents and their operations;
- AsyncAPI documents and GraphQL schemas.
Runtime prerequisites (npm
engines,requires-python) and build backends are recorded as component properties (runtime_requirements,build_backend,build_script), not as interfaces. - Go
-
Capabilities (datastores, caches, messaging, search, object storage, auth, HTTP clients, cloud SDKs and more) come from an ecosystem-aware catalog of the most common packages per ecosystem, from declared configuration keys, connection strings and Prisma datasource providers, and from top-level Go and Python imports (including
sqlite3). Because Go'snet/httpalso serves inbound requests, it counts as an HTTP client only where a file references a client API;net/http/pprof,httptest,fcgiandcginever do (rule). There is one capability node per owning component. Optional dependencies (extras, optional and peer dependencies) areconditional. Development-only ones (npmdevDependencies, PEP 735 dependency groups, pubdev_dependencies, Pipfile[dev-packages], Gemfiledevelopmentandtestgroups) are excluded. Maventestscope is not yet distinguished (#150).
Source identity, forests and hostile filesystems
- Directory maps carry a Git-compatible tree ID computed without Git. A clean checkout has the same ID as its commit's tree, so
map comparerecognizes the same source across Git and directory maps. Control it with--set source.digest=git|raw|off. On Windows, where Git checks out without executable bits or symlinks and matches names case-insensitively, the digest ispartial(windows_checkout_semantics). map --forestdiscovers nested, bare and submodule Git roots, summarizes dependency and build-output trees (node_modules, virtualenvs,target/, …), and accounts for every remaining file. Remote credentials are redacted.- FIFOs, sockets, devices, symlink loops, escaping symlinks and unreadable directories are skipped with warnings and
partialcoverage, never followed or fatal.
Joining deeper tools, without importing findings
map --attach KIND=PATHjoins saved Syft, SARIF 2.1.0, OWASP Noir and Bifrost reports:- Only facts and run coverage are imported: packages, endpoints, covered files and run state. Findings and verdicts never are.
- SARIF revision provenance binds a report to the selected commit.
--attach-binding caller-assertedrecords an explicit caller assertion for fully digested directories. - Per-attachment record limits degrade coverage instead of failing.
map routewrites inert follow-up plans.map comparecompares maps by stable identity;--format markdowngives a readable pull-request summary, and.github/actions/map-diffis a reusable action.map locateannotates SARIF locations with map ownership, with--source-uriand--uri-basefor tools that emit container paths or undeclared bases.
Control and measurement
-
Presets:
balanced(the default);low-memory(fewer workers, a smaller Git object cache, a lower per-file cap; about 60% less peak memory on the Linux kernel);thorough(larger inventory and observation budgets).
Every setting is typed and visible with
map settings, and each is labeled performance-only or coverage-affecting. -
--stats-json PATHwrites deterministic cost counters and timings to a separate document, and--cpuprofile/--memprofilewrite Go profiles. The map document itself is byte-identical across worker counts, locations, locales and time zones.
Git reading
- The binary excludes go-git's transport client, and a build contract test checks the linked executable. The embedded fork's
go.modstill downloads unused SSH transport modules. Release binaries are about 11% smaller than 0.9.0, despite the map. - Git-mode peak memory on the Linux kernel dropped by about 35%.
- SHA-256 object-format repositories and corrupt Git directories fall back to directory mode with a warning.
Distribution
- Two command names.
dirqis a shorter name fordircue: the same program, flags, exit statuses and machine-readable output, with help text showing the invoked name. Release archives includedirq(a symlink on Linux and macOS, a copy of the executable on Windows), wheels install both commands, an image built from theDockerfilehas both, andgo install github.com/war-and-code/dircue/cmd/dirq@v1.0.0installsdirq. - The module path is now
github.com/war-and-code/dircue. The maintained Enry, go-git and scc snapshots are embedded in the module with recorded provenance, so noreplacedirectives are needed. - Release archives for Linux, macOS and Windows (amd64 and arm64 where supported) come with
SHA256SUMSand matching Python wheels. - Every release asset carries a GitHub SLSA build-provenance attestation (
gh attestation verify <file> --repo war-and-code/dircue).SHA256SUMSis additionally signed with keyless Sigstore cosign; the bundle (SHA256SUMS.sigstore.json) is attached to the release. SeeREADME.mdfor exact verification commands. - Bulky evidence receipt files from prior performance and release runs are assets of the
evidence-archive-1prerelease;make fetch-receiptsrestores them. No test or CI job reads them from the working tree.
Evidence
-
Linguist parity. Across the 38-repository atlas, dircue matches Linguist 9.7.0 exactly on all 355 language totals. Against scc 4.1.0, 261,081 of 262,575 shared per-file counters are identical. Every remaining difference is a file where dircue follows its Linguist language to a different scc grammar, recorded per file (
tests/atlas/results/1.0.0). -
Map accuracy is measured per question against hand-written labels for seven repositories (GOLDEN.md). The labeled set also informed map development, so these results are not an independent evaluation. Every question meets the gate of precision ≥ 0.90 and recall ≥ 0.80:
- components, deployables and capabilities: 1.00 / 1.00;
- interfaces: 1.00 / 0.98;
- relationships: 0.995 / 0.98 (216 matched, 1 extra, 4 missed).
The map never claimed
completewhere the labels did not. -
Holdouts labeled before dircue ran. Four repositories were labeled from source before any map run: OWASP BenchmarkJava and BenchmarkPython, AppFlowy editor (Dart) and AWS CardDemo (COBOL). The first run found 5 of 18 labeled facts. The map work they prompted finds 12 of 18 against those original labels, and 18 of 18 after the labels were restated in the map's documented vocabulary, with each change citing the source (
tests/map_corpus/holdout_labels). -
A fresh blind check. Four repositories (OpenMRS core, a Flask application, the bloc Dart monorepo and a Prisma/Express application) were labeled blind before the first candidate run (
tests/map_corpus/fresh_labels). The preserved first-run receipt predates subsequent map and scorer changes; it is not validation of the current implementation. The original frozen-label receipt records relationship precision 0.38 and recall 0.88. Re-scoring the frozen labels with the revised scorer and map yields 0.425 / 1.00; this is regression evidence, not a second blind measurement. Source review found that most extra facts were true declarations the labels had omitted. After source-checked corrections, capabilities and relationships both score 1.00 / 1.00. Those corrected scores are also regression evidence, not an independent accuracy estimate. Procfiles remain a known gap (#146). -
An independent source-first check. Two further pinned sources were labeled before dircue was run on them (
tests/map_corpus/independent_labels). Within one fully enumerated Spring Boot Docker subtree, component and deployable precision and recall were both 3/3. The bounded Flask-on-Docker oracle and the other Spring categories support targeted recall only, and expose missed facts and naming disagreements. Those observations are retained inindependent_results.json; this small sample does not establish whole-map or population accuracy. -
Committed fixtures from pinned Syft, OWASP Noir, ruff and Semgrep exercise attachment, routing and location behavior (
tests/syft-oracle,tests/tools). -
A source-first review slice. Four more repositories (chi, FastAPI, changedetection.io and Umami) were labeled from source before the candidate ran on them, by the team that develops dircue (
tests/map_corpus/review_holdout). On its first run the map matched 27 of 29 selected positive facts after vocabulary normalization (15 raw-exact) and contradicted none of the 10 scorable bounded negatives (2 fell outside their oracle scope). Source review later dropped the two misses, both Composerunslabels inferred from name similarity, as label errors; the first-run score remains the measurement. CI reproduces the score from the committed receipts. -
Test strength. The suites include 14 metamorphic invariants, mutation-testing baselines, on-demand fuzz campaigns (including dircue's Git index reader) and executable regression checks from committed receipts.
Fixes
make fuzz-campaignruns each Go fuzz function for a configurable duration (FUZZ_TIME, default 60 s); acceptsFUZZ_PKGandFUZZ_CACHEoverrides.pyproject.tomltool-only files (those with[dependency-groups]but no[build-system], runtime dependencies, or packaging layout) are treated as non-component manifests, consistent with howsetup.cfgtool-only files were already handled.- Gemfile conditional gems: gems inside
if/unless/caseblocks, one-line blocks, trailingif/unlessmodifiers andoptional: truegroups are markedconditional. Gems ingroupblocks carry agemfile-group:<names>condition; groups made only ofdevelopmentandtestdo not contribute capabilities, while other groups (such asproduction) still do. --summaryduplicate component names: components that share a name and ecosystem in sibling directories are labeled with their root path (e.g.api (modules/billing/api)) instead of being silently dropped; a solution and its same-named member project are still shown once.- Cargo auxiliary target roles:
[[bench]]targets receiverole: tooling,[[test]]integration targets receiverole: test, and[[example]]targets receiverole: example. Theroleproperty and its allowed values are documented indocs/MAP.md. - Map availability: a workflow, Kubernetes manifest or other configuration file whose name starts with
readme,changelogorcontributing(for example.github/workflows/changelog.yml) was treated as documentation, and the map aborted with no output. Only Markdown-family files and plain-text README, CHANGELOG and CONTRIBUTING files are documentation now.make corpus-availabilityrequires every repository in a corpus to map successfully. - Go
net/httpclient evidence: importingnet/httpno longer impliesnet:http-client; the file must reference a client API, and the evidence cites that line with basiscode_syntax.net/http/pprof,httptest,fcgiandcgiimports never imply a client,httputilneeds a reverse-proxy or client reference, andhttp.NewRequestalone is not client evidence. - Project attribution at ambiguous roots: a
CodeUrior workflow working directory inside a directory with two projects was attributed to a broader ancestor project. It now stops at the nearest root and recordsambiguous_component_root; a reference that resolves outside the repository (such as a root-levelCodeUri: ..) is never attributed and recordspath_outside_repository. Workflow working directories created at run time, such as a checkout that names arepository:or agit clonetarget, were attributed to the enclosing project; they now recordnamed_repository_checkoutorpath_not_in_repository, while a directory inside a checkout of this repository maps back to its repository path.
Compatibility
- Against the published 0.9.0 executable, 227 of 278 compatibility cases produce identical stdout and stderr; 51 have output changes, with no exit-status changes. Eighteen cases expose a new warning when an unborn Git repository falls back to directory mode in structured analysis. Eighteen add a Gradle root name from
settings.gradle.kts. Fifteen reflect declaration changes: wider ecosystem support, Maven names, uv manifest classification, and removal of Go-version and npm developer-task interfaces. Strict raw Linguist output matches for committed trees, ordinary directories and unborn repositories. See the comparison receipt. dircue compare(saved profiles) anddircue map compare(maps) reject each other's documents with a pointer to the right command.
Upgrading from 0.9
- Most tested invocations retain their output and exit status; see the compatibility results above for the 51 cases whose output changed. Importers of
pkg/packages must use the qualified pathgithub.com/war-and-code/dircue/pkg/.... - Install with
go install github.com/war-and-code/dircue@v1.0.0, andgo install github.com/war-and-code/dircue/cmd/dirq@v1.0.0fordirq.