Skip to content

Repository files navigation

Serval

Serval logo

CI release Go Reference license

A local-first CLI that answers: if I change this file, what am I likely to affect?

Serval analyzes a Git repository's dependency graph, Git history, and CI configuration to estimate the blast radius of a code change, with a deterministic, explainable risk score, offline, with no account and no cloud backend.

Why

Understanding downstream impact of a change usually means either tribal knowledge or grepping for imports by hand. Serval automates the mechanical part of that question using evidence already in the repository (imports, exports, history, CI) instead of guesswork or an LLM.

Status

Implemented:

  • serval inspect [path]: direct/indirect dependents, Git history, relevant CI workflows, and an explainable risk score for a file. Defaults to . (the current directory) when no path is given; for a directory, every module inside it is analyzed and reported as a risk-sorted summary.

  • serval diff [<ref>]: the same analysis for every file changed against <ref> (default HEAD, i.e. uncommitted changes)

  • serval graph <path>: one-level dependency/dependent graph for a file

  • serval history [path]: Git churn and co-change frequency, defaults to . when no path is given

  • serval <path>: convenience alias for serval inspect <path>

  • serval doctor: environment/repository checks, including whether a local Ollama daemon is reachable

  • serval version, shell completion (serval completion bash|zsh|fish), generated man pages (man serval)

  • --json on every analysis command; --output-format sarif (on inspect/diff) for a SARIF 2.1.0 report, e.g. for github/codeql-action/upload-sarif; --output <file>/-o to write a report to disk instead of stdout; --fail-on <level> (exit code 2) on inspect/diff for CI gating

  • --explain (on inspect and diff): asks an AI provider to explain each result's risk score in natural language. Off by default, makes no network call or subprocess spawn unless passed explicitly, and can never alter the deterministic score, only explain it. --explain-provider picks the backend: ollama (default, a local daemon), claude/codex/gemini (a local CLI you already have installed and signed in — reuses your existing subscription, no API key needed), or anthropic/openai/gemini-api (a direct API call with your own key, via ANTHROPIC_API_KEY/OPENAI_API_KEY/ GEMINI_API_KEY, no CLI required). On a directory or multi-file diff, this is one call per file, sequentially — can be slow.

  • TTY-aware colored output, respecting NO_COLOR

  • Language support: JavaScript/TypeScript (relative ESM/CommonJS imports, tsconfig.json paths/baseUrl), Go (imports resolved against go.mod, standard library and external modules recorded as external), Python (plain and from-imports, including relative imports; repository root treated as the sole sys.path entry, standard library/third-party imports recorded as external), Java (plain, type-wildcard, and static imports; each file's own package declaration derives its source root, standard/Maven-Gradle dependency imports recorded as external), and C (quoted #include only, resolved relative to the including file; .c/.h, not C++).

  • .serval.yml: optional per-repository overrides for the critical-path keyword list and the Git history window (see docs/usage.md).

  • CI relevance: GitHub Actions (.github/workflows/*.yml), GitLab CI (.gitlab-ci.yml), Azure DevOps Pipelines (azure-pipelines.yml), Jenkins declarative pipelines (Jenkinsfile), CircleCI (.circleci/config.yml), and Bitbucket Pipelines (bitbucket-pipelines.yml). This completes the originally planned v0.1 CI provider set, plus CircleCI and Bitbucket Pipelines.

See docs/architecture.md for what's scaffolded versus what has real logic.

Installation

Homebrew (macOS/Linux)

brew install AlbertoBarrago/tap/serval

Published automatically on tagged releases via GoReleaser: see .goreleaser.yml and AlbertoBarrago/homebrew-tap.

From source

git clone https://github.com/AlbertoBarrago/serval
cd serval
go build -o serval .

Requires Go 1.22+.

Shell completion

# zsh
echo 'source <(serval completion zsh)' >> ~/.zshrc

# bash
echo 'source <(serval completion bash)' >> ~/.bashrc

# fish
serval completion fish > ~/.config/fish/completions/serval.fish

Quick start

serval inspect src/auth/token.ts
Target
  src/auth/token.ts

Direct impact
  src/auth/middleware.ts

Indirect impact
  src/api/client.ts

CI
  integration-auth.yml

Git history
  7 significant changes (last 90 days)
  3 frequently co-changed modules

Risk
  HIGH: 82/100
  +28  14 downstream modules
  +20  critical path (matched "auth" in src/auth/token.ts)
  +14  high historical churn (7 changes)
  +12  3 frequently co-changed modules
  +8   1 CI workflow(s) affected

Machine-readable output:

serval inspect src/auth/token.ts --json

CI gating:

serval diff --fail-on high   # exits 2 if any changed file scores HIGH

AI explanation (requires ollama serve running locally):

serval inspect src/auth/token.ts --explain

See docs/usage.md for the full command reference, resolution scope, and known limitations.

Commands

Command Description
serval inspect [path] Full analysis (impact, history, CI, risk) for a file, or a risk-sorted summary for a directory. Defaults to .
serval diff [<ref>] Full analysis for every file changed against <ref>
serval graph <path> One-level dependency/dependent graph for a file
serval history [path] Git churn and co-change frequency. Defaults to .
serval <path> Alias for serval inspect <path>
serval doctor Check environment and repository compatibility
serval version Print version

Architecture

CLI -> Target Resolver -> Repository Scanner -> Language Analyzer
    -> Dependency Graph -> Impact Engine -> Risk Engine -> Output

Full write-up, including why the JS/TS analyzer is regex-based rather than a full parser, the exact module-resolution scope, and the risk scoring model: see docs/architecture.md.

Development

go build ./...
go vet ./...
go test ./...
gofmt -l .   # should print nothing
make man     # regenerate docs/*.1 after changing command help text

Fixture repositories used by tests live under testdata/fixtures/.

Contributing

This repository is colocated with Git but developed using Jujutsu (jj) as the primary VCS. Plain git commands still work for read-only inspection (git log, git diff), but the day-to-day flow uses jj.

Work happens directly on main — no feature branches, no pull requests. A typical change looks like this:

# Fetch first: main may have moved from another machine/session since
# you last looked (this repo is worked from more than one).
jj git fetch

# Start a new change on top of the latest main
jj new main

# ... edit files ...

# Describe the change once it's ready
jj describe -m "fix: describe what actually changed"

# Advance main to this change, then push it
jj bookmark set main -r @
jj git push --bookmark main

Working copy changes in jj are always an anonymous commit (@); bookmarks (jj's equivalent of branches) are separate, movable pointers that only advance when you explicitly jj bookmark set them, so there's no "detached HEAD" state to worry about.

If jj git push refuses with "stale info" (remote main moved since your last fetch), don't force it: jj git fetch, inspect the new commit(s) (jj show <rev> --stat), and rebase your change on top (jj rebase -d main) instead of overwriting.

License

MIT, see LICENSE.

About

Local-first CLI that estimates the blast radius of a code change

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages