Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/completion-login-hint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bunny.net/cli": minor
---

feat(login): `bunny login` now ends with a shell-specific tip for enabling tab completion (`bunny completion >> ~/.zshrc` style for zsh/bash, a lazy-loaded completions file for fish)
12 changes: 10 additions & 2 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1264,12 +1264,20 @@ The method is case-insensitive (`get` and `GET` both work). Paths are relative t

### `bunny completion`

Generate a shell completion script. Add the output to your shell profile to enable tab completion.
Generate a shell completion script. Add the output to your shell profile to enable tab completion:

```bash
bunny completion >> ~/.zshrc
bunny completion >> ~/.zshrc # zsh; use ~/.bashrc for bash
```

Fish loads completion files from its own directory:

```bash
mkdir -p ~/.config/fish/completions && bunny completion > ~/.config/fish/completions/bunny.fish
```

After `bunny login` succeeds, it prints the exact line for your shell.

## Global Options

| Flag | Alias | Description | Default |
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/commands/auth/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
readPassword,
spinner,
} from "../../core/ui.ts";
import { hintShellCompletion } from "../completion/hint.ts";
import { offerGlobalSkillInstall } from "../skills/offer.ts";

const DASHBOARD_URL =
Expand Down Expand Up @@ -307,5 +308,6 @@ export const authLoginCommand = defineCommand<{
}

await offerGlobalSkillInstall(output, installSkill);
hintShellCompletion(output);
},
});
22 changes: 22 additions & 0 deletions packages/cli/src/commands/completion/hint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { describe, expect, test } from "bun:test";
import { completionHint } from "./hint.ts";

describe("completionHint", () => {
test("names the rc file and line for zsh, bash, and fish", () => {
expect(completionHint("/bin/zsh")).toBe(
"Tip: enable shell completions with: `bunny completion >> ~/.zshrc`.",
);
expect(completionHint("/usr/local/bin/bash")).toBe(
"Tip: enable shell completions with: `bunny completion >> ~/.bashrc`.",
);
expect(completionHint("/usr/local/bin/fish")).toBe(
"Tip: enable fish completions with: `mkdir -p ~/.config/fish/completions && bunny completion > ~/.config/fish/completions/bunny.fish`.",
);
});

test("returns undefined for other or missing shells", () => {
for (const shell of ["/usr/bin/nu", ""]) {
expect(completionHint(shell)).toBeUndefined();
}
});
});
31 changes: 31 additions & 0 deletions packages/cli/src/commands/completion/hint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { basename } from "node:path";
import { logger } from "../../core/logger.ts";

const SHELL_HINTS: Record<string, string> = {
zsh: "Tip: enable shell completions with: `bunny completion >> ~/.zshrc`.",
bash: "Tip: enable shell completions with: `bunny completion >> ~/.bashrc`.",
// Fish lazy-loads completion files from this directory. Alternative would be: appending it to ~/.config/fish/config.fish.
fish: "Tip: enable fish completions with: `mkdir -p ~/.config/fish/completions && bunny completion > ~/.config/fish/completions/bunny.fish`.",
};

/** The one-line completion tip for the given `$SHELL` value; `undefined` when the shell is unknown. */
export function completionHint(shell: string): string | undefined {
return SHELL_HINTS[basename(shell)];
}

/** Passive one-line completion hint shown after `bunny login`; suppressed under `--output json` and for unknown shells. */
export function hintShellCompletion(
output?: string,
shell: string | undefined = process.env.SHELL,
): void {
if (output === "json") return;

if (!shell) return;

// yargs doesn't support fish shell completions yet until next release.
// Once https://github.com/yargs/yargs/pull/2569 is merged, remove this check.
if (shell.includes("fish")) return;

const hint = completionHint(shell);
if (hint) logger.dim(hint);
}