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
2 changes: 1 addition & 1 deletion src/exec/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ export async function runExec(config: Config): Promise<ExecResult> {

if (config.skipPermissionsFromSettings) {
stderr.write(
`${savedSkipPermissionsWarning(config.globalSettingsPath)}\n`,
`${savedSkipPermissionsWarning(config.globalSettingsPath, "exec")}\n`,
);
}

Expand Down
70 changes: 70 additions & 0 deletions src/permission/saved-skip-warning.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { describe, expect, test } from "bun:test";
import { mkdtemp, symlink } from "node:fs/promises";
import { homedir, tmpdir } from "node:os";
import { join } from "node:path";
import { SETTINGS_DIR_NAME } from "../branding.js";
import { savedSkipPermissionsWarning } from "./saved-skip-warning.js";

describe("savedSkipPermissionsWarning", () => {
test("TUI default machine-wide source appends the /yolo off hint", () => {
const source = join(homedir(), SETTINGS_DIR_NAME, "settings.json");

const warning = savedSkipPermissionsWarning(source, "tui");

expect(warning).toContain(source);
expect(warning).toContain("edit that file to re-enable");
expect(warning).toContain("/yolo off");
});

test("TUI symlinked-home alias of the default source still appends the hint", async () => {
const sandbox = await mkdtemp(join(tmpdir(), "corbits-skip-warning-"));
const homeLink = join(sandbox, "home");
await symlink(homedir(), homeLink);
const aliased = join(homeLink, SETTINGS_DIR_NAME, "settings.json");

const warning = savedSkipPermissionsWarning(aliased, "tui");

expect(warning).toContain(aliased);
expect(warning).toContain("edit that file to re-enable");
expect(warning).toContain("/yolo off");
});

test("exec default source stays path-only with no slash", () => {
const source = join(homedir(), SETTINGS_DIR_NAME, "settings.json");

const warning = savedSkipPermissionsWarning(source, "exec");

expect(warning).toContain(source);
expect(warning).toContain("edit that file to re-enable");
expect(warning).not.toContain("/yolo");
expect(warning).toBe(
`Warning: permission prompts are disabled by saved settings at ${source}; edit that file to re-enable.`,
);
});

test("exec symlinked-home alias of the default source stays path-only", async () => {
const sandbox = await mkdtemp(join(tmpdir(), "corbits-skip-warning-"));
const homeLink = join(sandbox, "home");
await symlink(homedir(), homeLink);
const aliased = join(homeLink, SETTINGS_DIR_NAME, "settings.json");

const warning = savedSkipPermissionsWarning(aliased, "exec");

expect(warning).toContain(aliased);
expect(warning).toContain("edit that file to re-enable");
expect(warning).not.toContain("/yolo");
});

test("custom source keeps file-path wording with no false provenance on both surfaces", () => {
for (const surface of ["tui", "exec"] as const) {
const warning = savedSkipPermissionsWarning(
"/tmp/custom-corbits-settings.json",
surface,
);

expect(warning).toContain("/tmp/custom-corbits-settings.json");
expect(warning).toContain("edit that file to re-enable");
expect(warning).not.toMatch(/machine-wide|saved default|\/yolo off/i);
}
});
});
19 changes: 18 additions & 1 deletion src/permission/saved-skip-warning.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
import {
globalSettingsPath as defaultGlobalSettingsPath,
isProgrammaticSettingsOverride,
} from "../config/settings.js";

function isDefaultSettingsSource(sourcePath: string): boolean {
return !isProgrammaticSettingsOverride(
sourcePath,
defaultGlobalSettingsPath(),
);
}

export function savedSkipPermissionsWarning(
globalSettingsPath: string,
surface: "tui" | "exec",
): string {
return `Warning: permission prompts are disabled by saved settings at ${globalSettingsPath}; edit that file to re-enable.`;
const base = `Warning: permission prompts are disabled by saved settings at ${globalSettingsPath}; edit that file to re-enable`;
if (surface === "tui" && isDefaultSettingsSource(globalSettingsPath)) {
return `${base} (/yolo off to re-enable).`;
}
return `${base}.`;
}
11 changes: 6 additions & 5 deletions src/tui/runner/wiring.skip-permissions-warning.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { describe, expect, test } from "bun:test";
import { globalSettingsPath } from "../../config/settings.js";
import { createAppShell } from "../shell/index.js";
import { shellInternals } from "../shell/internals.js";
import { withTestRenderer } from "../harness.js";
Expand Down Expand Up @@ -34,12 +35,12 @@ describe("saved skip-permissions startup warning", () => {
expect(warning).not.toMatch(/machine-wide|saved default|\/yolo off/i);
});

test("identifies the default settings path", async () => {
const warning = await surfacedWarning(
"/home/operator/.corbits/settings.json",
);
test("appends the /yolo off hint for the default settings path", async () => {
const source = globalSettingsPath();
const warning = await surfacedWarning(source);

expect(warning).toContain("/home/operator/.corbits/settings.json");
expect(warning).toContain(source);
expect(warning).toContain("edit that file to re-enable");
expect(warning).toContain("/yolo off");
});
});
2 changes: 1 addition & 1 deletion src/tui/runner/wiring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export function surfaceSavedSkipPermissionsWarning(
if (!config.skipPermissionsFromSettings) return;
surfaceSystemNotice(
shell,
savedSkipPermissionsWarning(config.globalSettingsPath),
savedSkipPermissionsWarning(config.globalSettingsPath, "tui"),
);
}

Expand Down
6 changes: 5 additions & 1 deletion tests/unit/exec/runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,11 @@ describe("runExec", () => {
async () => {
const { runExec: runExecUnderMock } =
await import("../../../src/exec/runner.js");
const defaultSettingsPath = join(home, "settings.json");
const defaultSettingsPath = join(
home,
".corbits",
"settings.json",
);
const result = await runExecUnderMock({
...bareConfig("do the thing"),
cwd,
Expand Down
Loading