brief is a single-binary CLI tool written in Go that deterministically detects a software project's toolchain, configuration, and conventions, then outputs a structured report of what the project is and how to work with it.
It lives in the git-pkgs GitHub org alongside forge, git-pkgs, and shared Go modules that provide low-level ecosystem mapping — manifest parsing, package URL handling, version range resolution, and ecosyste.ms API integration. By building on these shared modules, brief launches with support for 30+ package ecosystems from day one.
The first consumer is Upkeep, the Forgejo-based autonomous package maintenance bot swarm. brief is one of several deterministic tools that Upkeep runs to provide structured context to AI coding agents before they begin work — replacing the trial-and-error exploration loop that burns turns and tokens. Beyond Upkeep, brief is useful for any AI coding agent (Claude Code, Copilot, Cursor, etc.), developers onboarding onto new codebases, and CI/CD pipelines.
brief does not score, grade, or judge. It reports facts.
Every AI coding agent, every new contributor, and every CI pipeline faces the same bootstrap problem: what language is this, how do I install dependencies, how do I run the tests, what linter is configured, where are the docs?
This knowledge is tribal — experienced developers "just know" that a Gemfile means bundle install, that spec/ with rspec means bundle exec rspec. No tool codifies the full decision tree across ecosystems in a machine-readable way. Existing tools solve adjacent problems: Repolinter (archived) checks governance, Netlify framework-info detects web frameworks only, mise manages declared toolchains, linguist does language detection only.
A Ruby developer on a Ruby project already knows the ecosystem. But that same developer cloning an R project, an Elixir project, or a Rust workspace is lost. They don't know what NAMESPACE and DESCRIPTION files mean, they don't know that mix test runs tests in Elixir. Brief tells them what they're looking at, what commands to run, and where to learn more. The breadcrumb links become the onramp into an unfamiliar ecosystem.
This is equally true for AI agents — an agent fluent in Python from training data will struggle with Nim or Zig. A single brief . call replaces a dozen exploratory turns of listing directories, reading manifests, and guessing at commands.
Enrichment scales naturally with the environment. A Ruby developer briefing a Ruby project has all the tools installed for deep analysis. That same developer briefing an R project gets full core detection from the baked-in knowledge base, plus breadcrumbs for what tools to install for deeper analysis.
Because the knowledge base defines what categories of tooling could exist for each ecosystem, brief highlights what's missing — at the project level ("no type checker configured") and at the aggregate level when run across thousands of repos via ecosyste.ms ("12% of PHP projects have a formatter vs 90% of Rust projects").
This is actionable intelligence for supply chain security work (Alpha-Omega, OpenSSF) and feeds into Upkeep's confidence model — strong ecosystem tooling means more confidence in automated changes.
- Single binary, zero runtime dependencies. Works in a
FROM scratchcontainer. - Deterministic. Same repo, same output, every time. No AI, no non-deterministic heuristics.
- Machine-first, human-friendly. JSON by default, human-readable on TTY.
- Knowledge base as the primary asset. Detection rules are data (TOML), not code. Adding ecosystem support is a data contribution.
- Offline by default. Everything baked in at build time. Network enrichment is opt-in.
- Non-judgmental. Reports what is detected and what is absent. The consumer decides what matters.
- Fast by default, deep on demand. Core detection targets <100ms on small repos, <500ms typical, <2s on large monorepos. Deep scans are opt-in subcommands. Go benchmarks run in CI against reference repos to catch regressions.
- Reuse git-pkgs modules. Manifest parsing, enrichment, and ecosystem primitives come from shared libraries.
- Library-first architecture. The detection engine, knowledge base, and reporters are separate Go packages within this repo. The CLI is a thin consumer. Other tools (Upkeep, editor plugins, CI integrations) can import the packages directly as a Go module dependency without shelling out to the binary.
- Brief does not execute project code or run any detected commands.
- Brief does not modify the project in any way.
- Brief does not enforce standards or tell you what tools you should be using.
- Brief does not replace CI, linters, or package managers. It tells you which ones are configured.
- Brief does not install tools or manage runtime versions. It reports the install and setup commands (e.g.
bundle install,rbenv install 3.4.2) so consumers can act on them.
┌─────────────────────────────────────────┐
│ Reporter │
│ (JSON / human-readable) │
├─────────────────────────────────────────┤
│ Knowledge Base │
│ (TOML files embedded in binary) │
│ detection rules → commands → breadcrumbs│
├─────────────────────────────────────────┤
│ Detectors │
│ (file exists / file contains / │
│ dependency lookup via manifests lib / │
│ manifest key / glob match) │
└─────────────────────────────────────────┘
▲
│
Local filesystem
The engine processes in order: language detection (file extensions, manifests) → package manager (manifest/lockfile presence) → project scripts (Makefile, package.json scripts, Justfile, Taskfile, Rakefile) → tool detection per category (test, lint, format, typecheck, docs, security) → style conventions (indentation, line endings, file layout) → platforms and environments (CI matrices, runtime versions, OS targets) → infrastructure (CI, containers, dependency bots, hooks) → project resources (README, CONTRIBUTING, LICENSE, etc.).
Project scripts take priority over inferred commands — if the Makefile defines a test target, that's the test command regardless of what framework is detected underneath. When a script wraps a detected tool, both are reported with their provenance so consumers get the full picture.
Every detected command carries a source field indicating where it came from:
| Source | Meaning | Example |
|---|---|---|
project_script |
Declared in Makefile, package.json scripts, Justfile, etc. | make test from Makefile |
knowledge_base |
Inferred from tool detection rules | bundle exec rspec from rspec.toml |
config_file |
Extracted from a tool's own config | Custom script in .github/workflows/ci.yml |
When a project script wraps a known tool, the output includes both: the script as the primary command with source: project_script, and the underlying tool as inferred_tool with its own provenance. An agent running make test can still know Jest is underneath and apply Jest-specific knowledge.
When multiple tools are detected in the same category, all are reported. A project with both jest.config.js and vitest.config.ts lists both test frameworks. Multiple languages are all reported, with the primary language (by file count) listed first. Brief does not pick winners — it tells you everything it finds and lets the consumer decide what matters.
Brief detects basic coding style conventions that apply across ecosystems. Explicit configuration takes priority over inference, following the same pattern as project scripts overriding detected commands.
Configured style (from files): .editorconfig, formatter configs (prettier, rustfmt.toml, .clang-format), per-ecosystem style configs (rubocop, black, gofmt).
Inferred style (sampled from source files when no config exists): indentation style and width (tabs, 2-space, 4-space), line endings (LF vs CRLF), trailing newline convention. Inference samples a bounded number of files to stay within the performance budget.
File layout: source directory convention (src/, lib/, app/, flat), test directory convention (test/, tests/, spec/, __tests__/, colocated), whether tests mirror source structure.
Brief extracts target platforms and runtime versions from CI configuration and project metadata.
CI matrices: Ruby versions, Node versions, Python versions, OS targets parsed from GitHub Actions, GitLab CI, and other CI configs. Reports the full matrix so consumers know what versions a project tests against.
Runtime version files: .ruby-version, .node-version, .python-version, .tool-versions, mise.toml, rust-toolchain.toml, .go-version. These indicate the project's expected development environment.
Build targets: cross-compilation targets from Makefile, goreleaser configs, Cargo.toml target triples, platform-specific build flags.
| Primitive | Example |
|---|---|
files — file/dir exists (glob) |
["Gemfile", "*.gemspec"] |
dependencies — in manifest (via manifests lib) |
["rspec", "rspec-core"] |
dev_dependencies — dev/test deps only |
["rubocop"] |
file_contains — string in file |
{"pyproject.toml" = ["[tool.ruff]"]} |
manifest_key — key in structured manifest |
{"package.json" = ["scripts.test"]} |
Each primitive carries an implicit confidence level based on how specific the signal is:
| Confidence | Source | Example |
|---|---|---|
high |
dependencies or dev_dependencies match |
rspec in Gemfile |
high |
file_contains match |
[tool.ruff] in pyproject.toml |
medium |
manifest_key match |
scripts.test exists in package.json |
medium |
specific config file exists | .rspec, jest.config.ts |
low |
directory or glob match alone | spec/ directory exists |
Confidence is included in JSON output. Consumers can filter or weight detections accordingly. Human-readable output omits it unless --verbose is set.
Core detection is pure file inspection — fast and offline. Following diffoscope's approach, brief can optionally use external tools when available:
Enrichment (fast, automatic when on PATH): git for repo metadata, scc/tokei for line counts, make -qp for Makefile target parsing.
Inlined capabilities: Where feasible, Go libraries are used instead of external tools for essential output — license detection, basic line counting, git metadata via go-git. The rule: if it's essential for a useful default output, inline it. Brief should produce a comprehensive report in a FROM scratch container.
Deep scans (slow, opt-in via brief scan): semgrep for security analysis, trivy/grype/osv-scanner for vulnerability scanning, hadolint for Dockerfile linting. Never run by default.
Network enrichment (opt-in via brief enrich): Dependency health, vulnerabilities, package metadata, and repository stats from ecosyste.ms APIs via the git-pkgs enrichment module. Automatic when using remote URLs.
The default output includes breadcrumbs pointing to available enrichment and scans without running them.
One TOML file per tool, organised by ecosystem. _shared/ for cross-ecosystem tools (CI, containers, dependency bots).
knowledge/
├── ruby/ (language.toml, bundler.toml, rspec.toml, rubocop.toml, ...)
├── python/ (language.toml, uv.toml, pytest.toml, ruff.toml, mypy.toml, ...)
├── rust/ (language.toml, cargo.toml, clippy.toml, rustfmt.toml)
├── go/ (language.toml, gomod.toml, golangci-lint.toml)
├── node/ (language.toml, npm.toml, pnpm.toml, jest.toml, eslint.toml, ...)
├── java/ ...
├── elixir/ ...
├── php/ ...
└── _shared/ (github-actions.toml, docker.toml, renovate.toml, dependabot.toml, ...)
[tool]
name = "rspec"
category = "test" # test, lint, format, typecheck, docs, build,
# ci, container, security, dependency_bot, ...
homepage = "https://rspec.info"
docs = "https://rspec.info/documentation"
repo = "https://github.com/rspec/rspec"
description = "BDD testing framework for Ruby"
[detect]
files = ["spec/", ".rspec"] # OR — any match triggers
dependencies = ["rspec", "rspec-core"] # OR — checked via manifests library
ecosystems = ["ruby"]
[commands]
run = "bundle exec rspec"
alternatives = ["rake spec", "rspec"]
[config]
files = [".rspec", "spec/spec_helper.rb", "spec/rails_helper.rb"]The schema is validated in CI — malformed TOML files fail the build.
JSON when piped, human-readable on TTY, verbose with --verbose for breadcrumb links.
brief v0.1.0 — /home/user/my-project
Language: Ruby (also: JavaScript)
Package Manager: Bundler (bundle install)
Lockfile: Gemfile.lock ✓
Scripts (Makefile):
test: make test
lint: make lint
build: make build
Test: RSpec (bundle exec rspec)
Lint: RuboCop (bundle exec rubocop)
Format: —
Typecheck: Sorbet (bundle exec srb tc)
Style: 2-space indent (editorconfig) LF trailing newline
Layout: app/ lib/ spec/ (mirrors app/)
Platforms: Ruby 3.2, 3.3, 3.4 (CI matrix)
.ruby-version: 3.4.2
OS: ubuntu-latest (CI matrix)
CI: GitHub Actions (.github/workflows/ci.yml)
Container: Dockerfile ✓ Compose ✗ Devcontainer ✗
Dep Updates: Dependabot (.github/dependabot.yml)
Resources: README.md ✓ CONTRIBUTING.md ✓ CHANGELOG.md ✓
LICENSE (MIT) ✓ SECURITY.md ✗
With --verbose, each tool also shows config paths, homepage, docs URL, and repo link.
brief [flags] [path | url] Core detection (fast, offline)
brief enrich [flags] [path | url] Core + ecosyste.ms data
brief scan <type> [path | url] Deep analysis with external tools
brief list tools All tools in the knowledge base
brief list ecosystems Supported ecosystems
brief schema JSON output schema
Global flags:
--json Force JSON output
--human Force human-readable output
--verbose Include breadcrumb/reference information
--category <c> Only report on specific category
--offline No network calls. Errors on remote URLs.
--keep Keep downloaded remote source
--version / --help
Scan types:
brief scan security Semgrep with ecosystem-appropriate rulesets
brief scan vulnerabilities trivy/grype/osv-scanner against dependencies
brief scan docker hadolint against detected Dockerfiles
brief scan all Run all available scans
Remote sources:
brief https://github.com/rails/rails Git repo URL
brief npm:express Registry package (also pypi:, crate:, gem:)
Registry packages resolve to source repos where possible, falling back to published archives. Remote URLs automatically enable network enrichment.
- Go, single statically-linked binary, cross-compiled for linux/darwin/windows (amd64/arm64)
- git-pkgs shared modules:
manifests(30+ ecosystem manifest parsing),enrichment(ecosyste.ms API), plus purl, vers, SPDX primitives as needed - Knowledge base embedded at build time via Go
embed. TOML validated in CI. - Distribution: GitHub releases, Homebrew, AUR, direct binary downloads
- Size target: < 10MB
Adding a new tool: create a TOML file in the appropriate ecosystem directory, add test fixtures, submit PR. CI validates the TOML and runs detection against fixtures.
Adding a new ecosystem: create directory under knowledge/, add language.toml plus at least a package manager, test framework, and linter.
Test fixtures in testdata/ are minimal project directories with expected JSON results alongside them.
- Monorepo support — workspace-aware detection of multiple sub-projects
- Custom overlays —
.brief.tomlin a project to override/extend detection - MCP server mode — expose detection as an MCP tool for direct agent integration
- Execution-based detection —
make --dry-run, CI log parsing
brief — as in "brief me on this project" or "here's the brief."
Evaluated and rejected: probe (probe-rs), detective (JS collisions), inspect (subcommand everywhere), triage (security associations), recon (military), rundown (@rushstack collision), onboard (Linux keyboard), vibe (saturated), orient (OrientDB), survey (form builders).