Conversation
`Optimus.parse!/3` returns a bare %Optimus.ParseResult{} (not the usual
{subcommand_path, parse_result} tuple) when no subcommand token is given
at all - a bare `lc` invocation crashed on that with a MatchError. Also
default a bare invocation to `issue list`, matching lc.sh's own behavior,
and fix `--help` breaking when flags come before it (e.g. `lc issue
update --close --help`, real usage from bin/lclose) by only treating the
leading run of non-flag tokens as a help path.
Users never type `linear_cli` - they type `lc`, or one of the bash wrapper scripts (lcreate, lcls, etc). Naming the escript/release binary itself `lc` means those wrappers work as-is, with no separate aliasing mechanism needed.
Ports the bash wrapper scripts verbatim from vendor/ruby-linear-cli/exe/scripts/ (lcreate, lcls, lclose, lcomment, lproj) - they already just `exec lc ...`, so they work unmodified now that the binary itself is named `lc`. The container image installs both the binary and the wrappers into /usr/local/bin/ together so the wrappers' `exec lc ...` actually resolves via PATH (the old bare `/linear_cli` wasn't on PATH at all), and adds bash (Alpine's default /bin/sh can't run bin/lclose's `[[ =~ ]]` syntax).
The Burrito-wrapped `lc` binary never actually invoked CLI.main - it boots via `-s elixir start_cli`, which only recognizes Elixir's own CLI flags (--help/--version) and otherwise tries to run its first argument as a script file. `lc issue list` failed with "No file named issue" instead of reaching the CLI at all; --help/--version only appeared to work by accident, via Elixir's own flag parsing. Per Burrito's README, the fix has to live in Application.start/2: detect the standalone-Burrito case (Burrito.Util.running_standalone?/0, which checks the __BURRITO env var the Zig wrapper sets) and call CLI.main/2 directly there, then halt. Verified against both the native macOS binary and the Linux container image - real subcommands now reach the actual CLI logic instead of dying at boot.
Floating on elixir = "latest" drifted local Erlang/Elixir ahead of what Burrito's precompiled-ERTS catalog actually has cataloged - `mix release` started 404ing locally (fetching OTP 29.0.5) while CI, pinned via erlef/setup-beam@v1, stayed fine. Pin both erlang and elixir to match CI exactly.
Builds a native Burrito release for the current machine (macOS aarch64 or Linux x86_64) via mise, and installs lc plus the bin/ wrapper scripts onto a directory already on $PATH rather than assuming one. Writes a manifest of exactly what it installed and where, so uninstall.sh can remove precisely those files even if $PATH changes in between - a fallback for machines without Homebrew (rubyists/homebrew-tap to come once a real release exists to pin against).
There was a problem hiding this comment.
Pull request overview
This PR updates the project’s distribution/entrypoint story to consistently ship and run the CLI as lc (including Burrito releases, container image, and wrapper scripts), and fixes CLI boot/help edge cases encountered along that path.
Changes:
- Renamed the escript/release binary from
linear_clitolcand updated supporting build/CI artifacts accordingly. - Fixed the Burrito standalone entrypoint so the real CLI logic runs, and improved CLI parsing behavior for no-subcommand and
--helpscenarios. - Added wrapper scripts plus
install.sh/uninstall.sh, and wired wrappers +bashinto the container image.
Reviewed changes
Copilot reviewed 10 out of 15 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| uninstall.sh | New uninstaller that removes files listed in an install manifest. |
| oci/Containerfile | Copies lc + wrapper scripts into /usr/local/bin and installs bash in the Alpine image. |
| mise.toml | Pins Erlang/Elixir tool versions to match CI for repeatable local Burrito builds. |
| install.sh | New local install path: builds Burrito release and installs lc + wrapper scripts, writing a manifest for uninstall. |
| bin/lproj | Wrapper script for lc project …. |
| bin/lcreate | Wrapper script for lc issue create …. |
| bin/lcomment | Wrapper script for lc issue update --comment - …. |
| bin/lcls | Wrapper script for lc issue list …. |
| bin/lclose | Wrapper script for lc issue update --close … with custom --help behavior. |
| app/test/linear_cli/cli_test.exs | Updates test expectations/rescue to match the new parse-shape handling. |
| app/mix.exs | Renames escript/release output to lc. |
| app/lib/linear_cli/cli.ex | Normalizes Optimus parse return shapes; adds default lc behavior; improves --help normalization; renames CLI name to lc. |
| app/lib/linear_cli/application.ex | Routes Burrito standalone startup into LinearCli.CLI.main/2 so args are handled. |
| app/.gitignore | Updates ignored escript name to /lc. |
| .github/workflows/burrito-release.yaml | Updates release workflow to build mix release lc. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+37
to
+43
| if [ "$failed" -ne 0 ] | ||
| then | ||
| printf '%d file(s) removed, %d failed\n' "$removed" "$failed" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| printf '%d file(s) removed\n' "$removed" |
Comment on lines
+88
to
+92
| {before, [_ | _]} -> | ||
| case Enum.take_while(before, &(not String.starts_with?(&1, "-"))) do | ||
| [] -> argv | ||
| path -> ["help" | path] | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #5.
linear_clitolc, so the existing bash wrapper scripts (lcreate,lcls,lclose,lcomment,lproj, ported verbatim fromvendor/ruby-linear-cli/exe/scripts/) work as-is with no separate aliasing mechanism.lcbinary never actually invokedLinearCli.CLI.main/2- it boots via-s elixir start_cli, which only recognizes Elixir's own--help/--versionflags and otherwise tries to run its first argument as a script file (lc issue listfailed with "No file named issue"). WiredCLI.main/2intoApplication.start/2for the standalone-Burrito case, verified against both the native macOS binary and the Linux container image.lcinvocation crashing with aMatchError(Optimus returns a different shape when no subcommand matches at all), and--helpbreaking when flags precede it (e.g.lc issue update --close --help, real usage frombin/lclose)./usr/local/bin/, soexec lc ...resolves via PATH) and addsbashto the Alpine image (bin/lcloseneeds it).mise.tomlto the exact OTP/Elixir version CI uses - Burrito's precompiled-ERTS catalog lags "latest", so floating broke localmix releasebuilds while CI stayed fine.install.sh/uninstall.shas a local, brew-less install path (builds a native release viamise, installs onto a directory already on$PATH, tracks exactly what it installed via a manifest so uninstall is precise). A Homebrew tap is planned as a follow-up once a real release exists to pin asha256against.Test plan
mix test- 175 passedmix format --check-formatted- cleanlc --version,lc whoami --help, andlc issue list(fails cleanly on missing API key, not a boot crash) all reach real CLI logic inside the containerlcvia PATH inside the containerLINEAR_CLI_DAEMON=true, no args) still boots and stays alive, unaffected by the fixinstall.sh/uninstall.shend-to-end in an isolated scratch dir - install, manifest, uninstall all verified🤖 Generated with Claude Code