{"id":48986785,"url":"https://github.com/get-tmonier/argot","last_synced_at":"2026-07-15T18:01:35.468Z","repository":{"id":352116604,"uuid":"1213857608","full_name":"get-tmonier/argot","owner":"get-tmonier","description":" Every repo has an argot. A statistical model learns yours and flags commits that don't speak it. ","archived":false,"fork":false,"pushed_at":"2026-07-09T08:55:28.000Z","size":25694,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-09T09:08:32.425Z","etag":null,"topics":["ci","cli","code-quality","code-review","coding-style","developer-tools","git","linter","rust","static-analysis"],"latest_commit_sha":null,"homepage":"https://argot.tmonier.com","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/get-tmonier.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-17T20:50:28.000Z","updated_at":"2026-07-07T09:50:19.000Z","dependencies_parsed_at":"2026-05-03T01:03:11.185Z","dependency_job_id":null,"html_url":"https://github.com/get-tmonier/argot","commit_stats":null,"previous_names":["get-tmonier/argot"],"tags_count":84,"template":false,"template_full_name":null,"purl":"pkg:github/get-tmonier/argot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-tmonier%2Fargot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-tmonier%2Fargot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-tmonier%2Fargot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-tmonier%2Fargot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/get-tmonier","download_url":"https://codeload.github.com/get-tmonier/argot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-tmonier%2Fargot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35515491,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-15T02:00:06.706Z","response_time":131,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ci","cli","code-quality","code-review","coding-style","developer-tools","git","linter","rust","static-analysis"],"created_at":"2026-04-18T13:04:44.466Z","updated_at":"2026-07-15T18:01:35.461Z","avatar_url":"https://github.com/get-tmonier.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/argot-logo.svg\" alt=\"argot\" width=\"200\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eYour codebase has a voice. argot makes AI code speak it.\u003c/strong\u003e\u003cbr/\u003e\n  \u003cem\u003eAI writes the code. argot harnesses it with the one thing that can't hallucinate: your repo's own history. Statistics, not a second LLM — flagging a dependency you've never used, a function you already wrote, an import that breaks your layering, a test quietly weakened, a convention only your team knows. 100% local, replayable.\u003c/em\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://argot.tmonier.com\"\u003e\u003cstrong\u003eargot.tmonier.com\u003c/strong\u003e\u003c/a\u003e\n  \u0026nbsp;·\u0026nbsp;\n  \u003ca href=\"https://argot.tmonier.com/docs/\"\u003eDocumentation\u003c/a\u003e\n  \u0026nbsp;·\u0026nbsp;\n  \u003ca href=\"https://argot.tmonier.com/benchmarks\"\u003eBenchmarks\u003c/a\u003e\n  \u0026nbsp;·\u0026nbsp;\n  \u003ca href=\"docs/research/README.md\"\u003eResearch log\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/get-tmonier/argot/releases/latest\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/get-tmonier/argot?color=E67E45\" alt=\"Release\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@tmonier/argot\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/@tmonier/argot?logo=npm\" alt=\"npm\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/get-tmonier/argot/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/get-tmonier/argot/actions/workflows/ci.yml/badge.svg\" alt=\"CI\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/get-tmonier/argot/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/get-tmonier/argot?color=E67E45\" alt=\"License\" /\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/status-alpha-5B8DEF\" alt=\"Status: alpha\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/rust-single%20static%20binary-DEA584?logo=rust\u0026logoColor=white\" alt=\"Rust\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/100%25-local%20%C2%B7%20no%20cloud-brightgreen\" alt=\"100% local, no cloud\" /\u003e\n  \u0026nbsp;·\u0026nbsp;\u003ca href=\"https://argot.tmonier.com/docs/languages/\"\u003e11 languages →\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\nType checkers ask *\"is this valid?\"* argot asks the question that used to live in code review: *\"is this how **we** do it here?\"* — and catches AI code that's flawless, type-correct, lint-clean, and still doesn't belong. It answers with statistics on your repo's own history — the statistical core deterministic and replayable, everything local — never a second LLM judging the first.\n\nIt also asks a second question no other tool asks: **did the AI play fair?** When an agent can't make a failing test pass, the cheapest path to \"done\" is to make the test stop looking. argot reads both sides of every diff and pairs a weakened, disabled, or deleted test with the production change it covers.\n\n### Five learned detectors — plus the rules only your repo could write\n\n| | Rule | It catches | |\n| :-- | :-- | :-- | :-- |\n| 🚫 | **`foreign-import`** + friends | a dependency, API, or idiom your repo has **never used** | *\"we don't do it this way here\"* |\n| ♻️ | **`redundant`** | a new function that **reinvents one you already have** | *\"you already have this\"* |\n| 📍 | **`misplaced`** | the right code, filed in the **wrong place** | *\"this doesn't belong here\"* |\n| 🧱 | **`layering`** | an internal import that **reverses your architecture** | *\"we never cross this boundary\"* |\n| 🧪 | **`test-deleted`** + friends | a test **quietly weakened, disabled, or removed** alongside the prod change it covers | *\"don't game the tests\"* |\n| 📜 | **your own rules** | the conventions **only your repo has** — scripted, no recompile | *\"here's exactly how we do it\"* |\n\nThe first five are learned from your git history. The sixth is [written by you](#your-conventions-as-rules) — and it's the part of every linter config your team actually cares about.\n\n## Get started\n\n```sh\n# install (single static binary — no Python, no Node)\ncurl --proto '=https' --tlsv1.2 -LsSf https://github.com/get-tmonier/argot/releases/latest/download/argot-installer.sh | sh\n```\n\nWindows: `powershell -c \"irm https://github.com/get-tmonier/argot/releases/latest/download/argot-installer.ps1 | iex\"` · npm: `npm install -g @tmonier/argot`\n\n**Sixty seconds of proof, zero setup, on your own history:**\n\n```sh\nargot audit      # ⏪ what did AI sneak into your last 50 commits?\n```\n\n`audit` fits the voice as it was 50 commits ago (in a temp worktree — your tree stays untouched), rescores everything since, and attributes every finding to its introducing commit — **ai-assisted / human / unknown**, from concrete commit markers only:\n\n```\n━━ argot audit ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n  last 50 commits · 52% carry AI markers · 1 finding would have met review\n\n  Worst offender — commit cae8349 · ai-assisted\n  ! landing/src/pages/llms-full.txt.ts:L1-32 · foreign-import\n      ↳ astro (L1), astro:content (L2) — 0 of 49 module specifiers…\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n```\n\nThen fit today's voice so `check` raises these *before* they merge:\n\n```sh\nargot init       # learn this repo's voice (~25 s on a 1,100-file repo)\nargot check      # score your working changes against it\n```\n\nAccuracy is a function of setup — argot learns from what it's allowed to see. Best path: `npx skills add get-tmonier/argot`, then `/argot-setup` in your coding agent (Claude Code, Cursor, 70+ agents) reads your repo, excludes what shouldn't shape the voice, and verifies the catch. Full guide: [Setup](https://argot.tmonier.com/docs/setup/) · [Getting started](https://argot.tmonier.com/docs/getting-started/).\n\n## Demo\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/demo/demo.gif\" alt=\"argot check flagging a foreign Django-style view in an all-FastAPI codebase\" width=\"760\" /\u003e\n\u003c/p\u003e\n\nA PR adds a **Django-style view** to an all-FastAPI codebase. mypy and ruff are silent — the framework it reaches for is one this repo has never imported:\n\n```\nargot check · 1 hunk above threshold (1 foreign)\n\nfastapi/receipts.py\n  !  L1-L10         1.00  foreign  · staged · foreign-import [94a92c256ea1]\n     ↳ django (L1) — 0 of 74 module specifiers in repo\n       common here: fastapi (357×), pydantic (129×), typing (129×) (+7 more)\n  1 | from django.views import View\n             ^^^^^^\n```\n\n`redundant` names the function you already have (`↳ duplicates slugify (src/utils/text.py:14) — similarity 0.86`), `misplaced` names where the code belongs, `layering` names the direction an import reverses, and every `↳` line is your repo's own evidence. Full anatomy: [Reading the output](https://argot.tmonier.com/docs/reading-the-output/) · [What it catches](https://argot.tmonier.com/docs/what-it-catches/).\n\n## Your conventions, as rules\n\nEvery team has conventions no generic linter ships: *\"presentational components take props — they don't fetch\"*, *\"files are parsed through our loader, never a raw `JSON.parse`\"*, *\"one HTTP client per repo\"*. They live in review comments and onboarding docs — until an AI agent, who read neither, merges around them. With argot they're **repo-local rules**: a TOML manifest + a small sandboxed script in `.argot/rules/`, versioned with your code, loaded at run time — no plugin build, no recompile, one rule format across all 11 languages.\n\nAnd they can do what no classic linter structurally can. A linter sees one version of one file; argot hands your rule **both sides of the diff** — so you can write rules about what a change *removed*:\n\n```toml\n# .argot/rules/no-dropped-endpoints/rule.toml\n[rule]\nschema = 1\nname = \"no-dropped-endpoints\"\ndescription = \"removing a public endpoint requires a deprecation cycle — catch the route that silently disappears in a diff\"\nseverity = \"error\"\nlanguages = [\"typescript\", \"javascript\"]\n```\n\n```rhai\n// check.rhai — a route that existed before this change, and is gone now\nconst ROUTES = \"(call_expression function: (member_expression property: (property_identifier) @verb)\n                arguments: (arguments (string (string_fragment) @path)))\";\nlet now = [];\nfor m in ts_query(ROUTES) { if m.capture == \"path\" { now.push(m.text); } }\nfor m in ts_query_old(ROUTES) {\n    if m.capture == \"path\" \u0026\u0026 !now.contains(m.text) {\n        report(m.line, \"endpoint '\" + m.text + \"' removed without a deprecation cycle — see docs/api-lifecycle.md\");\n    }\n}\n```\n\nNo ESLint plugin can express that rule — there is no \"old side\" in a linter. And because argot fits your history, a rule's allowlist can be **your own git log**: `import_attested(\"moment\")` asks *\"has this repo ever used this date library?\"* — no list to hardcode, no list to maintain. Rules run on **changed files only** (adopting one creates zero backlog noise), and their findings are suppressed, configured, and rendered exactly like built-in rules. `argot rules test` is the red/green authoring loop. Full reference + worked examples: [Custom rules](https://argot.tmonier.com/docs/custom-rules/).\n\n## Rules an agent can't game\n\nAn AI agent that can't satisfy a check will reach for the next-cheapest green: mute the rule, downgrade it in a local config, `--rule it=off`, or — for a custom rule — just rewrite the script that caught it. Lock the rule and every one of those doors closes:\n\n```toml\n[rules]\nlayering = { severity = \"error\", locked = true }\ncustom   = { severity = \"error\", locked = true }   # lock every repo-local rule\n```\n\nA **locked** rule (opt-in, from the committed `argot.toml` only):\n\n- **freezes its severity** — `argot.local.toml` and `--rule` overrides are refused;\n- **refuses every suppression surface for its findings** — inline `# argot: ignore`, `[[mute]]`, and `[exclude].paths` don't apply;\n- and — the teeth — **weakening the lock is itself a finding.** `rule-tampered` (group `governance`, pinned `error`, unsuppressable) reads *both sides of the diff being checked* and fires when the change removes a lock, downgrades a locked severity, adds a `[[mute]]` on a locked rule, or edits a locked custom rule's script — with a loud run-level warning your CI surfaces (a PR annotation under `--format github`).\n\nTamper-**evidence**, not tamper-proofing — the same philosophy as the test-integrity rules: an agent *can* touch the alarm, but touching the alarm **is** the alarm. The one quiet way to relax a locked rule is a committed `argot.toml` diff a human reviews. Guide: [Locked rules](https://argot.tmonier.com/docs/configure/#locked-rules--the-agent-cant-turn-off-the-alarm).\n\n## Configure it like any linter\n\nEvery rule (built-in or yours) defaults through `argot.toml [rules]` — `error` / `warn` / `off`, per rule or per group — or per run via `--rule layering=warn`. A `[rules]` entry can also scope a rule to paths (`layering = { include = [\"src/**\"] }`). Excludes are gitignore-style `[exclude].paths`; inline `# argot: ignore-next-line rule=… — reason` and `argot mute \u003chash\u003e` give line-level and durable committed acceptances. Guides: [Configure](https://argot.tmonier.com/docs/configure/) · [The commands](https://argot.tmonier.com/docs/the-commands/).\n\n### argot vs. the tools you already run\n\n|  | Type checker | Linter | Copilot · SAST | argot |\n|---|:---:|:---:|:---:|:---:|\n| Catches invalid code | ✅ | ✅ | ~ | — |\n| Flags what's foreign to *this* repo | ❌ | ❌ | ❌ | ✅ |\n| Flags a function you **already have** | ❌ | ❌ | ❌ | ✅ |\n| Flags code filed in the **wrong place** | ❌ | ❌ | ❌ | ✅ |\n| Flags an import that **breaks your layering** | ❌ | ❌ | ❌ | ✅ |\n| Flags a test **quietly weakened to game a failing suite** | ❌ | ❌ | ❌ | ✅ |\n| Enforces **your team's own conventions**, cross-language, on the diff | ❌ | ~ | ❌ | ✅ |\n| **Locked rules** an agent can't mute, override, or rewrite unnoticed | ❌ | ❌ | ❌ | ✅ |\n| Audits merged history · **attributes findings AI vs human** | ❌ | ❌ | ❌ | ✅ |\n| Learns from *your* history · runs 100% local | ❌ | ❌ | ❌ | ✅ |\n\nargot is additive: it sits *after* your type checker and linter and catches the one thing they can't — code that's valid and lint-clean but unlike anything your team has written. It's the harness around AI output, built from the one thing that can't hallucinate: your repo's own history.\n\n## Benchmarks\n\n**Honest, leak-free numbers**, measured by the real `fit → check` pipeline — foreign fixtures spliced into real host files; false alarms counted on a temporal holdout the model never saw:\n\n- **Foreign catch — 595/605 (98%)** when the foreign symbol is visible in the diff · **false alarms 0.29%** of 22,513 real hunks (worst corpus 1.46%)\n- **Architecture — 244/252 (96.8%)** caught · **0/140** controls flagged · ≤2.7% over-fire on replayed real history\n- **Reinvention — median 89%** at ≤4.5% false fires per hunk · **Misplacement — 85–99% (median 96%)** at ≤1.2%, where the repo has separable architecture\n- **Test-integrity — 144/153 (94.1%)** gaming tactics caught · **0/102** legitimate-refactor controls · 1.12% of 5,268 replayed accepted test-touching commits flagged at gating severity\n\nOne documented limit: **masked foreign** — a foreign symbol whose name collides with one you already use — is statistically invisible to a voice model. We publish that number rather than hide it. And the method, stated plainly: catch rates are measured on fixtures we authored under a [pre-registered rubric](benchmarks/catalogs/RUBRIC.md) frozen before scoring; false alarms are counted on real commits the model never saw. Independent validation is welcome — that's what the reproducible harness is for. Per-language and per-corpus tables, methodology, confidence intervals: [benchmarks page](https://argot.tmonier.com/benchmarks) (CI-fed, can't drift from what ships). Want a language validated? [Open an issue](https://github.com/get-tmonier/argot/issues/new).\n\n## CI\n\n```yaml\n- uses: get-tmonier/argot@main   # non-blocking voice score on every PR\n```\n\n`--format github` prints inline PR annotations; `--format sarif` feeds code scanning; `--format json` is a stable schema. Copy-paste setups incl. pre-commit: [the CI guide](https://argot.tmonier.com/docs/ci/).\n\n## How it works\n\nFive learned detectors, one source of truth — your git history — plus the rules you script yourself. A statistical voice model (two frequency tables + a callee-cluster partition — no neural net) catches foreign imports, callees, and token shapes; a local code-embedding model (jina-code via statically-linked llama.cpp) catches reinvention and misplacement; a module-dependency graph catches layering reversals; a test-inventory diff catches gamed tests. Fit in seconds, check in milliseconds, nothing leaves your machine — and nothing generates: every verdict is a statistic you can replay. Full detail: [How it works](https://argot.tmonier.com/docs/how-it-works/) · [The scoring model](https://argot.tmonier.com/docs/the-scoring-model/) · [Performance](https://argot.tmonier.com/docs/performance/) · experiment log in [docs/research/](docs/research/README.md).\n\n## Contributing\n\nIssues and PRs welcome — start with [CONTRIBUTING.md](CONTRIBUTING.md):\n\n```sh\ngit clone https://github.com/get-tmonier/argot \u0026\u0026 cd argot\njust build       # cargo build --release -p argot → target/release/argot\njust verify      # cargo fmt --check + clippy -D warnings + cargo test\n```\n\n## Acknowledgements\n\nargot is benchmarked against real repositories used as **read-only corpora** — cloned at benchmark time, never redistributed, each under its own license, none affiliated with argot: FastAPI, rich, faker, Saleor, Wagtail, Dagster, Scrapy, Hono, Ink, faker-js, Excalidraw, Outline, Express, Commander.js, ESLint, GitHub CLI, Hugo, ripgrep, bat, Guava, JUnit 5, PowerShell, Jellyfin, redis, curl, RocksDB, fmt, Homebrew, RuboCop, Laravel, and Composer.\n\nBuilt on [tree-sitter](https://tree-sitter.github.io/tree-sitter/) (11 grammars), [libgit2](https://libgit2.org/) via [git2](https://docs.rs/git2/), HuggingFace [tokenizers](https://github.com/huggingface/tokenizers) (UnixCoder BPE), [Rhai](https://rhai.rs/) (scripted rules), [clap](https://docs.rs/clap/), [Serde](https://serde.rs/), and [cargo-dist](https://opensource.axo.dev/cargo-dist/). The semantic layer links [llama.cpp](https://github.com/ggml-org/llama.cpp) (MIT) statically via [`llama-cpp-2`](https://crates.io/crates/llama-cpp-2); its model is [**jina-embeddings-v2-base-code**](https://huggingface.co/jinaai/jina-embeddings-v2-base-code) by [Jina AI](https://jina.ai/) (Apache-2.0), fetched on first use as a `Q4_K_M` GGUF quantization (a derivative work under Apache-2.0 §4) from the [`semantic-model-v1`](https://github.com/get-tmonier/argot/releases/tag/semantic-model-v1) release. argot is not affiliated with, nor endorsed by, Jina AI.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fget-tmonier%2Fargot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fget-tmonier%2Fargot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fget-tmonier%2Fargot/lists"}