diff --git a/README.md b/README.md index c115ee5..abdcd39 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Check what landed, and wire up the pit aliases: ```sh cli-tools list # * runs from here, ! is shadowed by another copy -cli-tools aliases --install # /blog /free /merge /prs /whois +cli-tools aliases --install # /aff /blog /free /merge /prs /speak /web /whois cli-tools config # API keys: what is set, and where it came from cli-tools update # git pull, reinstall, relink cli-tools autoupdate --install # …or have a timer do that daily @@ -606,9 +606,27 @@ moshcode install cli-tools # then /cli-tools list, /cli-tools update ## Aliases Pit aliases live in `~/.moshcode/aliases.json`. `cli-tools aliases --install` -writes a thin default set (`/blog`, `/free`, `/merge`, `/prs`, `/whois`), -merging rather than replacing — an alias you bound yourself is kept and the -collision is reported. `cli-tools aliases` prints them without writing anything. +writes a thin default set, merging rather than replacing — an alias you bound +yourself is kept and the collision is reported. `cli-tools aliases` prints them +without writing anything. + +| Alias | Expands to | +| --- | --- | +| `/aff` | `affiliate` | +| `/blog` | `blog-post` | +| `/free` | `domainfree` | +| `/merge` | `gh-prs-merge --apply` | +| `/prs` | `gh-prs` | +| `/speak` | `tts` | +| `/web` | `ask-web` | +| `/whois` | `domainjson` | + +Three of those are named around a collision rather than for elegance. `/ask` and +`/say` are the words you would reach for, and both already resolve to something +else on a normal box; because a pit alias beats `PATH`, binding them would +shadow those programs *from inside the pit only*, which is about the most +confusing failure available. `/tts` would be worse — it would shadow our own +command. Hence `/web`, `/speak` and `/aff`. To manage them by hand: diff --git a/bin/cli-tools.ts b/bin/cli-tools.ts index 39b7b58..f0ca1c5 100755 --- a/bin/cli-tools.ts +++ b/bin/cli-tools.ts @@ -73,6 +73,7 @@ Commands: config API keys: what is set, where it came from, and how to change it "config pull" imports them from the logicsrc team vault where Print the checkout this command is running from + help This usage Keys (config set ): openai OPENAI_API_KEY generate-names @@ -545,8 +546,11 @@ export async function run(argv: readonly string[]): Promise { // Anything that is not one of ours is one of the commands: pass it straight // through, arguments and streams untouched, so `cli-tools gh-prs --orgs x` // behaves exactly as `gh-prs --orgs x` does. + // `help` is here because it is what people type. Without it the word fell + // through to the passthrough below, which reported "unknown command: help" + // and exited 1 — before printing the usage that answers the question. const known = new Set([ - 'list', 'update', 'autoupdate', 'link', 'unlink', 'aliases', 'config', 'where', + 'help', 'list', 'update', 'autoupdate', 'link', 'unlink', 'aliases', 'config', 'where', ]); if (!known.has(command)) { const match = commands(root).find((entry) => entry.name === command); @@ -629,6 +633,10 @@ export async function run(argv: readonly string[]): Promise { return 0; } + case 'help': + process.stdout.write(USAGE); + return 0; + case 'update': return options.flags.has('--auto') ? autoUpdate(root, options.flags.has('--force')) diff --git a/plugins/tools/commands/install.md b/plugins/tools/commands/install.md index 459cf12..985ccb4 100644 --- a/plugins/tools/commands/install.md +++ b/plugins/tools/commands/install.md @@ -91,12 +91,20 @@ asked with `--fix`. Taking it over silently changes what a merge run does. | Alias | Expands to | | --- | --- | +| `/aff` | `affiliate` | | `/blog` | `blog-post` | | `/free` | `domainfree` | | `/merge` | `gh-prs-merge --apply` | | `/prs` | `gh-prs` | +| `/speak` | `tts` | +| `/web` | `ask-web` | | `/whois` | `domainjson` | +`/web`, `/speak` and `/aff` are named around a collision rather than for +elegance: `/ask` and `/say` both already resolve to something else on a normal +box, and `/tts` would shadow our own command. An alias beats `PATH`, so binding +any of those three would shadow the real program from inside the pit only. + An alias you already bound is never overwritten — the collision is reported and yours is kept. The pit re-reads the file on every lookup, so an open pit picks them up with no restart. Arguments append rather than substitute, so diff --git a/src/registry.ts b/src/registry.ts index bf0674c..62affb0 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -141,15 +141,25 @@ export function resolveCommand( * having, and `merge` is the one with a flag baked in because the long form is * what everybody types anyway. * + * Three of these are named around a collision rather than for elegance. `/ask` + * and `/say` are the words you would reach for, and both already resolve to + * something else on a normal box — and because a pit alias beats PATH, binding + * them would shadow those programs from inside the pit only, which is about the + * most confusing failure available. `/tts` is worse still: it would shadow our + * own command. Hence `/web`, `/speak` and `/aff`. + * * Keep them thin for a reason — `gh-prs-merge` repairs by default under * `--apply`, so baking `--fix` in as well is what once made `/merge --fix` * expand to `--apply --fix --fix`. */ export const PIT_ALIASES: Record = { + aff: 'affiliate', blog: 'blog-post', free: 'domainfree', merge: 'gh-prs-merge --apply', prs: 'gh-prs', + speak: 'tts', + web: 'ask-web', whois: 'domainjson', }; diff --git a/test/registry.test.ts b/test/registry.test.ts index 29dfdd1..17661ae 100644 --- a/test/registry.test.ts +++ b/test/registry.test.ts @@ -247,6 +247,19 @@ describe('mergeAliases', () => { } }); + // /ask and /say are the words you would reach for, and both already resolve + // to something else on a normal box. A pit alias beats PATH, so binding them + // would shadow those programs from inside the pit only — which is about the + // most confusing failure available. + it('avoids the names that would shadow something on PATH', () => { + for (const taken of ['ask', 'say']) { + expect(Object.keys(PIT_ALIASES)).not.toContain(taken); + } + expect(PIT_ALIASES.web).toBe('ask-web'); + expect(PIT_ALIASES.speak).toBe('tts'); + expect(PIT_ALIASES.aff).toBe('affiliate'); + }); + // Every alias has to start with a command that actually exists here, or it is // a `command not found` the moment somebody types it. it('expands to a command this repository installs', () => {