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
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ parallel copies under `docs/` or `scripts/notes/`. At cut time: rename

## [Unreleased]

### Removed

- `--force` is no longer accepted. It had no runtime effect; resume and the
session picker already include failed and done sessions without it.

## [0.3.20] - 2026-09-10

### Added
Expand Down Expand Up @@ -376,8 +381,8 @@ parallel copies under `docs/` or `scripts/notes/`. At cut time: rename
reply. `send_input` steers only an in-flight running turn. Closed workers
stay closed.
- `corbits resume` orders sessions by last persist. The picker shows the 10
most recent sessions and type-to-filter narrows that list. Default rows are
running and cancelled; `--force` includes failed and done.
most recent sessions across all statuses and type-to-filter narrows that
list.

### TUI

Expand Down
2 changes: 1 addition & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ In TUI chat mode there is no completion gate — the session stays open across t
- `--config <path>` replaces the global settings file as the provider source (useful for CI per-run injection). A provider must be defined in a settings file; there is no env fallback.
- `settings.ts` owns the schema, validators (the per-repo file rejects credentials), file loaders, and the pure `resolveProvider` precedence function.
- `providers.ts` defines the `ProviderCatalogEntry` type and helpers for building TUI provider lists; `profiles.ts` handles profile-level selection logic.
- `loadConfig` is async (it reads settings files). Parses a leading `exec`/`run` subcommand, flags `--cwd`, `--config`, `--provider`, `--model`, `--force`, `--dangerously-skip-permissions` (forces this process; TUI `/yolo` persists as the user-global default), `--auto` / `--no-auto` (auto mode defaults on); collects positional arguments as the optional initial task for the TUI or the required prompt for exec.
- `loadConfig` is async (it reads settings files). Parses a leading `exec`/`run` subcommand, flags `--cwd`, `--config`, `--provider`, `--model`, `--dangerously-skip-permissions` (forces this process; TUI `/yolo` persists as the user-global default), `--auto` / `--no-auto` (auto mode defaults on); collects positional arguments as the optional initial task for the TUI or the required prompt for exec.
- Both settings files are on the secret-guard denylist for path-keyed tools, so the agent cannot `read_file` its own credentials. Shell commands that reference them still require explicit operator approval.

