Skip to content

feat(cli): add "testsprite completion" for bash/zsh/fish#184

Closed
Andy00L wants to merge 79 commits into
TestSprite:mainfrom
Andy00L:feat/completion
Closed

feat(cli): add "testsprite completion" for bash/zsh/fish#184
Andy00L wants to merge 79 commits into
TestSprite:mainfrom
Andy00L:feat/completion

Conversation

@Andy00L

@Andy00L Andy00L commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Adds testsprite completion [bash|zsh|fish]: prints a shell completion script.

Command names, per-group subcommands, and global flags are NOT hardcoded: index.ts walks the fully-assembled Commander program (buildCompletionSpec) and passes a CompletionSpec in, so the generated script can never drift from the real command tree. renderCompletion is a pure function of the spec (unit-testable without a live program). The shell auto-detects from $SHELL when omitted.

Enable it:
eval "$(testsprite completion bash)" # bash
testsprite completion zsh > ~/.zsh/_testsprite
testsprite completion fish | source # fish

  • New command: src/commands/completion.ts (pure renderers + $SHELL detect)
  • 11 unit tests, fully offline

Fixes #74

Summary by CodeRabbit

  • New Features

    • Added a completion command to generate shell completion scripts.
    • Supports bash, zsh, and fish, with automatic shell detection when available (or erroring on unsupported/unknown shells).
    • Completion output is generated from the current CLI command tree, including global flags and appropriate command/subcommand entries.
  • Tests

    • Added a Vitest suite covering shell detection, completion script rendering for bash/zsh/fish, and command behavior for explicit shells vs auto-detect and validation failures.

zeshi-du and others added 30 commits June 11, 2026 04:49
The Lint & Format job runs prettier --check over workflow YAML; the
aligned trailing comment failed it on every push.
Updated the README with a new link and added a video description.
- prettier-clean the README video block (fixes CI format:check on main)
- bump version to 0.1.1
- CHANGELOG: add [0.1.1] docs-only entry

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
NPM_TOKEN secret was never configured, so tag-triggered releases failed
with ENEEDAUTH. Auth now uses npm trusted publishing (configured on
npmjs.com for this repo + release.yaml):

- drop NODE_AUTH_TOKEN env (empty token would shadow OIDC auth)
- upgrade npm to >= 11.5.1 (trusted publishing requirement; Node 22 bundles 10.x)
- bump checkout/setup-node to v5 ahead of the June 16 Node 20 runner cutoff
ci(release): switch npm publish to OIDC trusted publishing
- Onboarding consolidated into `testsprite setup` (formerly `init`); the
  granular auth commands remain as hidden, deprecated aliases.
- CLI reports its version in the User-Agent header.
- README: the launch video no longer renders as a bare URL on npm.
create-batch --run launched its first concurrencyLimit triggers in
parallel, but the steady-state loop awaited each subsequent job to
fully finish (trigger + full --wait poll) before launching the next
one. Effective concurrency dropped to 1 after the initial wave
regardless of --max-concurrency.

Switch to the launch-then-race pattern already used by the other
three fan-outs in this file: launch up to the limit, relaunch on
each completion via startNext(), never await a whole job inline.

Add a regression test using equal-delay trigger responses so the
first wave settles in the same microtask batch, which is the exact
condition that exposed the bug.
…urrency

fix(test): prevent batch-run scheduler from serializing after first wave
…estSprite#7)

test code get --out <path> opened (truncated) the destination file
before the network request. If the GET then failed, or hit the
"no code generated yet" branch which writes nothing, the user's
pre-existing --out file was left emptied with no way to recover it.

Write to a sibling temp file instead and rename it onto the real
path only after a successful, complete write. Mirrors the atomic
rename contract bundle.ts already uses for multi-file bundles.

Add regression tests for both failure modes: a failing fetch and
the no-code-yet branch. Both reproduce the truncation on the old
code and pass on the fix.
…ite#9)

Batch-rerun chunks (>50 testIds) were dispatched concurrently via
Promise.all. The backend's producer/teardown closure dedup happens
per-request, not across requests, so two concurrent chunks sharing a
project's producer could each independently decide to trigger it,
double-running the producer or teardown.

