Skip to content

feat(cli): add pty-relay completions <shell> (bash/fish/zsh) - #35

Merged
schickling merged 5 commits into
mainfrom
schickling-assistant/2026-07-20-completions
Jul 20, 2026
Merged

feat(cli): add pty-relay completions <shell> (bash/fish/zsh)#35
schickling merged 5 commits into
mainfrom
schickling-assistant/2026-07-20-completions

Conversation

@schickling-assistant

@schickling-assistant schickling-assistant commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Why

pty-relay ships no shell completions at all, while both sibling CLIs do:
pty gained pty completions <shell> in compoundingtech/pty#106, and
st has st completions. Downstream packaging (Nix, Home Manager) has
nothing to call.

What

Adds pty-relay completions <shell> for bash, fish and zsh.

All three scripts are emitted from ONE declarative spec of the command
tree in src/completions.ts, so they cannot drift apart. The spec is
derived from the actual switch dispatch in src/cli.tsmain,
dispatchServer, dispatchClient, dispatchLocal and the
CLIENT_PASSTHROUGH_COMMANDS set — not from the prose --help text.

Coverage: the 26 top-level commands (including the list/ls alias),
the server (11 verbs), client (signin/join + 9 passthroughs), local
(3 verbs) and clients (4 verbs) groups, their flags, the enum-valued
flags (init --backend, server rotate --role) and enum positionals
(server totp show|code, completions bash|fish|zsh).

How

  • src/completions.ts is the generator. It follows pty's module shape
    (side-effect free, console/exit-code based, same FlagSpec/CommandSpec
    vocabulary) and borrows st's nested verbs because pty-relay — unlike
    pty — has namespaced groups. Verb names such as start/status/reset
    collide across groups, so the fish guards are scoped on both the verb and
    its group.
  • src/cli.ts gains a case "completions". It sets process.exitCode
    rather than calling process.exit(), so a redirected script is fully
    flushed. completions is added to the list of commands excluded from the
    generic per-command --help short-circuit, so completions --help
    documents its own shell list.
  • The module stays inside the --experimental-strip-types subset (type
    annotations and as const only), since pty-relay runs raw .ts.

Deliberately not dynamic

pty completes live session names off disk. pty-relay's peers live in the
encrypted secret store — the plaintext ~/.config/pty-relay/peers file is
optional and usually absent — so there is no cheap, reliable source for
host-label completion at completion time. Subcommands and flags only.

Verification

  • test/completions.test.ts (new, 41 cases): dispatch and exit codes via
    spawnSync (importing cli.ts runs main()), the generated fish
    surface, a spec-covers-real-dispatch assertion listing every user-typeable
    case label, and bash -n / fish -n / zsh -n syntax checks that skip
    when the shell is absent. All pass.
  • test/cli.test.ts still passes except two doctor cases that fail on
    main too, in any checkout without a linked @compoundingtech/pty.

schickling-assistant and others added 2 commits July 20, 2026 17:10
pty-relay shipped no completions at all. Add a generator that emits all
three shells from ONE declarative spec of the command tree, so they can't
drift apart — the same design as `pty completions` and `st completions`,
extended with nested verbs for pty-relay's `server`/`client`/`local`/
`clients` groups.

The spec is derived from the `switch` dispatch in cli.ts (including
CLIENT_PASSTHROUGH_COMMANDS), not from the prose `--help` text.

Peer/host names are deliberately not completed dynamically: peers live in
the encrypted secret store, so there is no cheap reliable source at
completion time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T1xLDkqUDCYUMMADdMqVgP
agent-session-id: 312caff5-3274-4d97-baa4-8ff06ab03fc5
agent-tool: Claude Code
agent-tool-version: 2.1.215
agent-model: claude-opus-4-8
agent-runtime-profile: /nix/store/acr8a3l2v366jgmwiq8xdrhgz1py0db5-coding-agent-runtime-profile/share/coding-agents/profile.json
agent-skills-manifest: /nix/store/sj1v5j91h8v8d1w9lca4040302lwrd6v-agent-skills-corpus/share/agent-skills/manifest.json
tooling-profile: dotfiles@unknown-dirty
Packaging lived out-of-tree (in a dotfiles wrapper) and therefore had to
fetchFromGitHub a pinned rev. In-repo it builds from `self`, so the flake
tracks the checkout it ships with.

