{"id":51337614,"url":"https://github.com/lucas77778/fmt-rs","last_synced_at":"2026-07-02T04:04:00.274Z","repository":{"id":366784648,"uuid":"1277854709","full_name":"lucas77778/fmt-rs","owner":"lucas77778","description":"A tiny dependency-free Bash command formatter (Rust) for beautifying commands in the Claude Code permission dialog","archived":false,"fork":false,"pushed_at":"2026-06-23T08:51:41.000Z","size":53,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-23T10:25:44.478Z","etag":null,"topics":["bash","claude-code","cli","formatter","pretty-printer","rust","shell","shfmt"],"latest_commit_sha":null,"homepage":null,"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/lucas77778.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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":null,"dco":null,"cla":null}},"created_at":"2026-06-23T08:47:10.000Z","updated_at":"2026-06-23T08:51:45.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lucas77778/fmt-rs","commit_stats":null,"previous_names":["lucas77778/fmt-rs"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/lucas77778/fmt-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucas77778%2Ffmt-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucas77778%2Ffmt-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucas77778%2Ffmt-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucas77778%2Ffmt-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lucas77778","download_url":"https://codeload.github.com/lucas77778/fmt-rs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucas77778%2Ffmt-rs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35032150,"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-02T02:00:06.368Z","response_time":173,"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":["bash","claude-code","cli","formatter","pretty-printer","rust","shell","shfmt"],"created_at":"2026-07-02T04:03:59.513Z","updated_at":"2026-07-02T04:04:00.261Z","avatar_url":"https://github.com/lucas77778.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# fmt-rs\n\n[![crates.io](https://img.shields.io/crates/v/fmt-rs.svg)](https://crates.io/crates/fmt-rs)\n[![docs.rs](https://img.shields.io/docsrs/fmt-rs)](https://docs.rs/fmt-rs)\n[![license](https://img.shields.io/crates/l/fmt-rs.svg)](LICENSE)\n\nA tiny, dependency-free **Bash command formatter** written in Rust.\n\nIt pretty-prints shell commands so they are easy to read — its purpose is to\nbeautify the commands shown in [Claude Code](https://claude.com/claude-code)'s\npermission dialog *before* you approve running them. Model-generated commands\noften arrive cramped onto one line with messy spacing and stacked `;`\nstatements; fmt-rs turns them into a clean, reviewable layout.\n\n```bash\n$ echo 'cd /repo\u0026\u0026npm ci\u0026\u0026npm run build\u0026\u0026npm test\u0026\u0026npm run deploy' | fmt-rs\n```\n```bash\ncd /repo \u0026\u0026 npm ci \u0026\u0026 npm run build \u0026\u0026 npm test \u0026\u0026 npm run deploy\n```\n\n…and when a chain is too wide for the line, it reflows at the operators:\n\n```bash\n$ echo 'cd /repo\u0026\u0026npm ci\u0026\u0026npm run build\u0026\u0026npm test\u0026\u0026npm run deploy' | FMTRS_WIDTH=40 fmt-rs\n```\n```bash\ncd /repo \u0026\u0026\n  npm ci \u0026\u0026\n  npm run build \u0026\u0026\n  npm test \u0026\u0026\n  npm run deploy\n```\n\n## Why\n\nThe original implementation was a Node hook wrapping\n[`mvdan-sh`](https://github.com/mvdan/sh) (the GopherJS build of `shfmt`):\n~1.7 MiB of JavaScript and a Node cold-start on every command — hundreds of\nmilliseconds. fmt-rs is a single ~600 KiB static binary with **zero runtime\ndependencies** that runs in a few milliseconds.\n\n## Design\n\nThe pipeline mirrors `shfmt`'s stages, but the printer is **width-driven**\nrather than position-driven:\n\n```\ncommand ──lexer──▶ tokens ──parser──▶ AST ──printer──▶ Doc ──pretty──▶ formatted\n```\n\n- **Lexer** (`src/lexer.rs`) — tokenizes words, operators, quotes, and\n  redirections. Quotes and expansions (`'…'`, `\"…\"`, `$(…)`, `${…}`,\n  `` `…` ``, `\u003c(…)`, extglobs) are scanned as opaque, quote-aware regions so an\n  operator *inside* them never splits a word. Here-documents are detected and\n  flushed so the rest of the script is never mistaken for body text.\n- **Parser** (`src/parser.rs`) — recursive descent over `\u0026\u0026`/`||` lists,\n  `|`/`|\u0026` pipelines, redirections, subshells, brace blocks, and\n  `if`/`while`/`until`/`for`. `[[ … ]]` and `(( … ))` are sliced verbatim from\n  the source.\n- **Doc engine** (`src/doc.rs`) — a Wadler/Prettier-style pretty-printing\n  algebra (`text`/`line`/`group`/`nest`/…). A `group` lays out on one line if it\n  fits the target width, otherwise breaks. This is what powers long-chain\n  reflow.\n- **Printer** (`src/printer.rs`) — translates the AST into a `Doc`.\n\n### Safety contract\n\nfmt-rs never changes what a command *means*. It only reflows whitespace and\nline breaks. On anything it is not fully confident about, it returns the\n**original command unchanged** rather than risk displaying something different\nfrom what will run:\n\n- unparseable input, here-documents, function declarations, and `case`/`select`\n  → passed through untouched;\n- **comments are preserved** — a command containing `#` is passed through\n  verbatim (a comment can be the reason you approve a command, so it must never\n  silently vanish);\n- after formatting, the output is re-checked to confirm it carries the exact\n  same words as the input; if not, the original is used;\n- inputs over 8 KiB are passed through as-is.\n\nIt is validated against a corpus of 200+ real-world and adversarial commands\n(quotes hiding `)`/`]]`, nested `$()`, extglobs, fd redirections, …) with zero\ncorruption, plus 70+ unit tests.\n\n## Install\n\nFrom [crates.io](https://crates.io/crates/fmt-rs):\n\n```bash\ncargo install fmt-rs        # installs the `fmt-rs` binary\n```\n\nAs a library:\n\n```bash\ncargo add fmt-rs\n```\n\nRequires Rust 1.88 or newer.\n\n## Build from source\n\n```bash\ncargo build --release\n# binary at target/release/fmt-rs\n```\n\n## Usage\n\nRead a command on stdin, write the formatted command to stdout:\n\n```bash\necho 'x=1;y=2; ls  -la  \u003e/dev/null 2\u003e\u00261' | fmt-rs\n```\n\nThe target width defaults to 80 columns and can be overridden:\n\n```bash\nFMTRS_WIDTH=60 fmt-rs \u003c command.sh\n```\n\n### As a Claude Code hook\n\nIn `--hook` mode, fmt-rs speaks the PreToolUse hook protocol: it reads the\nhook's JSON payload on stdin and emits an `ask` + `updatedInput` response when\nit reformats a Bash command, or `{}` (no-op) otherwise.\n\n```jsonc\n// ~/.claude/settings.json\n{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Bash\",\n        \"hooks\": [\n          { \"type\": \"command\", \"command\": \"/path/to/fmt-rs --hook\", \"timeout\": 10 }\n        ]\n      }\n    ]\n  }\n}\n```\n\nThe permission dialog then shows the formatted command for review. Commands\nthat are already clean fall through normally and can still be auto-allowed.\n\n## Status\n\nThe formatter is complete and in use as a hook. See\n[`PROGRESS.md`](PROGRESS.md) for the full design rationale, scope decisions\n(what is reformatted vs. preserved verbatim vs. passed through), and milestones.\n\n## Acknowledgements\n\nThe AST and formatting behavior are modeled on Daniel Martí's\n[`mvdan/sh`](https://github.com/mvdan/sh). fmt-rs is an independent\nreimplementation focused narrowly on the permission-dialog use case.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucas77778%2Ffmt-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flucas77778%2Ffmt-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucas77778%2Ffmt-rs/lists"}