{"id":50693043,"url":"https://github.com/justinstimatze/weir","last_synced_at":"2026-06-09T05:00:43.106Z","repository":{"id":359391587,"uuid":"1245152289","full_name":"justinstimatze/weir","owner":"justinstimatze","description":"Claude Code wrote `grep` 4,575 times and `rg` zero times across 25,216 Bash calls in one user’s transcripts. weir is the hook that fixes that — tells the model what shell tools you actually have, suggests rewrites for the antipattern cluster, refuses to run the unambiguously-broken ones (`cat FILE | grep`, `which CMD`).","archived":false,"fork":false,"pushed_at":"2026-05-21T17:37:47.000Z","size":151,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-22T01:48:45.412Z","etag":null,"topics":["agent-tools","bash","claude-code","developer-tools","go","hooks","llm-tools","posix","ripgrep","shell"],"latest_commit_sha":null,"homepage":null,"language":"Go","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/justinstimatze.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-21T00:52:55.000Z","updated_at":"2026-05-21T17:37:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/justinstimatze/weir","commit_stats":null,"previous_names":["justinstimatze/weir"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/justinstimatze/weir","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinstimatze%2Fweir","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinstimatze%2Fweir/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinstimatze%2Fweir/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinstimatze%2Fweir/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/justinstimatze","download_url":"https://codeload.github.com/justinstimatze/weir/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinstimatze%2Fweir/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34092262,"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-06-09T02:00:06.510Z","response_time":63,"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":["agent-tools","bash","claude-code","developer-tools","go","hooks","llm-tools","posix","ripgrep","shell"],"created_at":"2026-06-09T05:00:19.257Z","updated_at":"2026-06-09T05:00:43.098Z","avatar_url":"https://github.com/justinstimatze.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# weir\n\n\u003e A barrier across a stream that's also the standard instrument for measuring its flow rate.\n\nweir is a Claude Code addon that closes a specific gap: the model writes shell pipelines fluently, but reaches for a 1995 toolbox.\n\n## The thesis\n\nMining one user's JSONL session transcripts across dozens of project directories (the sanitized aggregate is in [`internal/measure/baseline.json`](internal/measure/baseline.json)):\n\n- **25,216 Bash invocations**\n- **49.7%** contain a top-level pipe — the model writes pipelines constantly\n- **0** of those calls reached for `rg`, `fd`, `bat`, `sd`, `mlr`, `eza`, or any other modern alternative — not \"underused\", *never reached for*\n- A handful of antipatterns recur in volume — counts from the embedded baseline: `grep | head` (810), `ls | grep` (225), UUOC variants `cat FILE | tool` (~120 across head/grep/tail/jq/wc), `grep | wc -l` (99), `awk | awk` (38), `find ... -exec ... \\;` (13). A separate pass-A discovery on the same corpus (`data/mine_extra.py`) added `which CMD` (~179), `find | xargs` without `-print0` (~61), and `ls | wc -l` (~152).\n\nThe model's pipeline *shapes* are fine. What's broken is which binaries it reaches for inside each shape, and a small cluster of stereotyped antipatterns.\n\n## What weir does\n\nThree layers, registered as Claude Code hooks. All fail-open; bypass with `WEIR_SUGGEST_SKIP=1` / `WEIR_SKIP=1`.\n\n### Layer 1 — Capability manifest (SessionStart)\n\nAt the start of every session, weir probes `$PATH` for ~30 modern shell tools, emits a JSON manifest, and injects it into the model's context as `additionalContext`. The model now knows what's reachable on *this* host instead of defaulting to coreutils.\n\nSample output on a freshly-stocked Ubuntu 26.04:\n\n```\n[weir] Modern shell tools on this host:\n- bat (prefer over cat) -\u003e /usr/bin/batcat\n- delta (prefer over diff) -\u003e /usr/bin/delta\n- fd (prefer over find) -\u003e /usr/bin/fdfind\n- jq (additive) -\u003e /usr/bin/jq\n- mlr (prefer over awk) -\u003e /usr/bin/mlr\n- rg (prefer over grep) -\u003e /usr/bin/rg\n- sd (prefer over sed) -\u003e /usr/bin/sd\n...\n```\n\nWhen tools are missing but apt-installable, weir surfaces a `sudo apt install ...` line at install time and in `weir status`.\n\n### Layer 2 — Antipattern suggester (PreToolUse:Bash)\n\nBefore each Bash invocation, weir lints the command against ~12 rules. Two modes:\n\n- **Advisory** (default): the suggestion is injected as context; the command still runs. Used for rules whose rewrites have edge cases.\n- **Block**: weir refuses to run the command and shows the suggested rewrite; the model retries with the better form. Used only for *mechanically-safe* rewrites where the rewrite is lossless and unambiguous.\n\nCurrently block: `uuoc` (`cat FILE | tool` → `tool FILE`) and `which-vs-command-v` (`which X` → `command -v X`). Everything else stays advisory.\n\n```\n$ which python3\n[weir-suggest] blocking — antipattern with a mechanical-safe rewrite:\n- [which-vs-command-v] `which CMD` -\u003e `command -v CMD`. `which` is non-POSIX\n  with inconsistent cross-distro behavior — can't see shell functions/aliases,\n  exit codes vary. `command -v` is POSIX, sees functions/aliases, exits\n  non-zero cleanly when missing. Rewrite the command and retry.\n(To bypass this block for the rest of the session, set WEIR_SUGGEST_SKIP=1 in the env.)\n```\n\n### Layer 3 — Idiom library (SessionStart, additive)\n\nAfter the manifest, weir injects two more blocks:\n\n1. **Per-tool idioms** from a parsed tldr-pages corpus — the first 2 examples per installed modern tool.\n2. **Composition idioms** — 30 hand-curated goal-shaped pipelines (find files modified today and grep them; pretty-print every JSON in a tree; benchmark two command variants). Filtered to entries whose required tools are all present on the host; capped at 10 surfaced per session.\n\nLayer 3 teaches \"*how* to compose\"; layer 1 teaches \"*what's* installed.\"\n\n## Install\n\nRequires Go 1.26 or later.\n\n```sh\ngo install github.com/justinstimatze/weir@latest\nweir install\n```\n\n`weir install` does a non-destructive merge into `~/.claude/settings.json` — it backs up first, only adds its two hook entries (SessionStart + PreToolUse:Bash), and never touches other hooks. Re-running is idempotent; `weir uninstall` is clean.\n\nIf you want it scoped to one project instead of globally, point at a project settings file:\n\n```sh\nCLAUDE_SETTINGS=/path/to/project/.claude/settings.json weir install\n```\n\n## Subcommands\n\n| command | what it does |\n|---|---|\n| `weir suggest` | PreToolUse hook entry point — reads JSON on stdin, emits suggestion JSON |\n| `weir inject` | SessionStart hook entry point — renders manifest + idioms |\n| `weir probe` | emit the capability manifest JSON for the current host |\n| `weir install` | register weir's hooks; idempotent; backs up settings first |\n| `weir uninstall` | remove weir's hooks; leaves unrelated hooks untouched |\n| `weir status` | report which weir hooks are registered + suggest missing-apt tools |\n| `weir measure` | re-mine `~/.claude/projects/**/*.jsonl` and diff against the embedded baseline (modern-vs-classic tool counts, per-rule hit counts) |\n| `weir review CMD` | interactive spot-check — given a bash command, print whether weir would block, advise, or stay silent (use for rule tuning) |\n| `weir build-idioms` | maintainer-only: parse a tldr-pages clone into `internal/idioms/idioms.json` (then rebuild the binary to re-embed) |\n\n## How weir avoids being annoying\n\n- **Fail-open everywhere.** Any error in the hook → silent exit 0, command runs. weir cannot break your session.\n- **Block mode is conservative.** Only on rewrites where the substitution is mechanically lossless. Edge-case rules stay advisory.\n- **Bypass is one env var.** `WEIR_SUGGEST_SKIP=1` disables suggest output for the session; `WEIR_SKIP=1` disables the SessionStart inject.\n- **Suggestions are concise.** Rule fix-text is one paragraph max. SessionStart injection is bounded: the per-tool tldr idiom section caps at 2000 chars (~500 tokens), the cross-tool composition section caps at 1500 chars (~375 tokens), and the manifest itself scales with installed-tool count. Worst-case total on a richly-stocked host is ~1000 tokens.\n- **Settings.json edits are non-destructive + reversible.** Every write backs up first to `\u003cpath\u003e.weir-bak-\u003ctimestamp\u003e`; uninstall removes only weir-owned entries.\n\n## Architecture\n\nSingle self-contained binary (~2.6 MB, stripped). All runtime data (idiom corpus, baseline snapshot for `weir measure`) is embedded via `//go:embed` — no source-tree dependency at runtime.\n\n```\nmain.go                        # subcommand dispatch + --version\ninternal/\n  probe/                       # PATH discovery + apt-pkg mapping (+ symlink dedup)\n  suggest/                     # rule table + match engine + selftest + review\n  inject/                      # SessionStart prose renderer (manifest + idioms + composition)\n  idioms/                      # per-tool (tldr) + composition idioms; build-idioms parser\n  install/                     # non-destructive settings.json merge + status + uninstall\n  measure/                     # corpus streamer + baseline diff (embedded baseline.json)\n  guard/                       # panic-recover wrapper for all hook entry points\ndata/\n  baseline_2026-05-20.json     # sanitized aggregate baseline (the empirical evidence)\n  mine_pipes.py                # the original baseline-extraction script (Python, run rarely)\n  mine_extra.py                # pass-A: hunt named antipatterns\n  mine_discover.py             # pass-B: frequency-rank for unknowns\n  eval/                        # synthetic eval harness (uv venv, prompts.jsonl, run/grade/report)\nscripts/\n  smoke.sh                     # install/uninstall cycle test (CI runs on every push)\n  sanitize-baseline.py         # PII-strip raw mining output for the public artifact\n```\n\n## Prior art\n\nThe compiler/dataflow lineage (PaSh, PaSh-JIT, Koala, Smoosh, POSH),\nsurface-tooling lineage (ShellCheck, Ultimate Plumber), agent-side lineage\n(Warp, Butterfish, NL2SH, Terminal-Bench), and documentation lineage\n(tldr-pages, explainshell). Full citations + how each relates to weir\nin [INFLUENCES.md](INFLUENCES.md).\n\n## Roadmap\n\nLayer 4 (streaming/blocking classifier), eval extensions (block-and-retry\nsimulation, cross-host probe), rule-table extensions, and distribution\nwork. See [ROADMAP.md](ROADMAP.md). Historical dead ends in\n[WHAT_DIDNT_WORK.md](WHAT_DIDNT_WORK.md).\n\n## License\n\nMIT. See `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjustinstimatze%2Fweir","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjustinstimatze%2Fweir","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjustinstimatze%2Fweir/lists"}