### TUI Runner (`src/tui/runner.ts`)
Expand Down
3 changes: 1 addition & 2 deletions docs/IMPLEMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ Printed by `corbits --help` / `-h` from `CLI_HELP_TEXT` in `src/config/index.ts`
| -------------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| _(no verb)_ | — | Interactive session; optional trailing task text |
| `exec` / `run` | — | Run a prompt (non-interactive / one-shot) |
| `resume` / `continue` | — | Open the session picker for this folder (project-keyed to this checkout's git toplevel). Lists the 10 most recently persisted sessions, completed included. Type to filter. `--force` is not required to see finished threads. |
| `resume` / `continue` | — | Open the session picker for this folder (project-keyed to this checkout's git toplevel). Lists the 10 most recently persisted sessions, completed included. Type to filter. |
| `--resume` | — | Open the interactive session picker |
| `resume <session-id>` | — | Reopen a specific session |
| `resume --pick` / `--list` | — | Interactive session picker |
Expand All @@ -375,7 +375,6 @@ Printed by `corbits --help` / `-h` from `CLI_HELP_TEXT` in `src/config/index.ts`
| `--provider <name>` | from settings | Select a configured provider |
| `--model <id>` | provider default | Select a model for the active provider |
| `--profile <name>` | — | Settings profile |
| `--force` | false | Override an existing run state |
| `--dangerously-skip-permissions` | false | Auto-allow anything not denied by the authorization layer (gate + pre-gate workspace sandboxes; secret-guard / authz hard denies remain). This launch flag still forces this process; `/yolo [on\|off\|toggle]` persists as the user-global default via `setSkipPermissions` |
| `--auto` | true (default) | Force auto mode on (workspace writes + unconstrained shell without prompts) |
| `--no-auto` | false | Start with auto mode off (ask on every consequential action); no in-session key toggles it |
Expand Down
3 changes: 1 addition & 2 deletions docs/TUI.md
Original file line number Diff line number Diff line change
Expand Up @@ -542,8 +542,7 @@ terminal's own text selection and copy work by default, with no Alt+M dance
required. The resume picker lists the 10 most recently persisted sessions
for this checkout — completed, failed, and crashed included. Recency is
the last write to `run.json`, not start time. Type to filter by name
(printable keys claim the `>` row, same as the model picker); `--force`
is not a list filter.
(printable keys claim the `>` row, same as the model picker).

## The prompt box

Expand Down
1 change: 0 additions & 1 deletion scripts/eval-capability.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ function sampleConfig(over: Partial<Config> = {}): Config {
providerName: "openai",
cwd: process.cwd(),
task: "do it",
force: true,
dangerouslySkipPermissions: true,
skipPermissionsFromSettings: false,
auto: false,
Expand Down
3 changes: 1 addition & 2 deletions scripts/eval-capability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ async function resolveVariantLabels(
probe.push("--provider", variant.provider);
if (variant.model !== undefined) probe.push("--model", variant.model);
if (opts.configPath !== undefined) probe.push("--config", opts.configPath);
probe.push("--force", "probe");
probe.push("probe");
const cfg = await loadConfig(probe, { allowUnconfigured: true });
if (cfg.configured) {
return { provider: cfg.providerName, model: cfg.model };
Expand Down Expand Up @@ -777,7 +777,6 @@ async function runCase(
if (requested.model !== undefined) argv.push("--model", requested.model);
if (opts.configPath !== undefined) argv.push("--config", opts.configPath);
if (opts.skipPermissions) argv.push("--dangerously-skip-permissions");
argv.push("--force");
if (opts.director !== undefined) argv.push("--director", opts.director);

argv.push(prompt);
Expand Down
1 change: 0 additions & 1 deletion scripts/eval-public-swe-one.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,6 @@ async function main(): Promise<void> {
"--model",
opts.model,
"--dangerously-skip-permissions",
"--force",
prompt,
];

Expand Down
66 changes: 34 additions & 32 deletions src/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ describe("loadConfig", () => {
expect(config.providerName).toBe("fireworks");
expect(config.globalSettingsPath).toBe(globalPath);
expect(config.globalDefaultProvider).toBe("fireworks");
expect(config.force).toBe(false);
} finally {
await rm(cwd, { recursive: true, force: true });
}
Expand Down Expand Up @@ -450,16 +449,26 @@ describe("loadConfig", () => {
}
});

test("parses --force", async () => {
test("rejects --force as unrecognized for tui, exec, and resume", async () => {
const cwd = await emptyCwd();
try {
const globalPath = await writeGlobalSettings(cwd);
const config = await loadConfig(["--cwd", cwd, "--force", "run task"], {
globalSettingsPath: globalPath,
});
expect(config.force).toBe(true);
expect(config.task).toBe("run task");
expect(config.command).toBe("tui");
await expect(
loadConfig(["--cwd", cwd, "--force", "run task"], {
globalSettingsPath: globalPath,
}),
).rejects.toThrow("unrecognized flag: --force");
await expect(
loadConfig(["exec", "--cwd", cwd, "--force", "ship it"], {
globalSettingsPath: globalPath,
}),
).rejects.toThrow("unrecognized flag: --force");
const sessionId = generateSessionId();
await expect(
loadConfig(["resume", sessionId, "--force", "--cwd", cwd], {
globalSettingsPath: globalPath,
}),
).rejects.toThrow("unrecognized flag: --force");
} finally {
await rm(cwd, { recursive: true, force: true });
}
Expand All @@ -470,15 +479,15 @@ describe("loadConfig", () => {
try {
const globalPath = await writeGlobalSettings(cwd);
const config = await loadConfig(
["exec", "--cwd", cwd, "--force", "ship it"],
["exec", "--cwd", cwd, "--auto", "ship it"],
{
globalSettingsPath: globalPath,
},
);
assertConfigured(config);
expect(config.command).toBe("exec");
expect(config.task).toBe("ship it");
expect(config.force).toBe(true);
expect(config.auto).toBe(true);
} finally {
await rm(cwd, { recursive: true, force: true });
}
Expand Down Expand Up @@ -654,7 +663,7 @@ describe("loadConfig", () => {
}
});

test("resume <id> --force reopens a failed session that recorded an error", async () => {
test("resume <id> reopens a failed session that recorded an error", async () => {
const cwd = await emptyCwd();
const home = await mkdtemp(join(tmpdir(), "ic-resume-home-"));
try {
Expand All @@ -674,26 +683,22 @@ describe("loadConfig", () => {
},
home,
);
const config = await loadConfig(
["resume", sessionId, "--force", "--cwd", cwd],
{
globalSettingsPath: globalPath,
home,
},
);
const config = await loadConfig(["resume", sessionId, "--cwd", cwd], {
globalSettingsPath: globalPath,
home,
});
assertConfigured(config);
expect(config.resumeMode).toBe("id");
expect(config.sessionId).toBe(sessionId);
expect(config.skipInitialTask).toBe(true);
expect(config.task).toBe("ship resume after failure");
expect(config.force).toBe(true);
} finally {
await rm(cwd, { recursive: true, force: true });
await rm(home, { recursive: true, force: true });
}
});

test("resume <id> --force among failed siblings stays silent and reopens the target", async () => {
test("resume <id> among failed siblings stays silent and reopens the target", async () => {
const cwd = await emptyCwd();
const home = await mkdtemp(join(tmpdir(), "ic-resume-home-"));
try {
Expand All @@ -719,13 +724,10 @@ describe("loadConfig", () => {

let config: Awaited<ReturnType<typeof loadConfig>> | undefined;
const logged = await withFileLogSink(async () => {
config = await loadConfig(
["resume", targetId, "--force", "--cwd", cwd],
{
globalSettingsPath: globalPath,
home,
},
);
config = await loadConfig(["resume", targetId, "--cwd", cwd], {
globalSettingsPath: globalPath,
home,
});
});
const loaded = defined(config, "config");
assertConfigured(loaded);
Expand Down Expand Up @@ -843,7 +845,7 @@ describe("loadConfig", () => {
let thrown: unknown;
const logged = await withFileLogSink(async () => {
try {
await loadConfig(["resume", sessionId, "--force", "--cwd", cwd], {
await loadConfig(["resume", sessionId, "--cwd", cwd], {
globalSettingsPath: globalPath,
home,
});
Expand Down Expand Up @@ -923,8 +925,8 @@ describe("loadConfig", () => {
});

test("--help after flags throws CliHelpError", async () => {
await expectCliHelp(["--force", "--help"]);
await expectCliHelp(["--force", "-h"]);
await expectCliHelp(["--auto", "--help"]);
await expectCliHelp(["--auto", "-h"]);
});

test("--help after a positional throws CliHelpError", async () => {
Expand Down Expand Up @@ -967,7 +969,7 @@ describe("loadConfig", () => {

test("value flags reject other flag-shaped tokens as values", async () => {
await expect(
loadConfig(["--provider", "--force"], {
loadConfig(["--provider", "--auto"], {
globalSettingsPath: NO_SETTINGS,
}),
).rejects.toThrow("--provider requires a value");
Expand All @@ -978,7 +980,7 @@ describe("loadConfig", () => {
loadConfig(["--cwd", "--tmp"], { globalSettingsPath: NO_SETTINGS }),
).rejects.toThrow("--cwd requires a value");
await expect(
loadConfig(["exec", "--director", "--force", "ship it"], {
loadConfig(["exec", "--director", "--auto", "ship it"], {
globalSettingsPath: NO_SETTINGS,
}),
).rejects.toThrow("--director requires a value");
Expand Down
12 changes: 1 addition & 11 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,6 @@ export interface Config {
verified?: boolean;
cwd: string;
task: string;
force: boolean;
dangerouslySkipPermissions: boolean;
// True when dangerouslySkipPermissions came from the persisted global
// default rather than this invocation's CLI flag. Entry points use this to
Expand Down Expand Up @@ -529,7 +528,6 @@ export interface UnconfiguredConfig {
configured: false;
cwd: string;
task: string;
force: boolean;
dangerouslySkipPermissions: boolean;
skipPermissionsFromSettings: boolean;
auto: boolean;
Expand Down Expand Up @@ -573,7 +571,6 @@ Flags:
--model <id> model for the active provider
--profile <name> settings profile
--resume interactive session picker
--force override an existing run state
--director <id> exec-only: run as this director (default: skywalker)
--dangerously-skip-permissions
skip permission prompts for this run only;
Expand Down Expand Up @@ -684,7 +681,6 @@ export async function loadConfig(
}

let cwd = process.cwd();
let force = false;
let dangerouslySkipPermissions = false;
// Auto mode is the default: non-destructive consequential actions (file
// writes/edits and unconstrained shell) run without prompting, while shell
Expand All @@ -702,7 +698,7 @@ export async function loadConfig(
const positional: string[] = [];

const requireValue = (flag: string, value: string | undefined): string => {
// Flag-shaped tokens are never option values. `--provider --force` and a
// Flag-shaped tokens are never option values. `--provider --auto` and a
// trailing `--provider` both surface as a missing value rather than binding
// the next flag (or accepting `--help`, which is already handled above).
if (value === undefined || isFlagToken(value)) {
Expand Down Expand Up @@ -734,10 +730,6 @@ export async function loadConfig(
profileFlag = requireValue("--profile", args[++i]);
continue;
}
if (arg === "--force") {
force = true;
continue;
}
if (arg === "--director") {
const value = requireValue("--director", args[++i]);
if (command !== "exec") {
Expand Down Expand Up @@ -914,7 +906,6 @@ export async function loadConfig(
configured: false,
cwd,
task,
force,
dangerouslySkipPermissions,
skipPermissionsFromSettings,
auto,
Expand Down Expand Up @@ -984,7 +975,6 @@ export async function loadConfig(
...resolved,
cwd,
task: resumeTask,
force,
dangerouslySkipPermissions,
skipPermissionsFromSettings,
auto,
Expand Down
1 change: 0 additions & 1 deletion src/tui/onboarding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ export async function runOnboarding(
argv.push("--config", config.cliConfigPath);
if (config.dangerouslySkipPermissions)
argv.push("--dangerously-skip-permissions");
if (config.force) argv.push("--force");
if (config.task.length > 0) argv.push(config.task);

// Preserve programmatic isolation independently of the path that won settings
Expand Down
1 change: 0 additions & 1 deletion tests/fixtures/crash-run/simulate-exec-signal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ await withMockedModuleDuring(
providerName: "test-provider",
model: "test-model",
providers: {},
force: false,
dangerouslySkipPermissions: true,
autoMode: false,
sessionId,
Expand Down
1 change: 0 additions & 1 deletion tests/unit/exec/runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ function bareConfig(task: string): Config {
providerName: "test",
model: "test",
providers: {},
force: false,
dangerouslySkipPermissions: true,
autoMode: false,
sessionId: "test-session",
Expand Down
Loading