Add a --oneshot option

This commit is contained in:
cte 2026-01-16 22:20:00 -08:00
parent 1f08401523
commit d14e7e200c
6 changed files with 63 additions and 35 deletions

View file

@ -65,6 +65,7 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption
const effectiveWorkspacePath = flagOptions.workspace ? path.resolve(flagOptions.workspace) : process.cwd()
const effectiveDangerouslySkipPermissions =
flagOptions.yes || flagOptions.dangerouslySkipPermissions || settings.dangerouslySkipPermissions || false
const effectiveExitOnComplete = flagOptions.print || flagOptions.oneshot || settings.oneshot || false
const extensionHostOptions: ExtensionHostOptions = {
mode: effectiveMode,
@ -77,7 +78,7 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption
nonInteractive: effectiveDangerouslySkipPermissions,
ephemeral: flagOptions.ephemeral,
debug: flagOptions.debug,
exitOnComplete: flagOptions.print,
exitOnComplete: effectiveExitOnComplete,
}
// Roo Code Cloud Authentication

View file

@ -29,6 +29,7 @@ program
DEFAULT_FLAGS.reasoningEffort,
)
.option("--ephemeral", "Run without persisting state (uses temporary storage)", false)
.option("--oneshot", "Exit upon task completion", false)
.action(run)
const authCommand = program.command("auth").description("Manage authentication for Roo Code Cloud")

View file

@ -204,5 +204,33 @@ describe("Settings Storage", () => {
expect(loaded.reasoningEffort).toBe("high")
expect(loaded.dangerouslySkipPermissions).toBe(true)
})
it("should support oneshot setting", async () => {
await saveSettings({ oneshot: true })
const loaded = await loadSettings()
expect(loaded.oneshot).toBe(true)
})
it("should support all settings together including oneshot", async () => {
const allSettings = {
mode: "architect",
provider: "anthropic" as const,
model: "claude-sonnet-4-20250514",
reasoningEffort: "high" as const,
dangerouslySkipPermissions: true,
oneshot: true,
}
await saveSettings(allSettings)
const loaded = await loadSettings()
expect(loaded.mode).toBe("architect")
expect(loaded.provider).toBe("anthropic")
expect(loaded.model).toBe("claude-sonnet-4-20250514")
expect(loaded.reasoningEffort).toBe("high")
expect(loaded.dangerouslySkipPermissions).toBe(true)
expect(loaded.oneshot).toBe(true)
})
})
})

View file

@ -31,6 +31,7 @@ export type FlagOptions = {
mode?: string
reasoningEffort?: ReasoningEffortFlagOptions
ephemeral: boolean
oneshot: boolean
}
export enum OnboardingProviderChoice {
@ -56,4 +57,6 @@ export interface CliSettings {
reasoningEffort?: ReasoningEffortFlagOptions
/** Auto-approve all prompts (use with caution) */
dangerouslySkipPermissions?: boolean
/** Exit upon task completion */
oneshot?: boolean
}

View file

@ -68,23 +68,24 @@ export interface TUIAppProps extends ExtensionHostOptions {
/**
* Inner App component that uses the terminal size context
*/
function AppInner({
initialPrompt,
workspacePath,
extensionPath,
user,
provider,
apiKey,
model,
mode,
nonInteractive = false,
debug,
exitOnComplete,
reasoningEffort,
ephemeral,
version,
createExtensionHost,
}: TUIAppProps) {
function AppInner({ createExtensionHost, ...extensionHostOptions }: TUIAppProps) {
const {
initialPrompt,
workspacePath,
extensionPath,
user,
provider,
apiKey,
model,
mode,
nonInteractive = false,
debug,
exitOnComplete,
reasoningEffort,
ephemeral,
version,
} = extensionHostOptions
const { exit } = useApp()
const {
@ -454,12 +455,7 @@ function AppInner({
{/* Header - fixed size */}
<Box flexShrink={0}>
<Header
cwd={workspacePath}
user={user}
provider={provider}
model={model}
mode={currentMode || mode}
reasoningEffort={reasoningEffort}
{...extensionHostOptions}
version={version}
tokenUsage={tokenUsage}
contextWindow={contextWindow}

View file

@ -4,32 +4,27 @@ import { Text, Box } from "ink"
import type { TokenUsage } from "@roo-code/types"
import { ASCII_ROO } from "@/types/constants.js"
import { User } from "@/lib/sdk/types.js"
import { ExtensionHostOptions } from "@/agent/index.js"
import { useTerminalSize } from "../hooks/TerminalSizeContext.js"
import * as theme from "../theme.js"
import MetricsDisplay from "./MetricsDisplay.js"
interface HeaderProps {
cwd: string
user: User | null
provider: string
model: string
mode: string
reasoningEffort?: string
interface HeaderProps extends ExtensionHostOptions {
version: string
tokenUsage?: TokenUsage | null
contextWindow?: number
}
function Header({
cwd,
workspacePath,
user,
provider,
model,
mode,
reasoningEffort,
nonInteractive,
version,
tokenUsage,
contextWindow,
@ -53,12 +48,16 @@ function Header({
<Box flexDirection="column" marginLeft={1} marginTop={1}>
{user && <Text color={theme.dimText}>Welcome back, {user.name}</Text>}
<Text color={theme.dimText}>
cwd: {cwd.startsWith(homeDir) ? cwd.replace(homeDir, "~") : cwd}
cwd:{" "}
{workspacePath.startsWith(homeDir) ? workspacePath.replace(homeDir, "~") : workspacePath}
</Text>
<Text color={theme.dimText}>
{provider}: {model} [{reasoningEffort}]
</Text>
<Text color={theme.dimText}>mode: {mode}</Text>
<Text color={theme.dimText}>
mode: {mode}
{nonInteractive && " (YOLO)"}
</Text>
</Box>
</Box>
</Box>