Skip to content
Merged
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
8 changes: 7 additions & 1 deletion apps/cli-e2e/src/tests/gen.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ describe("gen", () => {
testBehaviour.skipIf(isRecording)(
"generates typescript types from project",
async ({ run, projectRef }) => {
const result = await run(["gen", "types", "--project-id", projectRef]);
// #5212 — with CLAUDECODE=1, piped/redirected output must not include the
// plugin hint (would break `gen types > file.ts` and similar captures).
const result = await run(["gen", "types", "--project-id", projectRef], {
env: { CLAUDECODE: "1" },
});
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("export type Json");
expect(result.stdout).toContain("export type Database");
expect(result.stdout).not.toMatch(/claude-code-hint/);
expect(result.stderr).not.toMatch(/claude-code-hint/);
},
);

Expand Down
9 changes: 6 additions & 3 deletions apps/cli-e2e/src/tests/test-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ function scenarioSlug(task: { name: string; suite?: { name: string } | null }):
return prefix + slugify(task.name);
}

type ExecOptions = NonNullable<Parameters<typeof exec>[2]>;

interface BehaviourFixtures {
projectRef: string;
orgId: string;
workspace: TempDir;
run: (cmd: string[]) => Promise<CLIResult>;
run: (cmd: string[], execOpts?: ExecOptions) => Promise<CLIResult>;
runNoProjectId: (cmd: string[]) => Promise<CLIResult>;
apiUrl: string;
storageBucket: string;
Expand All @@ -38,7 +40,8 @@ interface BehaviourFixtures {
* - `projectRef` — a real project ref (record mode) or the replay default
* - `orgId` — a real org slug (record mode) or the replay default
* - `workspace` — fresh temp dir, auto-disposed after the test
* - `run` — pre-configured `exec()` for the current TARGET
* - `run` — pre-configured `exec()` for the current TARGET (optional second
* argument forwarded as `exec` options, e.g. extra `env` entries)
* - `apiUrl` — the replay server base URL (for setting up error overrides)
*
* Auto-wires a named scenario for the test before running it, so the replay
Expand Down Expand Up @@ -90,7 +93,7 @@ export const testBehaviour = test.extend<BehaviourFixtures>({
cwd: workspace.path,
projectId: inject("projectRef") as string,
});
await use((cmd) => exec(harness, cmd));
await use((cmd, execOpts) => exec(harness, cmd, execOpts));
},

runNoProjectId: async ({ workspace }, use) => {
Expand Down
14 changes: 11 additions & 3 deletions apps/cli-go/internal/utils/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/spf13/viper"
"github.com/supabase/cli/internal/utils/agent"
"github.com/supabase/cli/pkg/migration"
"golang.org/x/term"
)

// Assigned using `-ldflags` https://stackoverflow.com/q/11354518
Expand All @@ -42,10 +43,17 @@ const SuggestDebugFlag = "Try rerunning the command with --debug to troubleshoot
const claudeCodeHint = `<claude-code-hint v="1" type="plugin" value="supabase@claude-plugins-official" />`

func SuggestClaudePlugin() string {
if agent.IsClaudeCode() {
return claudeCodeHint
if !agent.IsClaudeCode() {
return ""
}
return ""
// Suppress the hint when stdout is non-interactive (redirected to a file
// or piped). Without this guard, captured output could be contaminated by
// the trailer if anything ever leaks the tag onto stdout, and the
// install-prompt UX is only meaningful in an interactive shell anyway.
if !term.IsTerminal(int(os.Stdout.Fd())) { //nolint:gosec // G115: stdout fd is a small int on supported platforms
return ""
}
return claudeCodeHint
}

var (
Expand Down
Loading