Dispatch chunks sequentially in both the initial and deferred-retry
paths, closing the race at the source. Also dedupe the aggregated
accepted[] by testId and merge closure.byProject across chunks as a
defensive second layer, warning on stderr if a duplicate trigger is
detected.

Fixed a pre-existing test whose fixture relied on the old
double-counting behavior (same accepted entry returned from every
retry call).
…RROR (TestSprite#19)

A malformed API endpoint produced an opaque or misleading failure instead
of a clear config error:

  --endpoint-url "not a url"   -> `Error: Invalid URL` (exit 1, a raw
                                  `new URL()` throw with no guidance)
  --endpoint-url "localhost:3000" (missing scheme, parses as scheme
                                  "localhost:") and "ftp://x" (wrong scheme)
                               -> `fetch failed` / Service unavailable,
                                  emitted only after a full retry+backoff
                                  cycle — looks like a network outage, not a
                                  config typo.

Add `assertValidEndpointUrl` in client-factory.ts and run it in both the
real and dry-run paths of `makeHttpClient`, on the resolved endpoint (so it
covers --endpoint-url, TESTSPRITE_API_URL, and the credentials file). A
malformed value now throws a typed VALIDATION_ERROR (exit 5) with an
actionable message.

Crucially, and unlike the `--target-url` SSRF guard, this does NOT reject
localhost or private hosts — the API endpoint legitimately points at a
self-hosted, local-dev, or mock backend. Only syntactically invalid values
(unparseable, or a non-http(s) scheme) are rejected, so existing
self-hosted/CI configs and the test suite's localhost mock backend are
unaffected.

Adds unit coverage for assertValidEndpointUrl and the two makeHttpClient
paths, plus subprocess regressions.
runFailureGet now resolves and validates --out via resolveBundleDir and assertOutDirParentExists before calling GET /tests/{id}/failure, matching runArtifactGet and runCodeGet fast-fail behavior.

Adds regression tests asserting zero fetch calls on empty --out and missing parent dir paths.
…on (TestSprite#21)

A profile name (`--profile` / `TESTSPRITE_PROFILE`) is written verbatim as
an INI section header (`[name]`) in `~/.testsprite/credentials`, but was
never validated. A name containing the characters that break that grammar
silently corrupted the file:

  --profile "prod]"   -> serialises to `[prod]]`, which the section regex
                         cannot match, so the api_key/api_url lines that
                         follow are DROPPED on read. `setup` reports success
                         while the credential never persists.
  --profile $'a\nb'   -> the newline splits the header across two lines.
  --profile "  x  "   -> does not round-trip (the parser trims section
                         names, so it reads back as `x`).

Add `assertValidProfileName` in credentials.ts (a conservative allowlist:
letters, digits, dot, underscore, hyphen — covering `default`, `prod`,
`ci-staging`, `team.qa`) and call it from every profile-keyed entry point
(`readProfile`, `writeProfile`, `deleteProfile`). A malformed name now
throws a typed VALIDATION_ERROR (exit 5) before any file write, instead of
silently corrupting or failing to persist credentials.

Adds unit coverage for the guard and the three entry points, plus a
subprocess regression.

Co-authored-by: Zeshi Du <duke.zeshi@gmail.com>
…t json (TestSprite#22)

When a subcommand fired a parse error (unknown command, missing required
argument, invalid option), Commander's outputError callback wrote plain
text to stderr immediately and the catch block exited 5 with no further
output. A machine consumer that always parses stderr as JSON received an
unexpected plain-text string and crashed its JSON.parse.

Root cause: configureOutput was only applied to the root program, not to
subcommands. Each subcommand retained the default outputError that calls
write(str) directly. applyExitOverrideDeep now also propagates
configureOutput to every leaf so the message is buffered instead of
written.

In the CommanderError catch block, a resolved output mode is used to
either write a VALIDATION_ERROR JSON envelope or the buffered plain-text
message. An argv scan fallback handles the edge case where --output json
appears after the bad argument and was not yet parsed when the error fired.

The renderCommanderError helper is extracted to src/lib/render-error.ts
(alongside the existing rephraseUnknownOption helper) so it is unit-testable
without a subprocess. Eight unit tests cover JSON/text output, null fallback,
message trimming, and rephrased global-flag embedding. Four subprocess
regression tests in the [fix-5] block cover missing-arg, unknown subcommand,
argv-fallback, and text-mode no-regression paths.

Co-authored-by: zeshi-du <zeshi@testsprite.com>
New issue form for hackathon submissions — auto-applies the `hackathon`
label and captures the submitter's Discord identity for reward payout
coordination.
…rd (TestSprite#37)

assertNotLocal lowercased the hostname but did not strip a trailing dot, so http://localhost. (the FQDN form of localhost, RFC 6761) and http://localhost%2e bypassed the host === 'localhost' loopback check. IP literals are already dot-normalized by the WHATWG URL parser, so only named hosts were affected. Strips one trailing dot before the comparison. Adds 4 regression tests (3 blocked variants + 1 public-FQDN no-false-positive).
* fix(skill-nudge): require complete Codex managed section

* docs(skill-nudge): document helper contracts

---------

Co-authored-by: ahndohun <19940813+ahndohun@users.noreply.github.com>
TestSprite#36)

project create/update validated --name with the action handler's if (!name) check, which a whitespace-only string passes (a non-empty string is truthy). The blank name was then sent verbatim, creating a junk-named project. The sibling 	est create already rejects this via the requireString whitespace guard (dogfood P1 fix TestSprite#1); this aligns project create/update with that behavior. Adds 2 regression tests.
…Sprite#14)

Only `test` and `project` validated the global `--output` flag. The
`auth`, `usage`, `agent`, and `init` command groups resolved it with
`globals.output ?? 'text'`, so an unrecognised value (e.g. a typo like
`--output josn`) was silently coerced to text mode instead of being
rejected. A coding agent that asked for `--output json` then received a
human-readable text payload and failed to parse it as JSON, with no
signal as to why.

Extract the validation into a shared `resolveOutputMode` helper in
`lib/output.js` and route every command group's `resolveCommonOptions`
through it. Invalid values now throw a typed VALIDATION_ERROR (exit 5)
with an actionable message everywhere. This also unifies the error
wording, which previously differed between `test`
("Flag `--output` is invalid: must be one of: json, text.") and
`project` ("--output must be one of: json, text").
Davidson3556 and others added 9 commits July 5, 2026 12:38
…t pure) (TestSprite#31)

Interactive prompts (`prompt.ts` — the API-key prompt during `setup`/`auth
configure`, the target prompt during `agent install`) wrote the question and
masking to STDOUT, and the "Configuring profile …" prelude defaulted to
stdout too. On the interactive path that mixes UI text into stdout — and
under `--output json` it breaks the contract that stdout is a single JSON
document, so a consumer doing `JSON.parse(stdout)` fails.

Default both to stderr: prompts and informational preludes are interactive
UI, not result data. stdout now carries only the command's result (the §8.1
stdout-purity principle the repo already enforces elsewhere). stderr is still
the user's TTY, so prompts remain visible; the secret is still never echoed.
Callers that inject explicit streams are unaffected.

Adds regression tests: promptText writes the question to stderr by default,
and the configure prelude lands on stderr (not the result stdout).
* block artifact download redirects

* redact artifact download urls

---------

Co-authored-by: merlinsantiago982-cmd <merlinsantiago982-cmd@users.noreply.github.com>
* fix(test): validate artifact run id default path

* fix(test): reject windows dot artifact run ids

* fix(test): reject windows dot-suffix artifact run ids

* style(test): format artifact run id guard

---------

Co-authored-by: Lexiie <28455136+Lexiie@users.noreply.github.com>
…TestSprite#11)

* feat(cli): add runtime Node.js version check with clear error message

* refactor(version-guard): extract to a documented module tested against the real implementation
Windsurf (Cascade) reads workspace rules from `.windsurf/rules/*.md`. Add it
as an own-file agent target so `testsprite agent install --target windsurf`
(and `setup --agent windsurf`) installs the TestSprite skills into a Windsurf
project. Reworked onto the v0.2.0 multi-skill agent-targets API (pathFor /
SKILLS / DEFAULT_SKILLS).

Rule files use Cascade frontmatter with `trigger: model_decision` — the
equivalent of the Cursor `.mdc` `alwaysApply: false` mode (description shown
up front; full body pulled in on relevance).

Budget handling: a `.windsurf/rules/*.md` file caps at ~12 K characters and
Cascade silently truncates beyond it, which would cut the full ~22 KB verify
skill in half. The windsurf target therefore renders the COMPACT body per
skill (new `compactBody` flag + `compactBodyFor`): a skill that ships a
trimmed codex asset (`testsprite-verify` → ~5 KB) uses it, while a skill whose
codex contribution is only a one-liner (`testsprite-onboard`, ~6.5 KB full)
keeps its full body — both land well under the cap. `agent.ts` and
`renderForTarget` select the same body so installed bytes match the render.

Everything else derives from the TARGETS map automatically (agent list, the
setup --agent choices, skill-nudge install detection). Updated the hardcoded
help strings, the --help snapshot, the agent-targets/agent unit tests (incl.
Cascade-frontmatter and per-skill budget tests), the e2e matrix guards /
content-integrity (gated on compactBody), and the README/DOCUMENTATION target
lists (incl. the --force own-file list).
… CI proxies (TestSprite#169)

* feat(cli): honor HTTPS_PROXY/HTTP_PROXY/NO_PROXY behind corporate and CI proxies

* fix(proxy): degrade to default dispatcher when proxy agent init fails instead of crashing startup

* fix(proxy): pin undici to ^7.16.0 for Node 20 compatibility (8.x requires Node >=22.19)
…e#132)

* feat(cli): add 'test flaky' repeat-run flaky-test detector

* fix(flaky): cap --runs at 10 per maintainer scope (TestSprite#115)

Rescope the flaky detector's --runs bound from 1-100 to 1-10 as requested in the TestSprite#115 triage: uncapped FE replays amplify free executions. Updates the MAX_FLAKY_RUNS constant (which drives the validation, error message, and --runs help text), docs, changelog, and the runs-bound tests. Regenerates the help snapshot, which also adds the previously-missing 'test flaky' entry.

* test(snapshot): refresh flaky help snapshot after rebase onto main

Rebasing onto current main (which added the global --request-timeout option, TestSprite#17) changes the 'Global options' line rendered in the test flaky --help output. Regenerate the snapshot so the help snapshot test stays green on CI.

* docs(changelog): resolve leftover merge-conflict markers (keep JUnit + flaky 1-10)
)

* feat(test): add "test lint" offline plan/steps validator

* fix(lint): report physical JSONL line numbers (blank lines no longer shift them)
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 569004bc-9c0a-4b7a-b0cc-afbab82ba0da

📥 Commits

Reviewing files that changed from the base of the PR and between fe68ec0 and df8b676.

⛔ Files ignored due to path filters (1)
  • test/__snapshots__/help.snapshot.test.ts.snap is excluded by !**/*.snap, !**/*.snap
📒 Files selected for processing (3)
  • src/commands/completion.test.ts
  • src/commands/completion.ts
  • src/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/commands/completion.test.ts
  • src/commands/completion.ts
  • src/index.ts

Walkthrough

Adds shell completion support for bash, zsh, and fish. The new command detects or accepts a shell, builds completion output from the live Commander tree, and prints a generated script. Tests cover helper behavior, rendering, and command execution.

Changes

Shell completion feature

Layer / File(s) Summary
Completion types, shell detection, and renderers
src/commands/completion.ts
Defines shell and completion-spec types, shell detection helpers, and shell-specific completion rendering for bash, zsh, and fish.
Completion command wiring
src/commands/completion.ts, src/index.ts
Adds the completion subcommand, shell validation and auto-detection, stdout writing, and runtime spec construction from the Commander program tree.
Completion unit tests
src/commands/completion.test.ts
Adds tests for shell detection helpers, shell-specific rendering output, and command execution through parseAsync.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CompletionCommand
  participant Index
  participant Renderer
  User->>CompletionCommand: testsprite completion [shell]
  CompletionCommand->>CompletionCommand: isShell(shell) / detectShell($SHELL)
  CompletionCommand->>Index: buildCompletionSpec()
  Index-->>CompletionCommand: CompletionSpec
  CompletionCommand->>Renderer: renderCompletion(shell, spec)
  Renderer-->>CompletionCommand: completion script text
  CompletionCommand-->>User: stdout(script)
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The PR appears to meet #74, but the required help snapshot update lives in excluded test/snapshots/help.snapshot.test.ts.snap, so full compliance can't be verified. Please include the regenerated help snapshot diff or confirm test/snapshots/help.snapshot.test.ts.snap was updated after rebasing; it was filtered out by the !**/*.snap rule.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding shell completion for bash, zsh, and fish.
Out of Scope Changes check ✅ Passed The changes stay focused on shell completion wiring, rendering, tests, and command registration, with no obvious unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
src/commands/completion.test.ts (1)

57-91: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider covering stdout/stderr separation.

Per path instructions, machine output must go to stdout while human chatter/hints go to stderr. The tests only assert on injected stdout capture; there's no coverage ensuring validation error messages or hints for an unsupported/undetected shell are routed to stderr rather than stdout.

As per path instructions, "human chatter, hints, and advisories go to stderr" for src/** files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/completion.test.ts` around lines 57 - 91, The completion command
tests only verify stdout output, but they should also cover that validation
errors and hints are emitted to stderr for unsupported or undetected shells.
Update createCompletionCommand test coverage in createCompletionCommand and its
parseAsync cases by injecting a stderr sink alongside stdout, then assert that
normal completion script output stays on stdout while error/advisory messaging
is routed to stderr for the failing scenarios.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/commands/completion.test.ts`:
- Around line 57-91: The completion command tests only verify stdout output, but
they should also cover that validation errors and hints are emitted to stderr
for unsupported or undetected shells. Update createCompletionCommand test
coverage in createCompletionCommand and its parseAsync cases by injecting a
stderr sink alongside stdout, then assert that normal completion script output
stays on stdout while error/advisory messaging is routed to stderr for the
failing scenarios.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3017b8d1-d345-4207-906d-86b4c843bd19

📥 Commits

Reviewing files that changed from the base of the PR and between b9e9601 and fe68ec0.

⛔ Files ignored due to path filters (1)
  • test/__snapshots__/help.snapshot.test.ts.snap is excluded by !**/*.snap, !**/*.snap
📒 Files selected for processing (3)
  • src/commands/completion.test.ts
  • src/commands/completion.ts
  • src/index.ts

SahilRakhaiya05 and others added 9 commits July 6, 2026 13:09
* fix(config): treat empty env vars as unset in loadConfig

* test: address review — dry-run whoami uses offline client; fix whitespace env key expectation
* fix(http): clear per-attempt timeout timers

* fix(http): preserve timeout race classification
Bumps [marocchino/sticky-pull-request-comment](https://github.com/marocchino/sticky-pull-request-comment) from 2.9.4 to 3.0.5.
- [Release notes](https://github.com/marocchino/sticky-pull-request-comment/releases)
- [Commits](marocchino/sticky-pull-request-comment@7737449...5770ad5)

---
updated-dependencies:
- dependency-name: marocchino/sticky-pull-request-comment
  dependency-version: 3.0.5
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…an-out (TestSprite#154)

* fix(batch): classify RequestTimeoutError as timeout in --all --wait fan-out

When test run --all --wait or test rerun --all --wait hit a per-request timeout
during batch fan-out polling, RequestTimeoutError rejected the fan-out before
out.print(). Classify it as status:'timeout' in pollFreshAccepted and
pollAccepted so stdout always lists every dispatched runId.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Remove unused readFileSync import from test file

---------

Co-authored-by: Contributor <contributor@testsprite.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
… invocation (TestSprite#178)

* feat(test): make "test wait" variadic — attach to several runs in one invocation

* fix(wait): honor the shared deadline for queued members and hint only resumable runs
* feat(agent): add GitHub Copilot as an install target

Adds `copilot` to the agent-install targets. GitHub Copilot reads
path-specific custom instructions from `.github/instructions/*.instructions.md`
(VS Code / Visual Studio / Copilot Chat), with YAML frontmatter carrying an
`applyTo` glob. The skill installs to
`.github/instructions/testsprite-verify.instructions.md` (and the onboard
skill alongside) with `applyTo: '**'` so the guidance attaches to every
request in the repo.

Because `applyTo: '**'` is always-on (Copilot has no on-demand 'model
decides' mode like Cursor/Windsurf), the target renders the COMPACT body —
the same reasoning behind windsurf's compact render — keeping the
always-injected context small (~6 KB vs the ~23 KB full body).

Slots into the existing TARGETS machinery, so `agent list`, `setup --agent`,
and skill-nudge install-detection pick it up automatically. Updates the
AgentTarget union, pathFor, TARGETS, help/docs, unit + e2e matrix guards,
and the help snapshot.

Fixes TestSprite#193

* fix(agent): include Kiro in the agent command description

The top-level `agent` command description listed the other targets but
omitted Kiro (a pre-existing gap), leaving it inconsistent with the
`--target` help text and the docs. Align the description with the
`--target` list so every supported target is named.
…#183)

* feat(cli): add "testsprite doctor" environment diagnostic

One-shot preflight: checks CLI version, Node runtime, profile, API endpoint,
credentials, live connectivity (GET /me), and verify-skill install. Prints an
OK/WARN/FAIL report and exits non-zero when any check fails, so it gates a CI
step or agent preflight. Reuses the real resolution helpers; the API key is
never printed.

Fixes TestSprite#73

* fix(doctor): address review findings

- Node check reuses the CLI runtime guard (shouldRejectNodeVersion) instead of a
  hardcoded major floor, so the verdict matches what the entrypoint enforces at
  startup; the precise 20.19+/22.13+/24+ engines are enforced by npm engine-strict.
- --request-timeout raises a validation error on malformed input instead of
  silently defaulting, matching the other commands.
- The --output json test asserts the API key never appears in the JSON path
  (distinct from the text renderer already covered).
…te#185)

* feat(cli): graceful termination signals + broken-pipe guard

Install handlers for SIGINT/SIGTERM/SIGHUP that print a one-line explanation
(any started run keeps executing server-side; resume with `testsprite test list`
or `testsprite test wait <runId>`) and exit with the conventional 128+signum
code (SIGINT -> 130). Also guard EPIPE on stdout/stderr so piping to a reader
that closes early (`... | head`) exits cleanly instead of dumping a raw
`write EPIPE` stack. process and streams are injectable, so both are unit-tested
without spawning a subprocess or sending a real signal.

Fixes TestSprite#75

* fix(interrupt): flush the signal message synchronously before exit

A signal handler calls process.exit() immediately after writing the interrupt
hint. When stderr is a pipe, an async process.stderr.write() may not flush
before the process terminates, so the hint could be lost. The default stderr
writer now uses fs.writeSync (best-effort, guarded against EPIPE) so the hint is
reliably emitted. Added a test mocking fs.writeSync to assert the synchronous
write on the default path.
@zeshi-du

zeshi-du commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Approved on content — doctor (#183) landed first and both PRs add a line to the same top-level Commands block in the help snapshot, hence the conflict. Rebase on latest main, regenerate the snapshot (npx vitest run test/help.snapshot.test.ts -u), and it merges on green.

Emit a shell completion script for bash, zsh, or fish. Command names,
subcommands, and global flags are derived from the fully-assembled Commander
tree at call time (buildCompletionSpec walks program.commands), so the script
can never drift from the real command surface. The shell auto-detects from
$SHELL when the argument is omitted.

Fixes TestSprite#74
@Andy00L Andy00L force-pushed the feat/completion branch from fe68ec0 to df8b676 Compare July 6, 2026 23:27
@Andy00L

Andy00L commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main. Ready to merge.

@jangjos-128

Copy link
Copy Markdown
Contributor

Hi @Andy00L — so sorry for the disruption here. 🙏 While we were publishing a new CLI release, a hiccup in our process unintentionally closed this PR. To be clear: your contribution wasn't rejected, and nothing on your end caused this.

Your work is completely safe — your branch and every commit are intact and untouched, and main is already back to normal on our side.

Because of how the closure happened, the cleanest path is a fresh PR from your existing branch (rather than reopening this one) — and opening it yourself keeps it under your name, with full credit for your work:

👉 Open a new PR from your branch

It should merge cleanly. So your work doesn't get lost, if we don't hear back in the next ~3 days we'll go ahead and open it for you — but opening it yourself keeps it under your name, and either way we're always happy to hand it back to you.

Thank you for contributing to TestSprite, and again, we're sorry for the hassle. We really appreciate this work and want to see it merged. 🙌

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.

[Hackathon] Add shell completion (bash/zsh/fish)