Skip to content

feat(pit): /alias — name the lines you keep retyping - #359

Merged
ralyodio merged 1 commit into
mainfrom
worktree-alias-command
Aug 10, 2026
Merged

feat(pit): /alias — name the lines you keep retyping#359
ralyodio merged 1 commit into
mainfrom
worktree-alias-command

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

What

/alias in the pit: name a line you keep retyping, run it as /<name>.

/alias set gs "git status"        # then /gs — and /gs -sb appends
/alias set cx "/agents codex"     # a pit command, not a shell one
/alias                            # what is defined  (--json for the map)
/alias get gs
/alias rm gs

A value runs in $SHELL unless it starts with /, in which case it is a pit command. Shell-by-default because that is what the prompt is mostly asked for, and the leading slash is already how the pit spells its own verbs.

How

  • src/aliases.mjs — the store: ~/.moshcode/aliases.json, owner-only for the same reason the history file is (values are whatever was typed, and people alias commands that carry tokens). A corrupt or hand-edited file reads as no aliases rather than taking the prompt down.
  • An alias expands into a line that goes back through the top of the dispatch loop, not through a second copy of the dispatcher — that is what makes /gs -sb append the way a shell alias does, and lets one alias name another. Expansion is bounded, so two aliases naming each other report a loop instead of spinning the prompt.
  • Aliases are dispatched last, so they can never shadow a built-in; /alias set refuses a name the pit already owns (pit commands, engines and their aliases/cc is claude — and tools) rather than accepting a shortcut that would be silently dead.
  • renderPitCommand learned the synopsis/examples/note shapes renderCommand already reads, so a pit-only verb with sub-verbs documents itself. /quit and friends render unchanged.

Tests

test/aliases.test.mjs — 13 tests: set/run, argument append with quoting intact, pit-command values, persistence across sessions, all three reservation classes, unusable names, expansion loops, 0600 permissions, corrupt-file tolerance, list/get/rm, and the /help alias text.

Its harness feeds one command per prompt. That is not cosmetic: readline in non-terminal mode emits every buffered line at once and only the one a pending question catches survives, so the existing stdin.end("/a\n/b\n/quit\n") style silently runs only the first command. Pre-existing behaviour, unchanged here — but it is why these tests could not be written the old way.

Full suite: 1534 tests, 0 failures.

🤖 Generated with Claude Code

The pit is a prompt people sit at all day, and the things they retype are
their own: `git status`, `pnpm -r test`, `/agents claude --resume`. Shell
aliases cannot help, because the pit is not a shell — and `!git status` is
exactly the keystrokes an alias is supposed to save.

    /alias set gs "git status"        → /gs, and /gs -sb appends
    /alias set cx "/agents codex"     → a pit command, not a shell one
    /alias                            → what is defined
    /alias rm gs

A value runs in $SHELL unless it starts with `/`, in which case it is a pit
command. Shell-by-default because that is what the prompt is mostly asked
for, and the leading slash is already how the pit spells its own verbs, so
the rule reads the same way the rest of the pit does.

An alias expands into a line that goes back through the top of the dispatch
loop rather than through a second copy of the dispatcher, which is what makes
`/gs -sb` append the way a shell alias does and lets one alias name another.
Expansion is bounded, so two aliases naming each other report a loop instead
of spinning the prompt.

Aliases are dispatched last, so they can never shadow a built-in — and
`/alias set` refuses a name the pit already owns (commands, engines and their
aliases, tools) rather than accepting a shortcut that would be silently dead.
The store is ~/.moshcode/aliases.json, owner-only for the same reason the
history file is: values are whatever was typed, and people alias commands
that carry tokens. A corrupt or hand-broken file reads as no aliases rather
than taking the prompt down.

Also teaches renderPitCommand the synopsis/examples/note shapes renderCommand
already reads, so a pit-only verb with sub-verbs can document itself; `/quit`
and friends are unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

93 finding(s)

HIGH/CRITICAL: 2 | MEDIUM: 43 | LOW: 48

Severity Rule Location
HIGH manifest-typosquat apps/pwa/package.json:19
HIGH js-ssrf-outbound-request apps/pwa/public/sw.js:45
MEDIUM tls-verification-disabled apps/pwa/src/lib/moshpit-gateway.mjs:299
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:61
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:75
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:101
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:265
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:269
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:314
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:499
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:675
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:677
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:736
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:782
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:852
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:955
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1063
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1199
MEDIUM js-unescaped-html-sink apps/pwa/src/routes/moshpit.mjs:1419
MEDIUM js-dynamic-code-execution apps/pwa/test/apikey-mask.test.mjs:129
MEDIUM sql-template-interpolation apps/pwa/test/credits-webhook-event-match.test.mjs:111
MEDIUM sql-template-interpolation apps/pwa/test/credits-webhook-event-match.test.mjs:131
MEDIUM sql-template-interpolation apps/pwa/test/moshpit-terms.test.mjs:192
MEDIUM sql-string-concatenation src/cli-schema.mjs:127
MEDIUM tls-verification-disabled src/dns.mjs:741
MEDIUM sql-template-interpolation src/dns.mjs:2549
MEDIUM sql-template-interpolation src/selfupdate.mjs:166
MEDIUM sql-template-interpolation src/selfupdate.mjs:170
MEDIUM sql-template-interpolation src/selfupdate.mjs:208
MEDIUM sql-template-interpolation src/selfupdate.mjs:209
MEDIUM insecure-temp-file test/dns-disable-restore.test.mjs:93
MEDIUM insecure-temp-file test/dns-disable-restore.test.mjs:310
MEDIUM insecure-temp-file test/plugins.test.mjs:152
MEDIUM insecure-temp-file test/pty.test.mjs:28
MEDIUM insecure-temp-file test/pty.test.mjs:31
MEDIUM insecure-temp-file test/pty.test.mjs:40
MEDIUM insecure-temp-file test/pty.test.mjs:42
MEDIUM insecure-temp-file test/pty.test.mjs:47
MEDIUM insecure-temp-file test/pty.test.mjs:48
MEDIUM insecure-temp-file test/pty.test.mjs:49
MEDIUM insecure-temp-file test/tabs.test.mjs:8
MEDIUM insecure-temp-file test/tabs.test.mjs:13
MEDIUM insecure-temp-file test/tabs.test.mjs:14
MEDIUM insecure-temp-file test/tabs.test.mjs:22
MEDIUM insecure-temp-file test/trust.test.mjs:240
LOW secret-generic-credential apps/pwa/test/apikey-bearer-scheme.test.mjs:30
LOW secret-generic-credential apps/pwa/test/apikey-mask.test.mjs:38
LOW secret-generic-credential apps/pwa/test/apikey-reveal.test.mjs:35
LOW secret-generic-credential apps/pwa/test/approvals-context.test.mjs:28
LOW secret-generic-credential apps/pwa/test/approvals-credits.test.mjs:28

…and 43 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit ddb6785 into main Aug 10, 2026
4 checks passed
@ralyodio
ralyodio deleted the worktree-alias-command branch August 10, 2026 09:44
@ralyodio ralyodio mentioned this pull request Aug 10, 2026
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