Skip to content

Add alias command for creating extra CLI names - #32

Closed
rafaeelricco wants to merge 1 commit into
mainfrom
add-alias-management
Closed

Add alias command for creating extra CLI names#32
rafaeelricco wants to merge 1 commit into
mainfrom
add-alias-management

Conversation

@rafaeelricco

Copy link
Copy Markdown
Owner

Why

commit was the only name the CLI answered to, and there was no way to get a shorter one or a one-word shortcut for a subcommand — commit branch is 14 keystrokes for something run dozens of times a day.

This adds commit alias: list, create, and delete extra CLI names, each bound to a subcommand.

What changed

commit alias with no arguments opens an interactive hub (table + Create/Delete/Done, looping) styled like the existing setup/login flows. Scripted use is also supported:

commit alias add cb branch     # `cb` now runs `commit branch`
commit alias list
commit alias remove cb
File Role
src/domain/alias/alias.ts AliasName branded type, registry operations, target vocabulary
src/infra/storage/aliases.ts aliases.json load/save with boundary validation
src/infra/alias/shims.ts Shim write/remove/reconcile, PATH conflict scan
src/infra/alias/path-setup.ts Shell detection, managed rc block
src/cli/alias.ts Clack hub and scripted paths

Wired through src/cli/parser.ts and index.ts; documented in README.md.

Design decisions worth reviewing

Shims, not shell aliases or npm symlinks. Each alias is a small sh script in ~/.commit-tools/bin, with a one-time managed PATH block added to the user's rc file after explicit confirmation. This keeps aliases out of npm's global bin (no sudo, and npm uninstall -g cannot orphan or delete them), makes deletion a single file removal, and keeps aliases working in non-interactive shells and git hooks — which a .zshrc alias line would not.

AliasName is a branded class with a private constructor. The name becomes a filesystem path, so AliasName.parse is the only way to construct one. shimPath/removeShim take AliasName and do not re-validate. Names are also validated when loading aliases.json, so a hand-edited {"name": "../../evil"} is rejected at the boundary rather than reaching resolve().

Registry lives in aliases.json, not config.json. Aliases must work before commit setup has ever run, and setup/model/effort each rewrite the whole Config object.

Shim quoting is POSIX, not JSON. shellQuote uses single-quote escaping, so a $ or ' in the Node or entry-script path cannot break or expand inside the generated shim.

Reviewer notes

  • Shadowing: the alias bin dir is prepended to PATH, so an alias could shadow an existing binary (git). Interactive creation warns with the conflicting path and requires confirmation; scripted creation warns and proceeds.
  • Windows: commit alias exits with a clear message instead of writing an unusable sh shim. Windows support needs .cmd shims and setx; not attempted here.
  • Staleness: shims embed absolute Node and entry-script paths. reconcileShims rewrites every shim on hub entry and on add/remove, so they self-heal after an nvm or npm move. alias list deliberately performs no writes.
  • Scope: this branch contains only the alias feature. An unrelated in-progress workstream (per-model effort capabilities, ~21 files) was left uncommitted in the working tree and is not part of this PR.

Verification

  • pnpm typecheck, pnpm lint, pnpm exec prettier . --check, and pnpm test all pass — 199 tests, 33 of them new.
  • Exercised against the built dist/: shim contents and 0755 mode, shim executes the bound subcommand, and duplicate / unsafe-name / reserved-name / invalid-target / unknown-remove each exit non-zero with a specific message.
  • Two bugs found during that end-to-end pass and fixed here: alias remove <name> on an empty registry exited 0 silently, and a missing target produced the same error as an invalid one.

- Add `commit alias` with an interactive hub for listing, creating, and deleting aliases, plus scriptable `list`, `add <name> <target>`, and `remove <name>` subcommands.
- Bind each alias to a subcommand and install it as a POSIX shim under `~/.commit-tools/bin`, so reinstalling or removing the npm package never touches user aliases.
- Introduce a branded `AliasName` in `src/domain/alias/alias.ts` whose private constructor makes an unsafe filesystem path unrepresentable in `shimPath` and `removeShim`.
- Keep the registry in `aliases.json` separate from `config.json` so aliases work before `commit setup` has run, validating names and rejecting duplicates at the load boundary.
- Offer a one-time managed `PATH` block in `.zshrc`, `.bashrc`, or `config.fish`, written only after an explicit confirmation and idempotent on repeat runs.
- Warn before an alias shadows an existing binary on `PATH`, since the alias bin dir is prepended.
- Quote interpreter and entry-script paths with POSIX single-quote escaping so a `$` or `'` in the path cannot break the generated shim.
- Reject `commit alias` on Windows with a clear message rather than writing an unusable `sh` shim.
- Cover the domain, storage, shim, `PATH` setup, CLI, and parser behavior with unit tests.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a37f85e385

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/alias.ts
addAlias(aliases, alias).either(
(msg) => Future.reject<Error, readonly Alias[]>(new Error(msg)),
(next) =>
writeShim(alias)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reconcile existing shims during direct mutations

When a user changes an nvm prefix or reinstalls the CLI and then runs scripted commit alias add ..., this call rewrites only the new alias; every existing shim continues executing the old absolute Node and entry-script paths embedded by shimSource, so those aliases run stale code or fail once the old prefix is removed. This is a P1 availability regression and contradicts the documented self-healing behavior. Replace the per-alias write with reconcileShims(next), and likewise reconcile the remaining aliases after direct removal.

AGENTS.md reference: AGENTS.md:L5-L12

Useful? React with 👍 / 👎.

const current = await readProfile(profile.file);
if (current.includes(MARKER_START)) return "already-present" as const;

await writeFile(profile.file, current + managedBlock(profile.shell), "utf-8");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Create the Fish profile directory before writing

For a Fish user whose ~/.config/fish directory does not yet exist, accepting the PATH setup reaches this writeFile after readProfile treats the missing file as empty, but it fails with ENOENT because no parent directory is created. The alias and registry have already been written, so the command exits unsuccessfully, the alias remains unavailable on PATH, and retrying alias add reports a duplicate. This is a P1 first-use correctness failure; create dirname(profile.file) recursively before writing the profile.

AGENTS.md reference: AGENTS.md:L9-L12

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant