Deterministic repository analysis toolkit for LLMs and humans.
Tinkit analyzes code repositories using established open-source static analysis tools and produces structured JSON + Markdown output. Every fact traces back to a named, versioned tool. No proprietary logic, no LLM calls, no telemetry.
Download from Releases:
# Linux
curl -L https://github.com/rayyan-41/Tinkit/releases/latest/download/tinkit-linux-amd64 -o tinkit
chmod +x tinkit
sudo mv tinkit /usr/local/bin/
# macOS (Apple silicon; use tinkit-darwin-amd64 on Intel)
curl -L https://github.com/rayyan-41/Tinkit/releases/latest/download/tinkit-darwin-arm64 -o tinkit
chmod +x tinkit
sudo mv tinkit /usr/local/bin/Windows: download tinkit-windows-amd64.exe from the same release and put it on PATH.
Release assets are unversioned by design so the latest/download/ URLs above keep working; run tinkit --version to see which release you have.
docker pull ghcr.io/rayyan-41/tinkit:latest
docker run -v $(pwd):/repo ghcr.io/rayyan-41/tinkit check /repo
docker run -v $(pwd):/repo ghcr.io/rayyan-41/tinkit scan /repoThe image is the initialized state: the analysis tools are baked in, together
with the tinkit.lock generated when they were installed, so check and scan
work without running init first and are still version-verified against that
lockfile. A repository that carries its own tinkit.lock is verified against
that one instead.
git clone https://github.com/rayyan-41/Tinkit.git
cd Tinkit
make build# 1. Initialize: download tools, create config + lockfile
tinkit init
# 2. Fast probe: detect languages, frameworks, applicability
tinkit check
# 3. Full analysis: run all applicable tools
tinkit scan
# 4. Compare two scans
tinkit diffEvery command takes the repository as an optional positional argument, so you can analyze a tree you are not standing in:
tinkit init ../some-other-repo
tinkit check ../some-other-repo
tinkit scan ../some-other-repo| Command | Description |
|---|---|
tinkit init [path] [--with-codeql] [--yes] |
Download and verify tool binaries; write tinkit.lock and a default tinkit.config |
tinkit check [path] |
Fast probe: detect languages, frameworks, ORMs, IaC, and per-category applicability |
tinkit scan [path] [--mode auto|fast|complete] |
Full analysis run |
tinkit diff [ref1] [ref2] |
Compare two scan runs (run numbers like T4, git refs, or run directory names) |
tinkit --upgrade |
Re-pin tools to their latest stable versions |
Global flags: --path <dir> (equivalent to the positional argument), --config <file>, --verbose, --json-log.
| Mode | Behaviour |
|---|---|
auto |
Default. Runs every applicable stage. |
fast |
Skips the two whole-repository engine stages — security and call_graph — which are the only ones whose cost scales with codebase size rather than with the number of manifests, models or routes. They are reported as not_applicable with a reason naming the mode, never as empty results. |
complete |
Full depth regardless of the size estimate. |
CI can gate on these (design doc §5):
| Code | Meaning |
|---|---|
0 |
Success |
1 |
General failure |
2 |
tinkit.config or tinkit.lock invalid |
3 |
One or more stages failed — an actual tool failure, not not_applicable |
4 |
Version mismatch between tinkit.lock and the installed tool binaries |
Tinkit produces:
tinkit.check.json— applicability table, language stats, code organizationtinkit.json— manifest with category pointers, counts and per-stage durationstinkit.md— index/table of contents.tinkit/latest/— category shard files (security, secrets, dependencies, etc.).tinkit/runs/— archived scan history
Markdown is a pure projection of the JSON: nothing appears in tinkit.md that is not derivable from tinkit.json and the shards.
tinkit.config (YAML):
mode: auto # auto | fast | complete
exclude:
- vendor/
- node_modules/
- '*.min.js'
stages:
security: true
secrets: true
sca: true
platforms: true
db: true
api: true
call_graph: true
security:
rules: auto # --config value handed to the SAST engine
call_graph:
rules: '' # no default: see below
codeql:
enabled: false
concurrency:
max_parallel_stages: auto
timeouts:
per_stage_seconds: 600
incremental:
enabled: auto
custom_detectors: []Unrecognized keys and invalid values are a hard failure (exit code 2) — Tinkit never guesses at what was meant. The error names the offending key, its location, and the valid keys at that level.
call_graph.rules has no default on purpose. Call-graph extraction reads from_function / to_function metadata off each match, which only purpose-written rules carry; the generic security ruleset has none. With no ruleset the stage reports not_applicable with that reason rather than running the SAST engine over the whole repository to find nothing.
| Category | Tool | What it finds |
|---|---|---|
| Security | Opengrep | SAST vulnerabilities, CWEs |
| Secrets | TruffleHog | Hardcoded keys, tokens, passwords (location only — never the value) |
| Dependencies | OSV-Scanner | CVEs in packages, with fixed versions |
| Platforms | Checkov | IaC misconfigurations (Terraform, Docker, Kubernetes, …) |
| Database | ORM introspect | Table schemas, columns, constraints, relationships |
| API | Framework extract | Endpoints, methods, auth |
| Call graph | Opengrep | Function call edges (requires call_graph.rules) |
| Git history | git log |
Recent commits, changes |
A category that cannot run says so and why. not_applicable (nothing here to analyze, or the tool is not installed), succeeded_empty (it ran and found nothing) and failed are three different states and are never collapsed into one.
tinkit init downloads these and pins them in tinkit.lock:
| Tool | Purpose |
|---|---|
| Opengrep | SAST and call-graph extraction |
| TruffleHog | Secret detection |
| OSV-Scanner | Dependency vulnerabilities |
| Checkov | IaC misconfiguration |
| ripgrep | Fast search |
| tree-sitter | Parsing |
| Joern | Alternate code analysis engine |
Some tools publish no downloadable binary for some platforms, and init reports those rather than failing: Semgrep (pip/Homebrew, macOS and Linux only — used only if installed and Opengrep is absent), repomix (npm), universal-ctags (bundled binary on Windows, package manager elsewhere), and the CodeQL bundle (--with-codeql, GHAS licence required for private repositories).
A tool that is not installed makes its category not_applicable with an install hint. It does not fail the scan.
MIT