{"_id":"@caseywebb/elmq","_rev":"3-d8de2e139c37f8c286bb7f673efd66fd","name":"@caseywebb/elmq","dist-tags":{"latest":"0.8.0"},"versions":{"0.0.0":{"name":"@caseywebb/elmq","version":"0.0.0","license":"MIT","_id":"@caseywebb/elmq@0.0.0","maintainers":[{"name":"caseywebb","email":"notcaseywebb@gmail.com"}],"homepage":"https://github.com/caseyWebb/elmq#readme","bugs":{"url":"https://github.com/caseyWebb/elmq/issues"},"bin":{"elmq":"run.js"},"dist":{"shasum":"17180f7997bbbbab0b5ea13fbd6af2e195272fe4","tarball":"https://registry.npmjs.org/@caseywebb/elmq/-/elmq-0.0.0.tgz","fileCount":2,"integrity":"sha512-KwrTQt6cqi5xmGtixj+Hc7yo9bU9Qswy9LxDfKv1IpzILTU7xCwf+14ocjXpcsCznTKxLrlYWCdNpDKGH8LHvA==","signatures":[{"sig":"MEYCIQCYn9IdL0IFHaVi5P/U1zR252GhyA1ZSDVNGfyH4+k1TgIhAPTZDzakUDfQnLeOrsLYE/9uBDFw9obBhLYM65sxg0Gt","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1421},"gitHead":"66bd1ff2596a8b7feb16516c79a7ef107d62a5a0","_npmUser":{"name":"caseywebb","email":"notcaseywebb@gmail.com"},"repository":{"url":"git+https://github.com/caseyWebb/elmq.git","type":"git"},"_npmVersion":"11.12.1","description":"Query and edit Elm files — like jq for Elm","directories":{},"_nodeVersion":"25.9.0","_hasShrinkwrap":false,"optionalDependencies":{"@caseywebb/elmq-linux-x64":"0.0.0","@caseywebb/elmq-darwin-x64":"0.0.0","@caseywebb/elmq-linux-arm64":"0.0.0","@caseywebb/elmq-darwin-arm64":"0.0.0"},"_npmOperationalInternal":{"tmp":"tmp/elmq_0.0.0_1775849724608_0.11871069845670035","host":"s3://npm-registry-packages-npm-production"}},"0.7.4":{"name":"@caseywebb/elmq","version":"0.7.4","license":"MIT","_id":"@caseywebb/elmq@0.7.4","maintainers":[{"name":"caseywebb","email":"notcaseywebb@gmail.com"}],"homepage":"https://github.com/caseyWebb/elmq#readme","bugs":{"url":"https://github.com/caseyWebb/elmq/issues"},"bin":{"elmq":"run.js"},"dist":{"shasum":"d40b5ce19473d2462392aa591d7f7aca5c92c46e","tarball":"https://registry.npmjs.org/@caseywebb/elmq/-/elmq-0.7.4.tgz","fileCount":2,"integrity":"sha512-bfjR40kG4RQCyezlLHn35AXmF7qIfQ7BJySi2q6WMaNYNhiku3KO99jIV/eL9I1B8aPNKrifS5IQr2Is/nPZ+g==","signatures":[{"sig":"MEYCIQDA0uVycTWUBSC3i4D+63EO5uzVvtz0E0DclHj6Y7hbywIhANGridKrecPRW2VWcFx9DlbADWR77q1v6cqP2BhORMYF","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@caseywebb%2felmq@0.7.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1421},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:72944bfd-d350-49df-9358-cb8c4fbd73dc"}},"repository":{"url":"git+https://github.com/caseyWebb/elmq.git","type":"git"},"_npmVersion":"11.11.0","description":"Query and edit Elm files — like jq for Elm","directories":{},"_nodeVersion":"24.14.1","_hasShrinkwrap":false,"optionalDependencies":{"@caseywebb/elmq-linux-x64":"0.7.4","@caseywebb/elmq-darwin-x64":"0.7.4","@caseywebb/elmq-linux-arm64":"0.7.4","@caseywebb/elmq-darwin-arm64":"0.7.4"},"_npmOperationalInternal":{"tmp":"tmp/elmq_0.7.4_1776100172781_0.5869464898679098","host":"s3://npm-registry-packages-npm-production"}},"0.8.0":{"name":"@caseywebb/elmq","version":"0.8.0","description":"Query and edit Elm files — like jq for Elm","license":"MIT","repository":{"type":"git","url":"git+https://github.com/caseyWebb/elmq.git"},"bin":{"elmq":"run.js"},"optionalDependencies":{"@caseywebb/elmq-darwin-arm64":"0.8.0","@caseywebb/elmq-darwin-x64":"0.8.0","@caseywebb/elmq-linux-arm64":"0.8.0","@caseywebb/elmq-linux-x64":"0.8.0"},"_id":"@caseywebb/elmq@0.8.0","bugs":{"url":"https://github.com/caseyWebb/elmq/issues"},"homepage":"https://github.com/caseyWebb/elmq#readme","_nodeVersion":"24.14.1","_npmVersion":"11.11.0","dist":{"integrity":"sha512-fWdWtDdH7IjwNF9QeXgPEKzEKf1SY2oFxaia5fhgv30jZTTLrOZpgSa7ab4yyrAvnlA3atKKoNuYSRmRGA2qBQ==","shasum":"407be66a729594247e6e3358c59a639f42a4f3ca","tarball":"https://registry.npmjs.org/@caseywebb/elmq/-/elmq-0.8.0.tgz","fileCount":4,"unpackedSize":17315,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@caseywebb%2felmq@0.8.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIB84v2uBpqjps9piyY+W24QoFTv02mliCgv1JYEWo0jHAiBVppGov/y2hkhrmI+KDqrrVUIU75sg0btEBSCsmU6r3A=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:72944bfd-d350-49df-9358-cb8c4fbd73dc"}},"directories":{},"maintainers":[{"name":"caseywebb","email":"notcaseywebb@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/elmq_0.8.0_1776189338288_0.19169377801550413"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-10T19:35:24.542Z","modified":"2026-04-14T17:55:38.658Z","0.0.0":"2026-04-10T19:35:24.729Z","0.7.4":"2026-04-13T17:09:32.961Z","0.8.0":"2026-04-14T17:55:38.406Z"},"bugs":{"url":"https://github.com/caseyWebb/elmq/issues"},"license":"MIT","homepage":"https://github.com/caseyWebb/elmq#readme","repository":{"type":"git","url":"git+https://github.com/caseyWebb/elmq.git"},"description":"Query and edit Elm files — like jq for Elm","maintainers":[{"name":"caseywebb","email":"notcaseywebb@gmail.com"}],"readme":"---\nupdate-when: CLI commands, output format, or installation steps change\n---\n\n# elmq\n\nA CLI for querying and editing Elm files — like jq for Elm.\n\nDesigned as a next-gen LSP for agents and scripts, not editors. Optimized for token efficiency and structured output.\n\n> **Status:** Active development. Supports reading and writing Elm declarations, imports, and module lines, plus project-wide operations (rename, move, extract, add/remove variant). See [ROADMAP.md](ROADMAP.md) for what's planned.\n\n> [!TIP]\n> Curious why elmq exists and what it's trying to prove? Read [HYPOTHESIS.md](HYPOTHESIS.md) for the claim, the experimental design, and the threats to validity.\n\n## Install\n\n### Homebrew\n\n```sh\nbrew install caseyWebb/tap/elmq\n```\n\n### npm\n\n```sh\nnpm install -g @caseywebb/elmq\n```\n\nOr run without installing:\n\n```sh\nnpx @caseywebb/elmq <command>\n```\n\n### From source\n\nRequires [Rust](https://rustup.rs/):\n\n```sh\ncargo install --path .\n```\n\n## Usage\n\n### Write-safety precondition\n\nEvery elmq command that mutates a `.elm` file — `set decl`, `patch`, `rm decl`, `add/rm import`, `expose`/`unexpose`, `mv`, `rename decl`, `move-decl`, `add/rm variant`, `set let`, `set case`, `rm let`, `rm case`, `rm arg`, `rename let`, `rename arg`, `add arg` — refuses to operate on a file that has pre-existing tree-sitter parse errors, and refuses to produce an output buffer that would not parse. If either check fires, elmq exits non-zero with a `refusing to edit …` or `rejected '<op>' write to …` message naming the file and the first error location, and the file on disk is left unchanged. Fix the file by hand (or with your editor) and retry. All write commands confirm success with `ok` output (sole exception: `rm variant` emits an actionable `references_not_rewritten` advisory section when the removed constructor appears in non-cleanly-rewritable positions). Read commands (`list`, `get`, `grep`, `refs`, `variant cases`) keep their existing tolerant behavior — they print a warning and continue so you can still inspect broken files.\n\n### File summary\n\n```sh\nelmq list src/Main.elm\n```\n\n```\nmodule Main exposing (Model, Msg(..), update, view)  (38 lines)\n\nimports:\n  Html exposing (Html, div, text)\n  Html.Attributes as Attr\n\ntype aliases:\n  Model  L4-8\n\ntypes:\n  Msg  L11-15\n\nfunctions:\n  update  Msg -> Model -> Model  L18-28\n  view    Model -> Html Msg      L31-34\n  helper                         L37-38\n```\n\n### With doc comments\n\n```sh\nelmq list src/Main.elm --docs\n```\n\n```\ntype aliases:\n  Model  L4-8\n    The model for our app\n\ntypes:\n  Msg  L11-15\n    Messages for the update function\n...\n```\n\n### Extract a declaration\n\n```sh\nelmq get src/Main.elm update\n```\n\n```\nupdate : Msg -> Model -> Model\nupdate msg model =\n    case msg of\n        Increment ->\n            { model | count = model.count + 1 }\n\n        Decrement ->\n            { model | count = model.count - 1 }\n\n        Reset ->\n            { model | count = 0 }\n```\n\nIncludes doc comments and type annotations when present. Returns non-zero exit code if the declaration is not found.\n\nRead across multiple files in one call with `-f`:\n\n```sh\nelmq get -f src/Page/Home.elm update view -f src/Update.elm main\n```\n\nEach `-f` group is a file followed by one or more names. Output is framed as `## Module.decl` blocks (falls back to `## file:decl` without `elm.json`).\n\n### Upsert a declaration\n\n```sh\necho 'helper x =\n    x + 42' | elmq set decl src/Main.elm\n```\n\nReads a full declaration from stdin, parses the name, and replaces the existing declaration (or appends if new). Use `--content` to pass the declaration inline instead of stdin:\n\n```sh\nelmq set decl src/Main.elm --content 'helper x = x + 1'\n```\n\nA name mismatch between the declaration source and the target is an error; use `rename decl` to rename instead of upsert.\n\n### Patch a declaration\n\n```sh\nelmq patch src/Main.elm update --old \"model.count + 1\" --new \"model.count + 2\"\n```\n\nSurgical find-and-replace scoped to a single declaration. The `--old` string must match exactly once.\n\n### Remove a declaration\n\n```sh\nelmq rm decl src/Main.elm helper\n```\n\nRemoves the declaration, its type annotation, and doc comment. Cleans up excess blank lines.\n\n### Manage imports\n\n```sh\nelmq add import src/Main.elm \"Browser exposing (element)\"\nelmq rm import src/Main.elm Html\n```\n\n`add import` inserts in alphabetical order or replaces an existing import with the same module name.\n\n### Manage exposing list\n\n```sh\nelmq expose src/Main.elm update\nelmq expose src/Main.elm \"Msg(..)\"\nelmq unexpose src/Main.elm helper\n```\n\nGranularly add or remove items from the module's exposing list. If the module has `exposing (..)`, `unexpose` auto-expands to an explicit list then removes the target. `expose` is a no-op when `exposing (..)`. Neither command ever produces `exposing (..)`.\n\n### Rename/move a module\n\n```sh\nelmq mv src/Foo/Bar.elm src/Foo/Baz.elm\n```\n\n```\nrenamed src/Foo/Bar.elm -> src/Foo/Baz.elm\nupdated src/Main.elm\nupdated src/Page/Home.elm\n```\n\nRenames the file, updates the module declaration, and rewrites all imports and qualified references (`Foo.Bar.something` -> `Foo.Baz.something`) across the project. Requires `elm.json` in a parent directory. Use `--dry-run` to preview changes without writing.\n\n### Rename a declaration\n\n```sh\nelmq rename decl src/Main.elm helper newHelper\n```\n\n```\nrenamed helper -> newHelper\nupdated src/Main.elm\nupdated src/Page/Home.elm\n```\n\nRenames a declaration (function, type, type alias, port, or variant) in the defining file and updates all references across the project — including qualified (`Module.helper`), aliased (`M.helper`), and exposed references. Requires `elm.json` in a parent directory. Use `--dry-run` to preview changes without writing.\n\n### Find references\n\n```sh\nelmq refs src/Lib/Utils.elm\n```\n\n```\nsrc/Main.elm:3\nsrc/Page/Home.elm:5\nsrc/Page/Settings.elm:3\n```\n\nFind all files that import a module. Add a declaration name to find specific usage sites:\n\n```sh\nelmq refs src/Lib/Utils.elm helper\n```\n\n```\nsrc/Page/Home.elm:3: import Lib.Utils exposing (helper)\nsrc/Page/Settings.elm:5: LU.helper config\nsrc/Main.elm:7: Lib.Utils.helper model\n```\n\nResolves fully-qualified references (`Lib.Utils.helper`), aliased references (`LU.helper`), and explicitly-exposed names. Requires `elm.json` in a parent directory.\n\n### Move declarations between modules\n\n```sh\nelmq move-decl src/Page/Home.elm --to src/Shared/Layout.elm viewHeader viewFooter\n```\n\n```\nmoved viewHeader\nmoved viewFooter\nauto-included renderNav\nupdated src/Page/Home.elm\nupdated src/Shared/Layout.elm\nupdated src/Main.elm\n```\n\nMoves declarations from one module to another, rewriting the declaration bodies to match the target file's import conventions (aliases, exposed names). Automatically includes unexposed helpers used only by the moved declarations. Creates the target file if it doesn't exist. Auto-upgrades the target to a `port module` when moving ports.\n\nUse `--copy-shared-helpers` to duplicate (not move) helpers that are used by both moved and non-moved declarations. Use `--dry-run` to preview changes.\n\n### Add/remove type variant constructors\n\n```sh\nelmq add variant src/Types.elm --type Msg \"SetName String\"\n```\n\n```\nok\n  src/Update.elm:22  update      — inserted branch\n  src/View.elm:15    label       — inserted branch\n  src/Main.elm:31    update      — skipped (wildcard branch covers new variant)\n```\n\nAppends a constructor to a custom type and inserts `Debug.todo` branches in all matching case expressions project-wide. Case expressions with wildcard (`_`) branches are skipped with an info message.\n\nTo fill branch bodies in the same call, first survey the sites with `elmq variant cases`, then pass their keys to `--fill`:\n\n```sh\nelmq variant cases src/Types.elm --type Msg\n```\n\n```\n## case sites for type Types.Msg (2 files, 2 functions)\n\n### src/Update.elm\n\n#### update (key: update, line 12)\nupdate : Msg -> Model -> Model\nupdate msg model =\n    case msg of\n        Increment -> ...\n        Decrement -> ...\n\n### src/View.elm\n\n#### label (key: label, line 8)\n...\n```\n\n```sh\nelmq add variant src/Types.elm --type Msg \"Reset\" \\\n  --fill 'update=Reset -> { model | count = 0 }' \\\n  --fill 'label=Reset -> \"reset\"'\n```\n\n`variant cases` is read-only and returns every case expression project-wide that matches the target type, with its enclosing function body (including type annotation) and a stable site key. Pass those keys to `--fill <key>=<branch_text>` (repeatable) on `add variant` to replace the default `Debug.todo \"<Variant>\"` stub with real branch bodies in the same call. Unmatched fill keys fail validation before any file is touched; unfilled sites fall back to `Debug.todo` stubs (graceful degradation).\n\nWhen one function contains multiple case expressions on the same type, or two files both define a function with the same name, `variant cases` disambiguates with `function#N` or `file:function` keys. Passing an ambiguous bare key to `--fill` errors with the valid alternatives listed.\n\n```sh\nelmq rm variant src/Types.elm --type Msg Decrement\n```\n\n```\nok\n  src/Update.elm:22  update  — removed branch\n  src/View.elm:15    label   — removed branch\n```\n\nRemoves a constructor and its matching branches from all case expressions — including nested patterns like `Just Decrement -> ...`. Errors if removing the last variant (use `elmq rm decl` instead). Use `--dry-run` to preview changes.\n\nWhen the constructor is also used outside case branches (expression position, refutable patterns in function/lambda/let arguments), `rm variant` emits a `references_not_rewritten` advisory section listing every such site with its file, line, enclosing declaration, and classification so the agent can fix them by hand before running `elm make`:\n\n```\nok\n  src/Update.elm:47  update  — removed branch\n\nreferences_not_rewritten (2):\n  src/Init.elm:15  init      expression-position\n      init = ( Model 0, Cmd.map Wrap (Increment 1) )\n  src/Debug.elm:8  debugMsg  expression-position\n      debugMsg m = m == Increment 0\n  run `elm make` to confirm and fix these before continuing\n```\n\nThe advisory is the *same data* surfaced by `elmq refs` (see below), included in the rm output so the removal loop is one or two `elmq` touches at most — don't chain `elmq refs` into the rm flow.\n\n### Audit constructor references\n\nThe regular `elmq refs` command auto-routes on what the name is. For top-level declarations it emits a flat list of call sites; for a constructor of a custom type declared in the target file it emits a **classified** report:\n\n```sh\nelmq refs src/Types.elm Increment\n```\n\n```\nMsg.Increment — 3 references (1 clean, 2 blocking)\nsrc/Update.elm\n    47  update       case-branch\n        Increment ->\nsrc/Init.elm\n    15  init         expression-position\n        init = ( Model 0, Cmd.map Wrap (Increment 1) )\nsrc/Debug.elm\n     8  debugMsg     expression-position\n        debugMsg m = m == Increment 0\n```\n\nWalks the entire project and classifies every reference by its syntactic role: `case-branch` and `case-wildcard-covered` are \"clean\" (what `rm variant` would rewrite); `function-arg-pattern`, `lambda-arg-pattern`, `let-binding-pattern`, and `expression-position` are \"blocking\" (what `rm variant` would leave for the agent). Use it to audit whether a constructor is still needed, to plan a rename, or to understand the blast radius of a type change — independent of any removal flow. `--format json` emits `total_sites`, `total_clean`, `total_blocking`, and a flat `sites` array with `file`, `line`, `column`, `declaration`, `kind`, and `snippet` per entry.\n\nDecl and constructor names can be mixed in a single `elmq refs` call; each is framed under its own `## <arg>` header.\n\n### Search Elm sources\n\n```sh\nelmq grep \"Http\\.get\"\n```\n\n```\nsrc/Api.elm:42:fetchUser:    Http.get { url = userUrl, expect = Http.expectJson GotUser decoder }\nsrc/Page/Home.elm:88:init:    Http.get { url = feedUrl, expect = Http.expectJson GotFeed feedDecoder }\n```\n\nRegex search over Elm files (Rust `regex` dialect, same as ripgrep) that annotates each hit with its **enclosing top-level declaration** — the discovery entry point that feeds into `elmq get`. Use `-F` for literal matching and `-i` for case-insensitive. Matches inside `--` / `{- -}` comments and string literals are filtered by default; pass `--include-comments` or `--include-strings` to opt back in independently.\n\nProject discovery walks up for `elm.json` and honors its `source-directories`; if no `elm.json` is found, falls back to recursively walking the CWD. Both paths honor `.gitignore`. Exit codes match ripgrep: `0` on matches, `1` on none, `2` on error.\n\nTwo additional flags enable one-call definition lookup and source retrieval:\n\n- `--definitions` — only emit matches at the declaration name site (filters out call sites)\n- `--source` — emit full declaration source for each matched decl, deduped by `(file, decl)`. Output is framed `## Module.decl` (single result stays bare).\n\nCombine them for definition lookup: `elmq grep --definitions --source 'update'` returns the full source of the `update` declaration without any call-site noise.\n\nPipe into `elmq get` for a find-then-retrieve workflow:\n\n```sh\nelmq grep --format json \"Http\\.get\" \\\n  | jq -r 'select(.decl) | \"\\(.file) \\(.decl)\"' \\\n  | sort -u \\\n  | while read file decl; do elmq get \"$file\" \"$decl\"; done\n```\n\nMatches that land outside any top-level declaration (imports, module header) report `decl: null` in JSON and an empty decl slot in compact output.\n\n### Agent integration guide\n\n```sh\nelmq guide\n```\n\nPrints the built-in agent integration guide to stdout. This is the guide that tells LLM coding agents how to use elmq effectively — when to prefer `elmq get` over `Read`, how to chain edits, etc. The Claude Code plugin (`/plugin install elmq@caseyWebb`) uses this automatically via a SessionStart hook.\n\n### JSON output\n\n```sh\nelmq list src/Main.elm --format json\n```\n\n```json\n{\n  \"module_line\": \"module Main exposing (Model, Msg(..), update, view)\",\n  \"imports\": [\"Html exposing (Html, div, text)\"],\n  \"declarations\": [\n    {\n      \"name\": \"update\",\n      \"kind\": \"function\",\n      \"type_annotation\": \"Msg -> Model -> Model\",\n      \"start_line\": 18,\n      \"end_line\": 28\n    }\n  ]\n}\n```\n\n## Using elmq with LLM coding agents\n\nelmq is designed to be used from any coding agent that can shell out to a CLI (Claude Code, Cursor, Aider, Codex, etc.). The built-in agent guide (`elmq guide`) tells agents how to use elmq effectively.\n\n**Claude Code**: Install the plugin with `/plugin install elmq@caseyWebb`. It automatically injects the guide into sessions working in Elm projects.\n\n**Other agents**: Pipe `elmq guide` into your agent's system prompt or project instructions.\n\n## Roadmap\n\nSee [ROADMAP.md](ROADMAP.md) for the phased development plan.\n","readmeFilename":"README.md"}