Inputs are nixpkgs, flake-utils and the sibling `pty` flake only — no
private tooling — so `nix build` works standalone.

Notes on what changed versus the out-of-tree wrapper:
- The npm name is `@compoundingtech/pty`, and it is now a `file:../pty`
  link dependency rather than a peerDependency. `npm ci` resolves it to a
  dangling symlink in the sandbox, so no `--legacy-peer-deps` is needed;
  installPhase replaces the link with the store path from the `pty` flake.
  The mapping is a declarative npm-name -> store-path attrset.
- One `nodejs` binding feeds both the derivation and the bin shim, so a
  build and a run cannot disagree on the interpreter.
- Completions are generated at build time from the just-built binary and
  installed via installShellCompletion, so they cannot lag the CLI.

Verified: `nix build .#default` and `nix flake check` pass, and
`pty-relay --help`, `pty-relay completions fish` and `pty-relay doctor`
(which resolves the sibling pty link) all work from the built output.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T1xLDkqUDCYUMMADdMqVgP
agent-session-id: 312caff5-3274-4d97-baa4-8ff06ab03fc5
agent-tool: Claude Code
agent-tool-version: 2.1.215
agent-model: claude-opus-4-8
agent-runtime-profile: /nix/store/acr8a3l2v366jgmwiq8xdrhgz1py0db5-coding-agent-runtime-profile/share/coding-agents/profile.json
agent-skills-manifest: /nix/store/sj1v5j91h8v8d1w9lca4040302lwrd6v-agent-skills-corpus/share/agent-skills/manifest.json
tooling-profile: dotfiles@unknown-dirty
schickling-assistant and others added 3 commits July 20, 2026 17:15
`nix build` drops a `result` symlink holding a full copy of the source
tree, so `vitest run` collected and ran every test twice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T1xLDkqUDCYUMMADdMqVgP
agent-session-id: 312caff5-3274-4d97-baa4-8ff06ab03fc5
agent-tool: Claude Code
agent-tool-version: 2.1.215
agent-model: claude-opus-4-8
agent-runtime-profile: /nix/store/acr8a3l2v366jgmwiq8xdrhgz1py0db5-coding-agent-runtime-profile/share/coding-agents/profile.json
agent-skills-manifest: /nix/store/sj1v5j91h8v8d1w9lca4040302lwrd6v-agent-skills-corpus/share/agent-skills/manifest.json
tooling-profile: dotfiles@unknown-dirty
pty-relay had no CI. Rather than a bespoke Node workflow — awkward while
`@compoundingtech/pty` is a `file:../pty` link dependency with no sibling
checkout on the runner — the flake is the gate: its inputs resolve the
sibling from the `pty` flake.

Modeled on compoundingtech/pty's .github/workflows/nix.yml, but running
`nix flake check` rather than `nix build` so checks.* actually gate.

Also adds checks.typecheck, running the repo's own `tsc --noEmit` against
the built tree (where the sibling resolves; it cannot pass against a bare
checkout).

The vitest suite is deliberately not gated: under the nix sandbox
test/daemon-runtime.test.ts hangs indefinitely at 0/14 and blocks two more
files, while the other 69/72 pass. The same suite is fully green against
the built tree outside the sandbox.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T1xLDkqUDCYUMMADdMqVgP
agent-session-id: 312caff5-3274-4d97-baa4-8ff06ab03fc5
agent-tool: Claude Code
agent-tool-version: 2.1.215
agent-model: claude-opus-4-8
agent-runtime-profile: /nix/store/acr8a3l2v366jgmwiq8xdrhgz1py0db5-coding-agent-runtime-profile/share/coding-agents/profile.json
agent-skills-manifest: /nix/store/sj1v5j91h8v8d1w9lca4040302lwrd6v-agent-skills-corpus/share/agent-skills/manifest.json
tooling-profile: dotfiles@unknown-dirty
…-07-20-nix-flake

feat(nix): add a self-contained flake building pty-relay from source
@schickling
schickling marked this pull request as ready for review July 20, 2026 20:20
@schickling
schickling merged commit 1ea6ea6 into main Jul 20, 2026
1 check passed
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.

2 participants